From 6927458c7baa5baaf07c3b68ed30f6e517e87c9a Mon Sep 17 00:00:00 2001 From: Krista Pratico Date: Fri, 12 Jun 2020 16:43:40 -0700 Subject: [PATCH 01/21] [formrecognizer] set up live canary/prod pipelines (#11995) * set up live canary/prod pipelines for form recognizer * delete test_prod * remove region * hardcode buildtargetingstring --- .../azure-ai-formrecognizer/tests/testcase.py | 10 ++++++---- sdk/formrecognizer/tests.yml | 2 +- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/sdk/formrecognizer/azure-ai-formrecognizer/tests/testcase.py b/sdk/formrecognizer/azure-ai-formrecognizer/tests/testcase.py index 8f03f255fdb1..c67bb0592dcf 100644 --- a/sdk/formrecognizer/azure-ai-formrecognizer/tests/testcase.py +++ b/sdk/formrecognizer/azure-ai-formrecognizer/tests/testcase.py @@ -25,6 +25,8 @@ ) from azure_devtools.scenario_tests.utilities import is_text_payload +REGION = os.getenv('REGION', 'centraluseuap') + class AccessTokenReplacer(RecordingProcessor): """Replace the access token in a request/response body.""" @@ -459,13 +461,13 @@ def create_resource(self, name, **kwargs): resource_id = "/subscriptions/" + subscription_id + "/resourceGroups/" + resource_group.name + \ "/providers/Microsoft.CognitiveServices/accounts/" + form_recognizer_name - resource_location = "centraluseuap" + resource_location = REGION self.test_class_instance.scrubber.register_name_pair( resource_id, "resource_id" ) else: - resource_location = "centraluseuap" + resource_location = REGION resource_id = "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/rgname/providers/Microsoft.CognitiveServices/accounts/frname" return { @@ -529,12 +531,12 @@ def create_form_client_and_container_sas_url(self, **kwargs): @pytest.fixture(scope="session") def form_recognizer_account(): test_case = AzureTestCase("__init__") - rg_preparer = ResourceGroupPreparer(random_name_enabled=True, name_prefix='pycog', location="centraluseuap") + rg_preparer = ResourceGroupPreparer(random_name_enabled=True, name_prefix='pycog', location=REGION) form_recognizer_preparer = CognitiveServicesAccountPreparer( random_name_enabled=True, kind="formrecognizer", name_prefix='pycog', - location="centraluseuap" + location=REGION ) try: diff --git a/sdk/formrecognizer/tests.yml b/sdk/formrecognizer/tests.yml index 28c346e0521a..469c88f32d93 100644 --- a/sdk/formrecognizer/tests.yml +++ b/sdk/formrecognizer/tests.yml @@ -10,7 +10,7 @@ resources: jobs: - template: ../../eng/pipelines/templates/jobs/archetype-sdk-tests.yml parameters: - BuildTargetingString: $(BuildTargetingString) + BuildTargetingString: azure-ai-formrecognizer ServiceDirectory: formrecognizer EnvVars: AZURE_SUBSCRIPTION_ID: $(provisioner-subscription) From 935b7ad78efe6354fa81247ef0a5e9a44dca6d86 Mon Sep 17 00:00:00 2001 From: changlong-liu <59815250+changlong-liu@users.noreply.github.com> Date: Mon, 15 Jun 2020 17:51:58 +0800 Subject: [PATCH 02/21] add bug_bash template (#12045) --- .github/ISSUE_TEMPLATE/bug_bash.md | 39 ++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 .github/ISSUE_TEMPLATE/bug_bash.md diff --git a/.github/ISSUE_TEMPLATE/bug_bash.md b/.github/ISSUE_TEMPLATE/bug_bash.md new file mode 100644 index 000000000000..ff133adeeb9d --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_bash.md @@ -0,0 +1,39 @@ +--- +name: Bug Bash report +about: Create a report about bug bash +title: "[BUG Bash]" +labels: bugbash, Mgmt +assignees: '' + +--- + +**Describe the bug** +A clear and concise description of what the bug is. + +***Exception or Stack Trace*** +Add the exception log and stack trace if available + +**To Reproduce** +Steps to reproduce the behavior: + +***Code Snippet*** +Add the code snippet that causes the issue. + +**Expected behavior** +A clear and concise description of what you expected to happen. + +**Screenshots** +If applicable, add screenshots to help explain your problem. + +**Setup (please complete the following information):** + - Python Version: [e.g. Python 3.8] + - SDK Version: [e.g. azure-mgmt-resource-15.0.0b1] + +**Additional context** +Add any other context about the problem here. + +**Information Checklist** +Kindly make sure that you have added all the following information above and checkoff the required fields otherwise we will treat the issuer as an incomplete report +- [ ] Bug Description Added +- [ ] Repro Steps Added +- [ ] Setup information Added \ No newline at end of file From 743dea5b45652745eeda4aefc943113f6b2469a2 Mon Sep 17 00:00:00 2001 From: Charles Lowell Date: Mon, 15 Jun 2020 10:36:16 -0700 Subject: [PATCH 03/21] Respect nbf and exp in local encrypt/wrap operations (#11953) --- sdk/keyvault/azure-keyvault-keys/CHANGELOG.md | 3 +- .../azure/keyvault/keys/crypto/_client.py | 48 ++- .../keyvault/keys/crypto/_internal/rsa_key.py | 30 +- .../azure/keyvault/keys/crypto/aio/_client.py | 13 +- ...est_local_validity_period_enforcement.yaml | 370 ++++++++++++++++++ ...est_local_validity_period_enforcement.yaml | 250 ++++++++++++ .../tests/test_crypto_client.py | 61 ++- .../tests/test_crypto_client_async.py | 68 +++- 8 files changed, 798 insertions(+), 45 deletions(-) create mode 100644 sdk/keyvault/azure-keyvault-keys/tests/recordings/test_crypto_client.test_local_validity_period_enforcement.yaml create mode 100644 sdk/keyvault/azure-keyvault-keys/tests/recordings/test_crypto_client_async.test_local_validity_period_enforcement.yaml diff --git a/sdk/keyvault/azure-keyvault-keys/CHANGELOG.md b/sdk/keyvault/azure-keyvault-keys/CHANGELOG.md index 6900c1d1ada0..5812490d2289 100644 --- a/sdk/keyvault/azure-keyvault-keys/CHANGELOG.md +++ b/sdk/keyvault/azure-keyvault-keys/CHANGELOG.md @@ -1,7 +1,8 @@ # Release History ## 4.2.0b2 (Unreleased) - +- `CryptographyClient` will no longer perform encrypt or wrap operations when + its key has expired or is not yet valid. ## 4.2.0b1 (2020-03-10) - Support for Key Vault API version 7.1-preview diff --git a/sdk/keyvault/azure-keyvault-keys/azure/keyvault/keys/crypto/_client.py b/sdk/keyvault/azure-keyvault-keys/azure/keyvault/keys/crypto/_client.py index f41cb3cc8dc5..17977e4f8419 100644 --- a/sdk/keyvault/azure-keyvault-keys/azure/keyvault/keys/crypto/_client.py +++ b/sdk/keyvault/azure-keyvault-keys/azure/keyvault/keys/crypto/_client.py @@ -2,6 +2,8 @@ # Copyright (c) Microsoft Corporation. # Licensed under the MIT License. # ------------------------------------ +from datetime import datetime, timedelta, tzinfo + import six from azure.core.exceptions import AzureError, HttpResponseError from azure.core.tracing.decorator import distributed_trace @@ -24,6 +26,43 @@ from ._internal import Key as _Key +class _UTC_TZ(tzinfo): + """from https://docs.python.org/2/library/datetime.html#tzinfo-objects""" + + ZERO = timedelta(0) + + def utcoffset(self, dt): + return self.ZERO + + def tzname(self, dt): + return "UTC" + + def dst(self, dt): + return self.ZERO + + +_UTC = _UTC_TZ() + + +def _enforce_nbf_exp(key): + # type: (KeyVaultKey) -> None + try: + nbf = key.properties.not_before + exp = key.properties.expires_on + except AttributeError: + # we consider the key valid because a user must have deliberately created it + # (if it came from Key Vault, it would have those attributes) + return + + now = datetime.now(_UTC) + if (nbf and exp) and not nbf <= now <= exp: + raise ValueError("This client's key is useable only between {} and {} (UTC)".format(nbf, exp)) + if nbf and nbf >= now: + raise ValueError("This client's key is not useable until {} (UTC)".format(nbf)) + if exp and exp <= now: + raise ValueError("This client's key expired at {} (UTC)".format(exp)) + + class CryptographyClient(KeyVaultClientBase): """Performs cryptographic operations using Azure Key Vault keys. @@ -80,9 +119,7 @@ def __init__(self, key, credential, **kwargs): self._internal_key = None # type: Optional[_Key] - super(CryptographyClient, self).__init__( - vault_url=self._key_id.vault_url, credential=credential, **kwargs - ) + super(CryptographyClient, self).__init__(vault_url=self._key_id.vault_url, credential=credential, **kwargs) @property def key_id(self): @@ -116,7 +153,7 @@ def _get_key(self, **kwargs): return self._key def _get_local_key(self, **kwargs): - # type: () -> Optional[_Key] + # type: (**Any) -> Optional[_Key] """Gets an object implementing local operations. Will be ``None``, if the client was instantiated with a key id and lacks keys/get permission.""" @@ -140,7 +177,6 @@ def _get_local_key(self, **kwargs): @distributed_trace def encrypt(self, algorithm, plaintext, **kwargs): # type: (EncryptionAlgorithm, bytes, **Any) -> EncryptResult - # pylint:disable=line-too-long """Encrypt bytes using the client's key. Requires the keys/encrypt permission. This method encrypts only a single block of data, whose size depends on the key and encryption algorithm. @@ -166,6 +202,7 @@ def encrypt(self, algorithm, plaintext, **kwargs): local_key = self._get_local_key(**kwargs) if local_key: + _enforce_nbf_exp(self._key) if "encrypt" not in self._allowed_ops: raise AzureError("This client doesn't have 'keys/encrypt' permission") result = local_key.encrypt(plaintext, algorithm=algorithm.value) @@ -238,6 +275,7 @@ def wrap_key(self, algorithm, key, **kwargs): local_key = self._get_local_key(**kwargs) if local_key: + _enforce_nbf_exp(self._key) if "wrapKey" not in self._allowed_ops: raise AzureError("This client doesn't have 'keys/wrapKey' permission") result = local_key.wrap_key(key, algorithm=algorithm.value) diff --git a/sdk/keyvault/azure-keyvault-keys/azure/keyvault/keys/crypto/_internal/rsa_key.py b/sdk/keyvault/azure-keyvault-keys/azure/keyvault/keys/crypto/_internal/rsa_key.py index 41c94fb9f65c..117f0b900687 100644 --- a/sdk/keyvault/azure-keyvault-keys/azure/keyvault/keys/crypto/_internal/rsa_key.py +++ b/sdk/keyvault/azure-keyvault-keys/azure/keyvault/keys/crypto/_internal/rsa_key.py @@ -2,7 +2,6 @@ # Copyright (c) Microsoft Corporation. # Licensed under the MIT License. # ------------------------------------ -import codecs import uuid from cryptography.exceptions import InvalidSignature @@ -170,7 +169,7 @@ def default_signature_algorithm(self): def encrypt(self, plain_text, **kwargs): algorithm = self._get_algorithm("encrypt", **kwargs) - encryptor = algorithm.create_encryptor(self._rsa_impl) + encryptor = algorithm.create_encryptor(self.public_key) return encryptor.transform(plain_text) def decrypt(self, cipher_text, **kwargs): @@ -178,7 +177,7 @@ def decrypt(self, cipher_text, **kwargs): raise NotImplementedError("The current RsaKey does not support decrypt") algorithm = self._get_algorithm("decrypt", **kwargs) - decryptor = algorithm.create_decryptor(self._rsa_impl) + decryptor = algorithm.create_decryptor(self.private_key) return decryptor.transform(cipher_text) def sign(self, digest, **kwargs): @@ -186,12 +185,12 @@ def sign(self, digest, **kwargs): raise NotImplementedError("The current RsaKey does not support sign") algorithm = self._get_algorithm("sign", **kwargs) - signer = algorithm.create_signature_transform(self._rsa_impl) + signer = algorithm.create_signature_transform(self.private_key) return signer.sign(digest) def verify(self, digest, signature, **kwargs): algorithm = self._get_algorithm("verify", **kwargs) - signer = algorithm.create_signature_transform(self._rsa_impl) + signer = algorithm.create_signature_transform(self.public_key) try: # cryptography's verify methods return None, and raise when verification fails signer.verify(digest, signature) @@ -201,7 +200,7 @@ def verify(self, digest, signature, **kwargs): def wrap_key(self, key, **kwargs): algorithm = self._get_algorithm("wrapKey", **kwargs) - encryptor = algorithm.create_encryptor(self._rsa_impl) + encryptor = algorithm.create_encryptor(self.public_key) return encryptor.transform(key) def unwrap_key(self, encrypted_key, **kwargs): @@ -209,7 +208,7 @@ def unwrap_key(self, encrypted_key, **kwargs): raise NotImplementedError("The current RsaKey does not support unwrap") algorithm = self._get_algorithm("unwrapKey", **kwargs) - decryptor = algorithm.create_decryptor(self._rsa_impl) + decryptor = algorithm.create_decryptor(self.private_key) return decryptor.transform(encrypted_key) def is_private_key(self): @@ -223,20 +222,3 @@ def _public_key_material(self): def _private_key_material(self): return self.private_key.private_numbers() if self.private_key else None - - -def _bytes_to_int(b): - return int(codecs.encode(b, "hex"), 16) - - -def _int_to_bytes(i): - h = hex(i) - if len(h) > 1 and h[0:2] == "0x": - h = h[2:] - - # need to strip L in python 2.x - h = h.strip("L") - - if len(h) % 2: - h = "0" + h - return codecs.decode(h, "hex") diff --git a/sdk/keyvault/azure-keyvault-keys/azure/keyvault/keys/crypto/aio/_client.py b/sdk/keyvault/azure-keyvault-keys/azure/keyvault/keys/crypto/aio/_client.py index e0f8d3467dbf..9fae0a14fb76 100644 --- a/sdk/keyvault/azure-keyvault-keys/azure/keyvault/keys/crypto/aio/_client.py +++ b/sdk/keyvault/azure-keyvault-keys/azure/keyvault/keys/crypto/aio/_client.py @@ -4,11 +4,12 @@ # ------------------------------------ from azure.core.exceptions import AzureError, HttpResponseError from azure.core.tracing.decorator_async import distributed_trace_async -from azure.keyvault.keys._shared import AsyncKeyVaultClientBase, parse_vault_id from .. import DecryptResult, EncryptResult, SignResult, VerifyResult, UnwrapResult, WrapResult from .._internal import EllipticCurveKey, RsaKey, SymmetricKey +from ...crypto._client import _enforce_nbf_exp from ..._models import KeyVaultKey +from ..._shared import AsyncKeyVaultClientBase, parse_vault_id try: from typing import TYPE_CHECKING @@ -18,7 +19,7 @@ if TYPE_CHECKING: # pylint:disable=unused-import from typing import Any, Optional, Union - from azure.core.credentials import TokenCredential + from azure.core.credentials_async import AsyncTokenCredential from .. import EncryptionAlgorithm, KeyWrapAlgorithm, SignatureAlgorithm from .._internal import Key as _Key @@ -57,7 +58,7 @@ class CryptographyClient(AsyncKeyVaultClientBase): """ - def __init__(self, key: "Union[KeyVaultKey, str]", credential: "TokenCredential", **kwargs: "Any") -> None: + def __init__(self, key: "Union[KeyVaultKey, str]", credential: "AsyncTokenCredential", **kwargs: "Any") -> None: if isinstance(key, KeyVaultKey): self._key = key self._key_id = parse_vault_id(key.id) @@ -77,9 +78,7 @@ def __init__(self, key: "Union[KeyVaultKey, str]", credential: "TokenCredential" self._internal_key = None # type: Optional[_Key] - super(CryptographyClient, self).__init__( - vault_url=self._key_id.vault_url, credential=credential, **kwargs - ) + super().__init__(vault_url=self._key_id.vault_url, credential=credential, **kwargs) @property def key_id(self) -> str: @@ -158,6 +157,7 @@ async def encrypt(self, algorithm: "EncryptionAlgorithm", plaintext: bytes, **kw local_key = await self._get_local_key(**kwargs) if local_key: + _enforce_nbf_exp(self._key) if "encrypt" not in self._allowed_ops: raise AzureError("This client doesn't have 'keys/encrypt' permission") result = local_key.encrypt(plaintext, algorithm=algorithm.value) @@ -224,6 +224,7 @@ async def wrap_key(self, algorithm: "KeyWrapAlgorithm", key: bytes, **kwargs: "A local_key = await self._get_local_key(**kwargs) if local_key: + _enforce_nbf_exp(self._key) if "wrapKey" not in self._allowed_ops: raise AzureError("This client doesn't have 'keys/wrapKey' permission") result = local_key.wrap_key(key, algorithm=algorithm.value) diff --git a/sdk/keyvault/azure-keyvault-keys/tests/recordings/test_crypto_client.test_local_validity_period_enforcement.yaml b/sdk/keyvault/azure-keyvault-keys/tests/recordings/test_crypto_client.test_local_validity_period_enforcement.yaml new file mode 100644 index 000000000000..df83d3511295 --- /dev/null +++ b/sdk/keyvault/azure-keyvault-keys/tests/recordings/test_crypto_client.test_local_validity_period_enforcement.yaml @@ -0,0 +1,370 @@ +interactions: +- request: + body: null + headers: + Accept: + - application/json + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + Content-Length: + - '0' + Content-Type: + - application/json; charset=utf-8 + User-Agent: + - azsdk-python-keyvault-keys/4.2.0b2 Python/3.5.4 (Windows-10-10.0.18362-SP0) + method: POST + uri: https://vaultname.vault.azure.net/keys/rsa-not-yet-valid/create?api-version=7.1-preview + response: + body: + string: '{"error":{"code":"Unauthorized","message":"Request is missing a Bearer + or PoP token."}}' + headers: + cache-control: + - no-cache + content-length: + - '87' + content-type: + - application/json; charset=utf-8 + date: + - Fri, 20 Mar 2020 19:51:32 GMT + expires: + - '-1' + pragma: + - no-cache + server: + - Microsoft-IIS/10.0 + strict-transport-security: + - max-age=31536000;includeSubDomains + www-authenticate: + - Bearer authorization="https://login.windows.net/72f988bf-86f1-41af-91ab-2d7cd011db47", + resource="https://vault.azure.net" + x-aspnet-version: + - 4.0.30319 + x-content-type-options: + - nosniff + x-ms-keyvault-network-info: + - addr=76.121.58.221;act_addr_fam=InterNetwork; + x-ms-keyvault-region: + - westus + x-ms-keyvault-service-version: + - 1.1.0.898 + x-powered-by: + - ASP.NET + status: + code: 401 + message: Unauthorized +- request: + body: '{"attributes": {"nbf": 32503680000}, "kty": "RSA"}' + headers: + Accept: + - application/json + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + Content-Length: + - '50' + Content-Type: + - application/json; charset=utf-8 + User-Agent: + - azsdk-python-keyvault-keys/4.2.0b2 Python/3.5.4 (Windows-10-10.0.18362-SP0) + method: POST + uri: https://vaultname.vault.azure.net/keys/rsa-not-yet-valid/create?api-version=7.1-preview + response: + body: + string: '{"key":{"kid":"https://vaultname.vault.azure.net/keys/rsa-not-yet-valid/1460aacb704740988841fafb23bdb01d","kty":"RSA","key_ops":["encrypt","decrypt","sign","verify","wrapKey","unwrapKey"],"n":"uJ7KgbHcIg_ltIhsHzfUrTrgLZtSJpDfr3QfmBsa6hEm7kBKGO1NWWRgyeHVWUGNgJHhk6k2CcIuZv7Wux2HaOrMBya7F0XEBNJXduvLdDZkxjTUvCJ7QnvbZyJsiqQpfgsgRNZk61wOSqnEjIIY9VRShrUUtcgNd6SZQ5vK6EvODHSdMQSs91qcYyTIX1pk-V1nzxzQCbEmic6kT7G15KR9XQ7am1YHR5qPHlmu-euOwglJx_UVgtIth1A5RvKaWmvH8KOzUzvBNhvBC-q-5MjLOGGU3P4yCi0n9sIhM3laJkpDZjBoVqzC7VhUBQR60DuTloSdEhcI5eNwQxh-bw","e":"AQAB"},"attributes":{"enabled":true,"nbf":32503680000,"created":1584733893,"updated":1584733893,"recoveryLevel":"Recoverable+Purgeable","recoverableDays":90}}' + headers: + cache-control: + - no-cache + content-length: + - '715' + content-type: + - application/json; charset=utf-8 + date: + - Fri, 20 Mar 2020 19:51:33 GMT + expires: + - '-1' + pragma: + - no-cache + server: + - Microsoft-IIS/10.0 + strict-transport-security: + - max-age=31536000;includeSubDomains + x-aspnet-version: + - 4.0.30319 + x-content-type-options: + - nosniff + x-ms-keyvault-network-info: + - addr=76.121.58.221;act_addr_fam=InterNetwork; + x-ms-keyvault-region: + - westus + x-ms-keyvault-service-version: + - 1.1.0.898 + x-powered-by: + - ASP.NET + status: + code: 200 + message: OK +- request: + body: '{"attributes": {"nbf": 32503680000}, "kty": "EC"}' + headers: + Accept: + - application/json + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + Content-Length: + - '49' + Content-Type: + - application/json; charset=utf-8 + User-Agent: + - azsdk-python-keyvault-keys/4.2.0b2 Python/3.5.4 (Windows-10-10.0.18362-SP0) + method: POST + uri: https://vaultname.vault.azure.net/keys/ec-not-yet-valid/create?api-version=7.1-preview + response: + body: + string: '{"key":{"kid":"https://vaultname.vault.azure.net/keys/ec-not-yet-valid/995e8c4ae67943678ba3f8fec888faba","kty":"EC","key_ops":["sign","verify"],"crv":"P-256","x":"OisClUGMm6FO7mGeKuSkpMT2FczG7PEHidbkDN-NJfs","y":"AVZNy28U2_o2ihl-jbbl7izyS0EiBvmV7hAtsC5driM"},"attributes":{"enabled":true,"nbf":32503680000,"created":1584733894,"updated":1584733894,"recoveryLevel":"Recoverable+Purgeable","recoverableDays":90}}' + headers: + cache-control: + - no-cache + content-length: + - '425' + content-type: + - application/json; charset=utf-8 + date: + - Fri, 20 Mar 2020 19:51:33 GMT + expires: + - '-1' + pragma: + - no-cache + server: + - Microsoft-IIS/10.0 + strict-transport-security: + - max-age=31536000;includeSubDomains + x-aspnet-version: + - 4.0.30319 + x-content-type-options: + - nosniff + x-ms-keyvault-network-info: + - addr=76.121.58.221;act_addr_fam=InterNetwork; + x-ms-keyvault-region: + - westus + x-ms-keyvault-service-version: + - 1.1.0.898 + x-powered-by: + - ASP.NET + status: + code: 200 + message: OK +- request: + body: '{"attributes": {"exp": 946684800}, "kty": "RSA"}' + headers: + Accept: + - application/json + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + Content-Length: + - '48' + Content-Type: + - application/json; charset=utf-8 + User-Agent: + - azsdk-python-keyvault-keys/4.2.0b2 Python/3.5.4 (Windows-10-10.0.18362-SP0) + method: POST + uri: https://vaultname.vault.azure.net/keys/rsa-expired/create?api-version=7.1-preview + response: + body: + string: '{"key":{"kid":"https://vaultname.vault.azure.net/keys/rsa-expired/64dcda598a884f6d8398f4e23727ed57","kty":"RSA","key_ops":["encrypt","decrypt","sign","verify","wrapKey","unwrapKey"],"n":"sLGAAoDiY3gyQ0RJpdohqedKPLe4KGXaI8AyJTgZX5r8btRSmE_i2YTr1uxEoVekjwggiRZTo0owrhSR5MwGzWyJYOm5Xn6Aa5_ZW2Y2geVSckA6wXorRbbSUB17Ebg6qaooEjgl5D_ONk7UPJ-D7V_B3bgY-xq_cvfX6J5kmRBElh-63wfcrWz0C5wdr7SjtfWDAaJ4TyJKoHsgA5uvYyAgigqjiHV2tgnEESMg35hd_O4nSqLNNeRAGwbywXZ0Etm8JiwIRmBVjgSHECtgGO1PvC9hM45CgdS9jLPqLW6JWZLEztFxcwB1VABYXSVs6Nu_MnyNSq0c6vtbM0H9pQ","e":"AQAB"},"attributes":{"enabled":true,"exp":946684800,"created":1584733894,"updated":1584733894,"recoveryLevel":"Recoverable+Purgeable","recoverableDays":90}}' + headers: + cache-control: + - no-cache + content-length: + - '707' + content-type: + - application/json; charset=utf-8 + date: + - Fri, 20 Mar 2020 19:51:33 GMT + expires: + - '-1' + pragma: + - no-cache + server: + - Microsoft-IIS/10.0 + strict-transport-security: + - max-age=31536000;includeSubDomains + x-aspnet-version: + - 4.0.30319 + x-content-type-options: + - nosniff + x-ms-keyvault-network-info: + - addr=76.121.58.221;act_addr_fam=InterNetwork; + x-ms-keyvault-region: + - westus + x-ms-keyvault-service-version: + - 1.1.0.898 + x-powered-by: + - ASP.NET + status: + code: 200 + message: OK +- request: + body: '{"attributes": {"exp": 946684800}, "kty": "EC"}' + headers: + Accept: + - application/json + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + Content-Length: + - '47' + Content-Type: + - application/json; charset=utf-8 + User-Agent: + - azsdk-python-keyvault-keys/4.2.0b2 Python/3.5.4 (Windows-10-10.0.18362-SP0) + method: POST + uri: https://vaultname.vault.azure.net/keys/ec-expired/create?api-version=7.1-preview + response: + body: + string: '{"key":{"kid":"https://vaultname.vault.azure.net/keys/ec-expired/85cacb15b40e423f83e0354636fd898c","kty":"EC","key_ops":["sign","verify"],"crv":"P-256","x":"41cGfisihfHqmzlW4VTUY0Aiu4zLj_yHjNPvjBkmVbY","y":"-7erU6kv6KUGZj83f_r_gZkLmjzn0tP_8diwNLtOX0w"},"attributes":{"enabled":true,"exp":946684800,"created":1584733894,"updated":1584733894,"recoveryLevel":"Recoverable+Purgeable","recoverableDays":90}}' + headers: + cache-control: + - no-cache + content-length: + - '417' + content-type: + - application/json; charset=utf-8 + date: + - Fri, 20 Mar 2020 19:51:33 GMT + expires: + - '-1' + pragma: + - no-cache + server: + - Microsoft-IIS/10.0 + strict-transport-security: + - max-age=31536000;includeSubDomains + x-aspnet-version: + - 4.0.30319 + x-content-type-options: + - nosniff + x-ms-keyvault-network-info: + - addr=76.121.58.221;act_addr_fam=InterNetwork; + x-ms-keyvault-region: + - westus + x-ms-keyvault-service-version: + - 1.1.0.898 + x-powered-by: + - ASP.NET + status: + code: 200 + message: OK +- request: + body: '{"attributes": {"nbf": 32503680000, "exp": 32535216000}, "kty": "RSA"}' + headers: + Accept: + - application/json + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + Content-Length: + - '70' + Content-Type: + - application/json; charset=utf-8 + User-Agent: + - azsdk-python-keyvault-keys/4.2.0b2 Python/3.5.4 (Windows-10-10.0.18362-SP0) + method: POST + uri: https://vaultname.vault.azure.net/keys/rsa-valid/create?api-version=7.1-preview + response: + body: + string: '{"key":{"kid":"https://vaultname.vault.azure.net/keys/rsa-valid/b33d044648764ae2a5e918ece13ea090","kty":"RSA","key_ops":["encrypt","decrypt","sign","verify","wrapKey","unwrapKey"],"n":"2xuX3gz7of3s_RQTOSLUeisFxIQBF3bp7v1VN-SSGk_h4gc6lTTyI9a5rfCO9FRKUG2VP5r83o_zWUmFIYjSx9vlwhqW8FOOURA7X9ia1Hf-TMPeHG0KXAaDqSRe3R0zTVk6uLzMHJHqgpTvOA_cmVneCGjA4__IdiBiDjP7KTcU40blq7N0wjDrrPvX-hP5CmLko3WgDUUYY6pH_17pPyPOnqN6hGaLPf65o4Ithb4p8z3oHrzmtQY3rAxMGUyHBqf1ek5XQEcsQkbqa6DeMl7RsYacaeDEVWqcIeHk-XzXUlW8_e8BPK76dq-wye_DJu89FEKka5b7XRQdLJf21w","e":"AQAB"},"attributes":{"enabled":true,"nbf":32503680000,"exp":32535216000,"created":1584733894,"updated":1584733894,"recoveryLevel":"Recoverable+Purgeable","recoverableDays":90}}' + headers: + cache-control: + - no-cache + content-length: + - '725' + content-type: + - application/json; charset=utf-8 + date: + - Fri, 20 Mar 2020 19:51:33 GMT + expires: + - '-1' + pragma: + - no-cache + server: + - Microsoft-IIS/10.0 + strict-transport-security: + - max-age=31536000;includeSubDomains + x-aspnet-version: + - 4.0.30319 + x-content-type-options: + - nosniff + x-ms-keyvault-network-info: + - addr=76.121.58.221;act_addr_fam=InterNetwork; + x-ms-keyvault-region: + - westus + x-ms-keyvault-service-version: + - 1.1.0.898 + x-powered-by: + - ASP.NET + status: + code: 200 + message: OK +- request: + body: '{"attributes": {"nbf": 32503680000, "exp": 32535216000}, "kty": "EC"}' + headers: + Accept: + - application/json + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + Content-Length: + - '69' + Content-Type: + - application/json; charset=utf-8 + User-Agent: + - azsdk-python-keyvault-keys/4.2.0b2 Python/3.5.4 (Windows-10-10.0.18362-SP0) + method: POST + uri: https://vaultname.vault.azure.net/keys/ec-valid/create?api-version=7.1-preview + response: + body: + string: '{"key":{"kid":"https://vaultname.vault.azure.net/keys/ec-valid/e5491c2d9a874e51aed7b11c4831c3da","kty":"EC","key_ops":["sign","verify"],"crv":"P-256","x":"k7NEW0UOGk1HY-iJFEXVauRLZbjcI_qmsBaou7waoZk","y":"UXNWcIQwwSuGiAtBxTkh6S9J6Ahq3IPVrApaKivQUGs"},"attributes":{"enabled":true,"nbf":32503680000,"exp":32535216000,"created":1584733894,"updated":1584733894,"recoveryLevel":"Recoverable+Purgeable","recoverableDays":90}}' + headers: + cache-control: + - no-cache + content-length: + - '435' + content-type: + - application/json; charset=utf-8 + date: + - Fri, 20 Mar 2020 19:51:33 GMT + expires: + - '-1' + pragma: + - no-cache + server: + - Microsoft-IIS/10.0 + strict-transport-security: + - max-age=31536000;includeSubDomains + x-aspnet-version: + - 4.0.30319 + x-content-type-options: + - nosniff + x-ms-keyvault-network-info: + - addr=76.121.58.221;act_addr_fam=InterNetwork; + x-ms-keyvault-region: + - westus + x-ms-keyvault-service-version: + - 1.1.0.898 + x-powered-by: + - ASP.NET + status: + code: 200 + message: OK +version: 1 diff --git a/sdk/keyvault/azure-keyvault-keys/tests/recordings/test_crypto_client_async.test_local_validity_period_enforcement.yaml b/sdk/keyvault/azure-keyvault-keys/tests/recordings/test_crypto_client_async.test_local_validity_period_enforcement.yaml new file mode 100644 index 000000000000..ee82be24f57d --- /dev/null +++ b/sdk/keyvault/azure-keyvault-keys/tests/recordings/test_crypto_client_async.test_local_validity_period_enforcement.yaml @@ -0,0 +1,250 @@ +interactions: +- request: + body: null + headers: + Accept: + - application/json + Content-Length: + - '0' + Content-Type: + - application/json; charset=utf-8 + User-Agent: + - azsdk-python-keyvault-keys/4.2.0b2 Python/3.5.4 (Windows-10-10.0.18362-SP0) + method: POST + uri: https://vaultname.vault.azure.net/keys/rsa-not-yet-valid/create?api-version=7.1-preview + response: + body: + string: '{"error":{"code":"Unauthorized","message":"Request is missing a Bearer + or PoP token."}}' + headers: + cache-control: no-cache + content-length: '87' + content-type: application/json; charset=utf-8 + date: Fri, 20 Mar 2020 19:59:44 GMT + expires: '-1' + pragma: no-cache + server: Microsoft-IIS/10.0 + strict-transport-security: max-age=31536000;includeSubDomains + www-authenticate: Bearer authorization="https://login.windows.net/72f988bf-86f1-41af-91ab-2d7cd011db47", + resource="https://vault.azure.net" + x-aspnet-version: 4.0.30319 + x-content-type-options: nosniff + x-ms-keyvault-network-info: addr=76.121.58.221;act_addr_fam=InterNetwork; + x-ms-keyvault-region: westus + x-ms-keyvault-service-version: 1.1.0.898 + x-powered-by: ASP.NET + status: + code: 401 + message: Unauthorized + url: https://r2gy5gpzzda6sxycp53mdnwx.vault.azure.net/keys/rsa-not-yet-valid/create?api-version=7.1-preview +- request: + body: '{"attributes": {"nbf": 32503680000}, "kty": "RSA"}' + headers: + Accept: + - application/json + Content-Length: + - '50' + Content-Type: + - application/json; charset=utf-8 + User-Agent: + - azsdk-python-keyvault-keys/4.2.0b2 Python/3.5.4 (Windows-10-10.0.18362-SP0) + method: POST + uri: https://vaultname.vault.azure.net/keys/rsa-not-yet-valid/create?api-version=7.1-preview + response: + body: + string: '{"key":{"kid":"https://vaultname.vault.azure.net/keys/rsa-not-yet-valid/7e86762e2e5a41c69ddf46da6443721d","kty":"RSA","key_ops":["encrypt","decrypt","sign","verify","wrapKey","unwrapKey"],"n":"n_f1Zxk8H2FYnM6tf9kH3whK721rSOktfV4bgfzS-N6drnAZpuIxiTzcrXYqCbgbsk4Q3mZO46j1mKYEklwcFXsaqV5WMxx-rEvs8yRW4ydBhp-uXhq4sFHZlLd4RbdcCKBuJVVX0v8Z2uv7gsws_LAt7VbBlNGwB3On00pT_L8gPuBG9uzyvXPrOhvs2oxgvou5HGEzMpTQyfSMrQ5gi8cUMBbRksseftPPbCUtJAPX7dxWdj3PtDn1YVgogSyxnyR9jPNqkMS3UvP1ogryz-2KnEgvU-0LYXllNyDPK-ZqnrUitT6ghGbGpIVAEKfEMXR3kGrU0Ie7DfvKIUlLZQ","e":"AQAB"},"attributes":{"enabled":true,"nbf":32503680000,"created":1584734384,"updated":1584734384,"recoveryLevel":"Recoverable+Purgeable","recoverableDays":90}}' + headers: + cache-control: no-cache + content-length: '715' + content-type: application/json; charset=utf-8 + date: Fri, 20 Mar 2020 19:59:44 GMT + expires: '-1' + pragma: no-cache + server: Microsoft-IIS/10.0 + strict-transport-security: max-age=31536000;includeSubDomains + x-aspnet-version: 4.0.30319 + x-content-type-options: nosniff + x-ms-keyvault-network-info: addr=76.121.58.221;act_addr_fam=InterNetwork; + x-ms-keyvault-region: westus + x-ms-keyvault-service-version: 1.1.0.898 + x-powered-by: ASP.NET + status: + code: 200 + message: OK + url: https://r2gy5gpzzda6sxycp53mdnwx.vault.azure.net/keys/rsa-not-yet-valid/create?api-version=7.1-preview +- request: + body: '{"attributes": {"nbf": 32503680000}, "kty": "EC"}' + headers: + Accept: + - application/json + Content-Length: + - '49' + Content-Type: + - application/json; charset=utf-8 + User-Agent: + - azsdk-python-keyvault-keys/4.2.0b2 Python/3.5.4 (Windows-10-10.0.18362-SP0) + method: POST + uri: https://vaultname.vault.azure.net/keys/ec-not-yet-valid/create?api-version=7.1-preview + response: + body: + string: '{"key":{"kid":"https://vaultname.vault.azure.net/keys/ec-not-yet-valid/5b250d819b2942d8b97ae31761be4e25","kty":"EC","key_ops":["sign","verify"],"crv":"P-256","x":"MGBrShFjdRDxzUm2q6Nb5VYEWyZ9HtqfJK9hSbRZsyk","y":"epWjy1j8_hhcJ_mGj41vEZOM6CYZN9ppX2lytWrnkLs"},"attributes":{"enabled":true,"nbf":32503680000,"created":1584734384,"updated":1584734384,"recoveryLevel":"Recoverable+Purgeable","recoverableDays":90}}' + headers: + cache-control: no-cache + content-length: '425' + content-type: application/json; charset=utf-8 + date: Fri, 20 Mar 2020 19:59:44 GMT + expires: '-1' + pragma: no-cache + server: Microsoft-IIS/10.0 + strict-transport-security: max-age=31536000;includeSubDomains + x-aspnet-version: 4.0.30319 + x-content-type-options: nosniff + x-ms-keyvault-network-info: addr=76.121.58.221;act_addr_fam=InterNetwork; + x-ms-keyvault-region: westus + x-ms-keyvault-service-version: 1.1.0.898 + x-powered-by: ASP.NET + status: + code: 200 + message: OK + url: https://r2gy5gpzzda6sxycp53mdnwx.vault.azure.net/keys/ec-not-yet-valid/create?api-version=7.1-preview +- request: + body: '{"attributes": {"exp": 946684800}, "kty": "RSA"}' + headers: + Accept: + - application/json + Content-Length: + - '48' + Content-Type: + - application/json; charset=utf-8 + User-Agent: + - azsdk-python-keyvault-keys/4.2.0b2 Python/3.5.4 (Windows-10-10.0.18362-SP0) + method: POST + uri: https://vaultname.vault.azure.net/keys/rsa-expired/create?api-version=7.1-preview + response: + body: + string: '{"key":{"kid":"https://vaultname.vault.azure.net/keys/rsa-expired/dd6358e9c6814c5883b3f25e516cfa0b","kty":"RSA","key_ops":["encrypt","decrypt","sign","verify","wrapKey","unwrapKey"],"n":"zpnwFfZQXMyhN_83cPqjDfOD543vk2F76cflEAJdmvYvlx-ytb35nB5ZWjEAAmCRJG2wfcX_JLwmsD-TNhJJiBsZruf951HrqKlsY5Cr25-4IJs11jPuajucYXi6P4BWhGt-Mv-qYSLB7k1BiNNJSwwwOVCobFDCO5ffTAz7rnvu4ixPh5GeDyVP2vdmt8US8aBPE8inlatr30FFY_Tu4ZYemOIW4frmq_JMDGV4AF6Gln0eBJLCHlcQYLoWDpzTs4RdgH5rvu9K7wQK2JdM5xVuHX0pUBWriz9MGB8rDZWbbp_uerF_nnYedLB4IDwhzwhweVyrYE6SpzCrQvkEPw","e":"AQAB"},"attributes":{"enabled":true,"exp":946684800,"created":1584734385,"updated":1584734385,"recoveryLevel":"Recoverable+Purgeable","recoverableDays":90}}' + headers: + cache-control: no-cache + content-length: '707' + content-type: application/json; charset=utf-8 + date: Fri, 20 Mar 2020 19:59:44 GMT + expires: '-1' + pragma: no-cache + server: Microsoft-IIS/10.0 + strict-transport-security: max-age=31536000;includeSubDomains + x-aspnet-version: 4.0.30319 + x-content-type-options: nosniff + x-ms-keyvault-network-info: addr=76.121.58.221;act_addr_fam=InterNetwork; + x-ms-keyvault-region: westus + x-ms-keyvault-service-version: 1.1.0.898 + x-powered-by: ASP.NET + status: + code: 200 + message: OK + url: https://r2gy5gpzzda6sxycp53mdnwx.vault.azure.net/keys/rsa-expired/create?api-version=7.1-preview +- request: + body: '{"attributes": {"exp": 946684800}, "kty": "EC"}' + headers: + Accept: + - application/json + Content-Length: + - '47' + Content-Type: + - application/json; charset=utf-8 + User-Agent: + - azsdk-python-keyvault-keys/4.2.0b2 Python/3.5.4 (Windows-10-10.0.18362-SP0) + method: POST + uri: https://vaultname.vault.azure.net/keys/ec-expired/create?api-version=7.1-preview + response: + body: + string: '{"key":{"kid":"https://vaultname.vault.azure.net/keys/ec-expired/c4d84e4537b84df78f10478cf584f101","kty":"EC","key_ops":["sign","verify"],"crv":"P-256","x":"TZFmYa1LSeR4nd9b82pozBG4xh24I-ATbu4MkzJ0eRI","y":"nYosAlTxNpBVtFviuBzHF4swNRT7JahJoH6_-fIKI6k"},"attributes":{"enabled":true,"exp":946684800,"created":1584734385,"updated":1584734385,"recoveryLevel":"Recoverable+Purgeable","recoverableDays":90}}' + headers: + cache-control: no-cache + content-length: '417' + content-type: application/json; charset=utf-8 + date: Fri, 20 Mar 2020 19:59:45 GMT + expires: '-1' + pragma: no-cache + server: Microsoft-IIS/10.0 + strict-transport-security: max-age=31536000;includeSubDomains + x-aspnet-version: 4.0.30319 + x-content-type-options: nosniff + x-ms-keyvault-network-info: addr=76.121.58.221;act_addr_fam=InterNetwork; + x-ms-keyvault-region: westus + x-ms-keyvault-service-version: 1.1.0.898 + x-powered-by: ASP.NET + status: + code: 200 + message: OK + url: https://r2gy5gpzzda6sxycp53mdnwx.vault.azure.net/keys/ec-expired/create?api-version=7.1-preview +- request: + body: '{"attributes": {"exp": 32535216000, "nbf": 32503680000}, "kty": "RSA"}' + headers: + Accept: + - application/json + Content-Length: + - '70' + Content-Type: + - application/json; charset=utf-8 + User-Agent: + - azsdk-python-keyvault-keys/4.2.0b2 Python/3.5.4 (Windows-10-10.0.18362-SP0) + method: POST + uri: https://vaultname.vault.azure.net/keys/rsa-valid/create?api-version=7.1-preview + response: + body: + string: '{"key":{"kid":"https://vaultname.vault.azure.net/keys/rsa-valid/8c594c653af945a88db7a8c4cc8c3e93","kty":"RSA","key_ops":["encrypt","decrypt","sign","verify","wrapKey","unwrapKey"],"n":"r9NyKZixLoYzwDtOcTG0KtIf8S-7zuSeUePJ2wvuCprhzVIICqUNu8F-kkLxNOl0ApEVFEtqRmcshpd_h17jySjoWzYCT53pGUEPRt0wTCp2r-O_rzk6p3r9lnb9BxN-FrCRU3At1Ql6HVzo4tP0xHvCuRjolStTB6JqePEm3I6YI_0im8d1AQbMf0EdP-5ot3Fam5BlpVctnm2ES9cfy2RMs5c08VuPNB-SylzxPtMLMYaBbn4uurqKalbpZdtcEPFa4fmRg661l2ZjwVoP6RnWMFE0aIsu3szOK7WhYn5mWX6Wbib0lMpOlx5TKSD_u9qiFKWYAeldOgROakh88w","e":"AQAB"},"attributes":{"enabled":true,"nbf":32503680000,"exp":32535216000,"created":1584734385,"updated":1584734385,"recoveryLevel":"Recoverable+Purgeable","recoverableDays":90}}' + headers: + cache-control: no-cache + content-length: '725' + content-type: application/json; charset=utf-8 + date: Fri, 20 Mar 2020 19:59:45 GMT + expires: '-1' + pragma: no-cache + server: Microsoft-IIS/10.0 + strict-transport-security: max-age=31536000;includeSubDomains + x-aspnet-version: 4.0.30319 + x-content-type-options: nosniff + x-ms-keyvault-network-info: addr=76.121.58.221;act_addr_fam=InterNetwork; + x-ms-keyvault-region: westus + x-ms-keyvault-service-version: 1.1.0.898 + x-powered-by: ASP.NET + status: + code: 200 + message: OK + url: https://r2gy5gpzzda6sxycp53mdnwx.vault.azure.net/keys/rsa-valid/create?api-version=7.1-preview +- request: + body: '{"attributes": {"exp": 32535216000, "nbf": 32503680000}, "kty": "EC"}' + headers: + Accept: + - application/json + Content-Length: + - '69' + Content-Type: + - application/json; charset=utf-8 + User-Agent: + - azsdk-python-keyvault-keys/4.2.0b2 Python/3.5.4 (Windows-10-10.0.18362-SP0) + method: POST + uri: https://vaultname.vault.azure.net/keys/ec-valid/create?api-version=7.1-preview + response: + body: + string: '{"key":{"kid":"https://vaultname.vault.azure.net/keys/ec-valid/15ded77c448d4541a9db521aff47fe14","kty":"EC","key_ops":["sign","verify"],"crv":"P-256","x":"cyTRkx71JgizNK9kdZqNfS2GIlVhWVXqOQAssKqzy10","y":"tCR7fSTtr60gSzsCRjFCvUAFywN-bjGEEyzVTzrtq8c"},"attributes":{"enabled":true,"nbf":32503680000,"exp":32535216000,"created":1584734385,"updated":1584734385,"recoveryLevel":"Recoverable+Purgeable","recoverableDays":90}}' + headers: + cache-control: no-cache + content-length: '435' + content-type: application/json; charset=utf-8 + date: Fri, 20 Mar 2020 19:59:45 GMT + expires: '-1' + pragma: no-cache + server: Microsoft-IIS/10.0 + strict-transport-security: max-age=31536000;includeSubDomains + x-aspnet-version: 4.0.30319 + x-content-type-options: nosniff + x-ms-keyvault-network-info: addr=76.121.58.221;act_addr_fam=InterNetwork; + x-ms-keyvault-region: westus + x-ms-keyvault-service-version: 1.1.0.898 + x-powered-by: ASP.NET + status: + code: 200 + message: OK + url: https://r2gy5gpzzda6sxycp53mdnwx.vault.azure.net/keys/ec-valid/create?api-version=7.1-preview +version: 1 diff --git a/sdk/keyvault/azure-keyvault-keys/tests/test_crypto_client.py b/sdk/keyvault/azure-keyvault-keys/tests/test_crypto_client.py index 6965c8697e93..7ec2f53df1ed 100644 --- a/sdk/keyvault/azure-keyvault-keys/tests/test_crypto_client.py +++ b/sdk/keyvault/azure-keyvault-keys/tests/test_crypto_client.py @@ -6,12 +6,15 @@ import functools import hashlib import os +from datetime import datetime from azure.core.credentials import AccessToken from azure.keyvault.keys import JsonWebKey, KeyClient, KeyCurveName, KeyVaultKey from azure.keyvault.keys.crypto import CryptographyClient, EncryptionAlgorithm, KeyWrapAlgorithm, SignatureAlgorithm +from azure.keyvault.keys.crypto._client import _UTC from azure.mgmt.keyvault.models import KeyPermissions, Permissions from devtools_testutils import ResourceGroupPreparer, KeyVaultPreparer +import pytest from _shared.json_attribute_matcher import json_attribute_matcher from _shared.test_case import KeyVaultTestCase @@ -82,7 +85,6 @@ def _to_bytes(hex): @KeyVaultPreparer(permissions=NO_GET) @CryptoClientPreparer() def test_encrypt_and_decrypt(self, key_client, credential, **kwargs): - # TODO: use iv, authentication_data key_name = self.get_resource_name("keycrypt") imported_key = self._import_test_key(key_client, key_name) @@ -100,7 +102,6 @@ def test_encrypt_and_decrypt(self, key_client, credential, **kwargs): @KeyVaultPreparer(permissions=NO_GET) @CryptoClientPreparer() def test_sign_and_verify(self, key_client, credential, **kwargs): - key_name = self.get_resource_name("keysign") md = hashlib.sha256() @@ -229,3 +230,59 @@ def test_ec_verify_local(self, key_client, credential, **kwargs): result = crypto_client.verify(result.algorithm, digest, result.signature) self.assertTrue(result.is_valid) + + @ResourceGroupPreparer(random_name_enabled=True) + @KeyVaultPreparer(permissions=NO_GET) + @CryptoClientPreparer() + def test_local_validity_period_enforcement(self, key_client, credential, **kwargs): + """Local crypto operations should respect a key's nbf and exp properties""" + + def test_operations(key, expected_error_substrings, encrypt_algorithms, wrap_algorithms): + crypto_client = CryptographyClient(key, credential) + for algorithm in encrypt_algorithms: + with pytest.raises(ValueError) as ex: + crypto_client.encrypt(algorithm, self.plaintext) + for substring in expected_error_substrings: + assert substring in str(ex.value) + for algorithm in wrap_algorithms: + with pytest.raises(ValueError) as ex: + crypto_client.wrap_key(algorithm, self.plaintext) + for substring in expected_error_substrings: + assert substring in str(ex.value) + + # operations should not succeed with a key whose nbf is in the future + the_year_3000 = datetime(3000, 1, 1, tzinfo=_UTC) + + rsa_wrap_algorithms = [algo for algo in KeyWrapAlgorithm if algo.startswith("RSA")] + rsa_not_yet_valid = key_client.create_rsa_key("rsa-not-yet-valid", not_before=the_year_3000) + test_operations(rsa_not_yet_valid, [str(the_year_3000)], EncryptionAlgorithm, rsa_wrap_algorithms) + + ec_not_yet_valid = key_client.create_ec_key("ec-not-yet-valid", not_before=the_year_3000) + test_operations( + ec_not_yet_valid, [str(the_year_3000)], encrypt_algorithms=[], wrap_algorithms=[KeyWrapAlgorithm.aes_256] + ) + + # nor should they succeed with a key whose exp has passed + the_year_2000 = datetime(2000, 1, 1, tzinfo=_UTC) + + rsa_expired = key_client.create_rsa_key("rsa-expired", expires_on=the_year_2000) + test_operations(rsa_expired, [str(the_year_2000)], EncryptionAlgorithm, rsa_wrap_algorithms) + + ec_expired = key_client.create_ec_key("ec-expired", expires_on=the_year_2000) + test_operations( + ec_expired, [str(the_year_2000)], encrypt_algorithms=[], wrap_algorithms=[KeyWrapAlgorithm.aes_256] + ) + + # when exp and nbf are set, error messages should contain both + the_year_3001 = datetime(3001, 1, 1, tzinfo=_UTC) + + rsa_valid = key_client.create_rsa_key("rsa-valid", not_before=the_year_3000, expires_on=the_year_3001) + test_operations(rsa_valid, (str(the_year_3000), str(the_year_3001)), EncryptionAlgorithm, rsa_wrap_algorithms) + + ec_valid = key_client.create_ec_key("ec-valid", not_before=the_year_3000, expires_on=the_year_3001) + test_operations( + ec_valid, + (str(the_year_3000), str(the_year_3001)), + encrypt_algorithms=[], + wrap_algorithms=[KeyWrapAlgorithm.aes_256], + ) diff --git a/sdk/keyvault/azure-keyvault-keys/tests/test_crypto_client_async.py b/sdk/keyvault/azure-keyvault-keys/tests/test_crypto_client_async.py index 1f069d86e781..ffb4113b7b7e 100644 --- a/sdk/keyvault/azure-keyvault-keys/tests/test_crypto_client_async.py +++ b/sdk/keyvault/azure-keyvault-keys/tests/test_crypto_client_async.py @@ -3,11 +3,12 @@ # Licensed under the MIT License. # ------------------------------------ import codecs -import functools +from datetime import datetime import hashlib import os from azure.keyvault.keys import JsonWebKey, KeyCurveName, KeyVaultKey +from azure.keyvault.keys.crypto._client import _UTC from azure.keyvault.keys.crypto.aio import CryptographyClient, EncryptionAlgorithm, KeyWrapAlgorithm, SignatureAlgorithm from azure.mgmt.keyvault.models import KeyPermissions, Permissions from devtools_testutils import ResourceGroupPreparer, KeyVaultPreparer @@ -15,7 +16,6 @@ from _shared.json_attribute_matcher import json_attribute_matcher from _shared.test_case_async import KeyVaultTestCase - from crypto_client_preparer_async import CryptoClientPreparer # without keys/get, a CryptographyClient created with a key ID performs all ops remotely @@ -81,7 +81,6 @@ def _to_bytes(hex): @KeyVaultPreparer(permissions=NO_GET) @CryptoClientPreparer() async def test_encrypt_and_decrypt(self, key_client, credential, **kwargs): - # TODO: use iv, authentication_data key_name = self.get_resource_name("keycrypt") imported_key = await self._import_test_key(key_client, key_name) @@ -216,14 +215,69 @@ async def test_ec_verify_local(self, key_client, credential, **kwargs): result = await crypto_client.verify(result.algorithm, digest, result.signature) self.assertTrue(result.is_valid) + @ResourceGroupPreparer(random_name_enabled=True) + @KeyVaultPreparer(permissions=NO_GET) + @CryptoClientPreparer() + async def test_local_validity_period_enforcement(self, key_client, credential, **kwargs): + """Local crypto operations should respect a key's nbf and exp properties""" + + async def test_operations(key, expected_error_substrings, encrypt_algorithms, wrap_algorithms): + crypto_client = CryptographyClient(key, credential) + for algorithm in encrypt_algorithms: + with pytest.raises(ValueError) as ex: + await crypto_client.encrypt(algorithm, self.plaintext) + for substring in expected_error_substrings: + assert substring in str(ex.value) + for algorithm in wrap_algorithms: + with pytest.raises(ValueError) as ex: + await crypto_client.wrap_key(algorithm, self.plaintext) + for substring in expected_error_substrings: + assert substring in str(ex.value) + + # operations should not succeed with a key whose nbf is in the future + the_year_3000 = datetime(3000, 1, 1, tzinfo=_UTC) + + rsa_wrap_algorithms = [algo for algo in KeyWrapAlgorithm if algo.startswith("RSA")] + rsa_not_yet_valid = await key_client.create_rsa_key("rsa-not-yet-valid", not_before=the_year_3000) + await test_operations(rsa_not_yet_valid, [str(the_year_3000)], EncryptionAlgorithm, rsa_wrap_algorithms) + + ec_not_yet_valid = await key_client.create_ec_key("ec-not-yet-valid", not_before=the_year_3000) + await test_operations( + ec_not_yet_valid, [str(the_year_3000)], encrypt_algorithms=[], wrap_algorithms=[KeyWrapAlgorithm.aes_256] + ) + + # nor should they succeed with a key whose exp has passed + the_year_2000 = datetime(2000, 1, 1, tzinfo=_UTC) + + rsa_expired = await key_client.create_rsa_key("rsa-expired", expires_on=the_year_2000) + await test_operations(rsa_expired, [str(the_year_2000)], EncryptionAlgorithm, rsa_wrap_algorithms) + + ec_expired = await key_client.create_ec_key("ec-expired", expires_on=the_year_2000) + await test_operations( + ec_expired, [str(the_year_2000)], encrypt_algorithms=[], wrap_algorithms=[KeyWrapAlgorithm.aes_256] + ) + + # when exp and nbf are set, error messages should contain both + the_year_3001 = datetime(3001, 1, 1, tzinfo=_UTC) + + rsa_valid = await key_client.create_rsa_key("rsa-valid", not_before=the_year_3000, expires_on=the_year_3001) + await test_operations( + rsa_valid, (str(the_year_3000), str(the_year_3001)), EncryptionAlgorithm, rsa_wrap_algorithms + ) + + ec_valid = await key_client.create_ec_key("ec-valid", not_before=the_year_3000, expires_on=the_year_3001) + await test_operations( + ec_valid, + (str(the_year_3000), str(the_year_3001)), + encrypt_algorithms=[], + wrap_algorithms=[KeyWrapAlgorithm.aes_256], + ) + @pytest.mark.asyncio async def test_symmetric_wrap_and_unwrap_local(): key = KeyVaultKey( - key_id="http://fake.test.vault/keys/key/version", - k=os.urandom(32), - kty="oct", - key_ops=["unwrapKey", "wrapKey"], + key_id="http://fake.test.vault/keys/key/version", k=os.urandom(32), kty="oct", key_ops=["unwrapKey", "wrapKey"], ) crypto_client = CryptographyClient(key, credential=lambda *_: None) From 4ee33688354115f5efebc6a407c843c34ff29475 Mon Sep 17 00:00:00 2001 From: Xiang Yan Date: Mon, 15 Jun 2020 11:45:09 -0700 Subject: [PATCH 04/21] disable some bandit warnings (#12054) --- .../azure/core/pipeline/policies/_universal.py | 2 +- .../azure-core/azure/core/polling/_poller.py | 2 +- .../azure/core/polling/base_polling.py | 2 +- .../tests/test_universal_pipeline.py | 2 +- .../_execution_context/endpoint_component.py | 4 ++-- sdk/cosmos/azure-cosmos/test/test_crud.py | 16 ++++++++-------- sdk/cosmos/azure-cosmos/test/test_globaldb.py | 4 ++-- .../azure-cosmos/test/test_multi_orderby.py | 2 +- sdk/cosmos/azure-cosmos/test/test_orderby.py | 8 ++++---- sdk/cosmos/azure-cosmos/test/test_query.py | 18 +++++++++--------- .../tests/test_mgmt_datalake_analytics.py | 2 +- .../_vendor/storage/blob/_shared/policies.py | 2 +- .../_vendor/storage/blob/_shared/policies.py | 2 +- .../tests/test_browser_credential.py | 4 ++-- .../azure/storage/blob/_shared/policies.py | 2 +- .../blob_samples_client_side_encryption.py | 8 ++++---- .../tests/encryption_test_helper.py | 8 ++++---- .../storage/filedatalake/_shared/policies.py | 2 +- .../tests/testcase.py | 2 +- .../storage/fileshare/_shared/policies.py | 2 +- .../tests/encryption_test_helper.py | 8 ++++---- .../azure/storage/queue/_shared/policies.py | 2 +- .../tests/encryption_test_helper.py | 8 ++++---- 23 files changed, 56 insertions(+), 56 deletions(-) diff --git a/sdk/core/azure-core/azure/core/pipeline/policies/_universal.py b/sdk/core/azure-core/azure/core/pipeline/policies/_universal.py index ea4cc0055103..a90f10a7bda4 100644 --- a/sdk/core/azure-core/azure/core/pipeline/policies/_universal.py +++ b/sdk/core/azure-core/azure/core/pipeline/policies/_universal.py @@ -501,7 +501,7 @@ def deserialize_from_text( data_as_str = cast(str, data_as_str.encode(encoding="utf-8")) except NameError: pass - return ET.fromstring(data_as_str) + return ET.fromstring(data_as_str) # nosec except ET.ParseError: # It might be because the server has an issue, and returned JSON with # content-type XML.... diff --git a/sdk/core/azure-core/azure/core/polling/_poller.py b/sdk/core/azure-core/azure/core/polling/_poller.py index fc0e79701245..7a8745a38e45 100644 --- a/sdk/core/azure-core/azure/core/polling/_poller.py +++ b/sdk/core/azure-core/azure/core/polling/_poller.py @@ -133,7 +133,7 @@ def from_continuation_token(cls, continuation_token, **kwargs): except KeyError: raise ValueError("Need kwarg 'deserialization_callback' to be recreated from continuation_token") import pickle - initial_response = pickle.loads(base64.b64decode(continuation_token)) + initial_response = pickle.loads(base64.b64decode(continuation_token)) # nosec return None, initial_response, deserialization_callback diff --git a/sdk/core/azure-core/azure/core/polling/base_polling.py b/sdk/core/azure-core/azure/core/polling/base_polling.py index 7ddcd6ef0007..ba6731efb1f8 100644 --- a/sdk/core/azure-core/azure/core/polling/base_polling.py +++ b/sdk/core/azure-core/azure/core/polling/base_polling.py @@ -467,7 +467,7 @@ def from_continuation_token(cls, continuation_token, **kwargs): raise ValueError("Need kwarg 'deserialization_callback' to be recreated from continuation_token") import pickle - initial_response = pickle.loads(base64.b64decode(continuation_token)) + initial_response = pickle.loads(base64.b64decode(continuation_token)) # nosec # Restore the transport in the context initial_response.context.transport = client._pipeline._transport # pylint: disable=protected-access return client, initial_response, deserialization_callback diff --git a/sdk/core/azure-core/tests/test_universal_pipeline.py b/sdk/core/azure-core/tests/test_universal_pipeline.py index 40223f06965f..b2d964ba225c 100644 --- a/sdk/core/azure-core/tests/test_universal_pipeline.py +++ b/sdk/core/azure-core/tests/test_universal_pipeline.py @@ -81,7 +81,7 @@ def test_pipeline_context(): serialized = pickle.dumps(context) - revived_context = pickle.loads(serialized) + revived_context = pickle.loads(serialized) # nosec assert revived_context.options == kwargs assert revived_context.transport is None assert 'deserialized_data' in revived_context diff --git a/sdk/cosmos/azure-cosmos/azure/cosmos/_execution_context/endpoint_component.py b/sdk/cosmos/azure-cosmos/azure/cosmos/_execution_context/endpoint_component.py index 9e1a70306bbf..619b25ee9563 100644 --- a/sdk/cosmos/azure-cosmos/azure/cosmos/_execution_context/endpoint_component.py +++ b/sdk/cosmos/azure-cosmos/azure/cosmos/_execution_context/endpoint_component.py @@ -126,7 +126,7 @@ def next(self): if six.PY3: json_repr = json_repr.encode("utf-8") - hash_object = hashlib.sha1(json_repr) + hash_object = hashlib.sha1(json_repr) # nosec hashed_result = hash_object.hexdigest() while hashed_result in self.last_result: @@ -135,7 +135,7 @@ def next(self): if six.PY3: json_repr = json_repr.encode("utf-8") - hash_object = hashlib.sha1(json_repr) + hash_object = hashlib.sha1(json_repr) # nosec hashed_result = hash_object.hexdigest() self.last_result.add(hashed_result) return res diff --git a/sdk/cosmos/azure-cosmos/test/test_crud.py b/sdk/cosmos/azure-cosmos/test/test_crud.py index a809382a197b..754c0d1108ac 100644 --- a/sdk/cosmos/azure-cosmos/test/test_crud.py +++ b/sdk/cosmos/azure-cosmos/test/test_crud.py @@ -207,7 +207,7 @@ def test_sql_query_crud(self): self.assertEqual(0, len(databases), 'Unexpected number of query results.') # query with a string. - databases = list(self.client.query_databases('SELECT * FROM root r WHERE r.id="' + db2.id + '"')) #nosec + databases = list(self.client.query_databases('SELECT * FROM root r WHERE r.id="' + db2.id + '"')) # nosec self.assertEqual(1, len(databases), 'Unexpected number of query results.') self.client.delete_database(db1.id) self.client.delete_database(db2.id) @@ -507,7 +507,7 @@ def test_partitioned_collection_document_crud_and_query(self): # query document on the partition key specified in the predicate will pass even without setting enableCrossPartitionQuery or passing in the partitionKey value documentlist = list(created_collection.query_items( { - 'query': 'SELECT * FROM root r WHERE r.id=\'' + replaced_document.get('id') + '\'' #nosec + 'query': 'SELECT * FROM root r WHERE r.id=\'' + replaced_document.get('id') + '\'' # nosec })) self.assertEqual(1, len(documentlist)) @@ -515,14 +515,14 @@ def test_partitioned_collection_document_crud_and_query(self): try: list(created_collection.query_items( { - 'query': 'SELECT * FROM root r WHERE r.key=\'' + replaced_document.get('key') + '\'' #nosec + 'query': 'SELECT * FROM root r WHERE r.key=\'' + replaced_document.get('key') + '\'' # nosec })) except Exception: pass # cross partition query documentlist = list(created_collection.query_items( - query='SELECT * FROM root r WHERE r.key=\'' + replaced_document.get('key') + '\'', #nosec + query='SELECT * FROM root r WHERE r.key=\'' + replaced_document.get('key') + '\'', # nosec enable_cross_partition_query=True )) @@ -530,7 +530,7 @@ def test_partitioned_collection_document_crud_and_query(self): # query document by providing the partitionKey value documentlist = list(created_collection.query_items( - query='SELECT * FROM root r WHERE r.key=\'' + replaced_document.get('key') + '\'', #nosec + query='SELECT * FROM root r WHERE r.key=\'' + replaced_document.get('key') + '\'', # nosec partition_key=replaced_document.get('id') )) @@ -746,14 +746,14 @@ def test_partitioned_collection_conflict_crud_and_query(self): # query conflicts on any property other than partitionKey will fail without setting enableCrossPartitionQuery or passing in the partitionKey value try: list(created_collection.query_conflicts( - query='SELECT * FROM root r WHERE r.resourceType=\'' + conflict_definition.get( #nosec + query='SELECT * FROM root r WHERE r.resourceType=\'' + conflict_definition.get( # nosec 'resourceType') + '\'' )) except Exception: pass conflictlist = list(created_collection.query_conflicts( - query='SELECT * FROM root r WHERE r.resourceType=\'' + conflict_definition.get('resourceType') + '\'', #nosec + query='SELECT * FROM root r WHERE r.resourceType=\'' + conflict_definition.get('resourceType') + '\'', # nosec enable_cross_partition_query=True )) @@ -762,7 +762,7 @@ def test_partitioned_collection_conflict_crud_and_query(self): # query conflicts by providing the partitionKey value options = {'partitionKey': conflict_definition.get('id')} conflictlist = list(created_collection.query_conflicts( - query='SELECT * FROM root r WHERE r.resourceType=\'' + conflict_definition.get('resourceType') + '\'', #nosec + query='SELECT * FROM root r WHERE r.resourceType=\'' + conflict_definition.get('resourceType') + '\'', # nosec partition_key=conflict_definition['id'] )) diff --git a/sdk/cosmos/azure-cosmos/test/test_globaldb.py b/sdk/cosmos/azure-cosmos/test/test_globaldb.py index 88a9289c6694..4d534c883dd4 100644 --- a/sdk/cosmos/azure-cosmos/test/test_globaldb.py +++ b/sdk/cosmos/azure-cosmos/test/test_globaldb.py @@ -89,7 +89,7 @@ def setUp(self): self.client = cosmos_client_connection.CosmosClientConnection(Test_globaldb_tests.host, Test_globaldb_tests.masterKey) # Create the test database only when it's not already present - query_iterable = self.client.QueryDatabases('SELECT * FROM root r WHERE r.id=\'' + Test_globaldb_tests.test_database_id + '\'') #nosec + query_iterable = self.client.QueryDatabases('SELECT * FROM root r WHERE r.id=\'' + Test_globaldb_tests.test_database_id + '\'') # nosec it = iter(query_iterable) self.test_db = next(it, None) @@ -97,7 +97,7 @@ def setUp(self): self.test_db = self.client.CreateDatabase({'id' : Test_globaldb_tests.test_database_id}) # Create the test collection only when it's not already present - query_iterable = self.client.QueryContainers(self.test_db['_self'], 'SELECT * FROM root r WHERE r.id=\'' + Test_globaldb_tests.test_collection_id + '\'') #nosec + query_iterable = self.client.QueryContainers(self.test_db['_self'], 'SELECT * FROM root r WHERE r.id=\'' + Test_globaldb_tests.test_collection_id + '\'') # nosec it = iter(query_iterable) self.test_coll = next(it, None) diff --git a/sdk/cosmos/azure-cosmos/test/test_multi_orderby.py b/sdk/cosmos/azure-cosmos/test/test_multi_orderby.py index 1b68b39ae090..8fadcf10f92f 100644 --- a/sdk/cosmos/azure-cosmos/test/test_multi_orderby.py +++ b/sdk/cosmos/azure-cosmos/test/test_multi_orderby.py @@ -252,7 +252,7 @@ def test_multi_orderby_queries(self): where_string = "WHERE root." + self.NUMBER_FIELD + " % 2 = 0" if has_filter else "" query = "SELECT " + top_string + " [" + select_item_builder + "] " + \ "FROM root " + where_string + " " + \ - "ORDER BY " + orderby_item_builder #nosec + "ORDER BY " + orderby_item_builder # nosec expected_ordered_list = self.top(self.sort(self.filter(self.items, has_filter), composite_index, invert), has_top, top_count) diff --git a/sdk/cosmos/azure-cosmos/test/test_orderby.py b/sdk/cosmos/azure-cosmos/test/test_orderby.py index da9e26bc3d16..5e6ebf1ad7d7 100644 --- a/sdk/cosmos/azure-cosmos/test/test_orderby.py +++ b/sdk/cosmos/azure-cosmos/test/test_orderby.py @@ -168,7 +168,7 @@ def test_orderby_top_query(self): # an order by query with top, total existing docs more than requested top count query = { - 'query': 'SELECT top %d * FROM root r order by r.spam' % top_count #nosec + 'query': 'SELECT top %d * FROM root r order by r.spam' % top_count # nosec } def get_order_by_key(r): @@ -186,7 +186,7 @@ def test_orderby_top_query_less_results_than_top_counts(self): # an order by query with top, total existing docs less than requested top count query = { - 'query': 'SELECT top %d * FROM root r order by r.spam' % top_count #nosec + 'query': 'SELECT top %d * FROM root r order by r.spam' % top_count # nosec } def get_order_by_key(r): @@ -226,7 +226,7 @@ def test_top_query(self): # a top query, the results will be sorted based on the target partition key range query = { - 'query': 'SELECT top %d * FROM root r' % len(expected_ordered_ids) #nosec + 'query': 'SELECT top %d * FROM root r' % len(expected_ordered_ids) # nosec } self.execute_query_and_validate_results(query, expected_ordered_ids) @@ -260,7 +260,7 @@ def test_top_query_as_string(self): expected_ordered_ids = [d['id'] for d in first_two_ranges_results] # a top query, the results will be sorted based on the target partition key range - query = 'SELECT top %d * FROM root r' % len(expected_ordered_ids) #nosec + query = 'SELECT top %d * FROM root r' % len(expected_ordered_ids) # nosec self.execute_query_and_validate_results(query, expected_ordered_ids) def test_parametrized_top_query(self): diff --git a/sdk/cosmos/azure-cosmos/test/test_query.py b/sdk/cosmos/azure-cosmos/test/test_query.py index 80cb9c3ec32c..2e1734860aea 100644 --- a/sdk/cosmos/azure-cosmos/test/test_query.py +++ b/sdk/cosmos/azure-cosmos/test/test_query.py @@ -349,55 +349,55 @@ def test_distinct(self): padded_docs = self._pad_with_none(documents, distinct_field) self._validate_distinct(created_collection=created_collection, - query='SELECT distinct c.%s from c ORDER BY c.%s' % (distinct_field, distinct_field), #nosec + query='SELECT distinct c.%s from c ORDER BY c.%s' % (distinct_field, distinct_field), # nosec results=self._get_distinct_docs(self._get_order_by_docs(padded_docs, distinct_field, None), distinct_field, None, True), is_select=False, fields=[distinct_field]) self._validate_distinct(created_collection=created_collection, - query='SELECT distinct c.%s, c.%s from c ORDER BY c.%s, c.%s' % (distinct_field, pk_field, pk_field, distinct_field), #nosec + query='SELECT distinct c.%s, c.%s from c ORDER BY c.%s, c.%s' % (distinct_field, pk_field, pk_field, distinct_field), # nosec results=self._get_distinct_docs(self._get_order_by_docs(padded_docs, pk_field, distinct_field), distinct_field, pk_field, True), is_select=False, fields=[distinct_field, pk_field]) self._validate_distinct(created_collection=created_collection, - query='SELECT distinct c.%s, c.%s from c ORDER BY c.%s, c.%s' % (distinct_field, pk_field, distinct_field, pk_field), #nosec + query='SELECT distinct c.%s, c.%s from c ORDER BY c.%s, c.%s' % (distinct_field, pk_field, distinct_field, pk_field), # nosec results=self._get_distinct_docs(self._get_order_by_docs(padded_docs, distinct_field, pk_field), distinct_field, pk_field, True), is_select=False, fields=[distinct_field, pk_field]) self._validate_distinct(created_collection=created_collection, - query='SELECT distinct value c.%s from c ORDER BY c.%s' % (distinct_field, distinct_field), #nosec + query='SELECT distinct value c.%s from c ORDER BY c.%s' % (distinct_field, distinct_field), # nosec results=self._get_distinct_docs(self._get_order_by_docs(padded_docs, distinct_field, None), distinct_field, None, True), is_select=False, fields=[distinct_field]) self._validate_distinct(created_collection=created_collection, - query='SELECT distinct c.%s from c' % (distinct_field), #nosec + query='SELECT distinct c.%s from c' % (distinct_field), # nosec results=self._get_distinct_docs(padded_docs, distinct_field, None, False), is_select=True, fields=[distinct_field]) self._validate_distinct(created_collection=created_collection, - query='SELECT distinct c.%s, c.%s from c' % (distinct_field, pk_field), #nosec + query='SELECT distinct c.%s, c.%s from c' % (distinct_field, pk_field), # nosec results=self._get_distinct_docs(padded_docs, distinct_field, pk_field, False), is_select=True, fields=[distinct_field, pk_field]) self._validate_distinct(created_collection=created_collection, - query='SELECT distinct value c.%s from c' % (distinct_field), #nosec + query='SELECT distinct value c.%s from c' % (distinct_field), # nosec results=self._get_distinct_docs(padded_docs, distinct_field, None, True), is_select=True, fields=[distinct_field]) self._validate_distinct(created_collection=created_collection, - query='SELECT distinct c.%s from c ORDER BY c.%s' % (different_field, different_field), #nosec + query='SELECT distinct c.%s from c ORDER BY c.%s' % (different_field, different_field), # nosec results=[], is_select=True, fields=[different_field]) self._validate_distinct(created_collection=created_collection, - query='SELECT distinct c.%s from c' % (different_field), #nosec + query='SELECT distinct c.%s from c' % (different_field), # nosec results=['None'], is_select=True, fields=[different_field]) diff --git a/sdk/datalake/azure-mgmt-datalake-analytics/tests/test_mgmt_datalake_analytics.py b/sdk/datalake/azure-mgmt-datalake-analytics/tests/test_mgmt_datalake_analytics.py index 812dd68c8f3b..5ceb12c2d15c 100644 --- a/sdk/datalake/azure-mgmt-datalake-analytics/tests/test_mgmt_datalake_analytics.py +++ b/sdk/datalake/azure-mgmt-datalake-analytics/tests/test_mgmt_datalake_analytics.py @@ -154,7 +154,7 @@ def setUp(self): ) AS T(a, b); -END;""".format(self.db_name, self.table_name, self.tvf_name, self.view_name, self.proc_name) #nosec +END;""".format(self.db_name, self.table_name, self.tvf_name, self.view_name, self.proc_name) # nosec # define all the job IDs to be used during execution if self.is_playback(): diff --git a/sdk/eventhub/azure-eventhub-checkpointstoreblob-aio/azure/eventhub/extensions/checkpointstoreblobaio/_vendor/storage/blob/_shared/policies.py b/sdk/eventhub/azure-eventhub-checkpointstoreblob-aio/azure/eventhub/extensions/checkpointstoreblobaio/_vendor/storage/blob/_shared/policies.py index 07331541191e..117ecd32e7d2 100644 --- a/sdk/eventhub/azure-eventhub-checkpointstoreblob-aio/azure/eventhub/extensions/checkpointstoreblobaio/_vendor/storage/blob/_shared/policies.py +++ b/sdk/eventhub/azure-eventhub-checkpointstoreblob-aio/azure/eventhub/extensions/checkpointstoreblobaio/_vendor/storage/blob/_shared/policies.py @@ -350,7 +350,7 @@ def __init__(self, **kwargs): # pylint: disable=unused-argument @staticmethod def get_content_md5(data): - md5 = hashlib.md5() #nosec + md5 = hashlib.md5() # nosec if isinstance(data, bytes): md5.update(data) elif hasattr(data, 'read'): diff --git a/sdk/eventhub/azure-eventhub-checkpointstoreblob/azure/eventhub/extensions/checkpointstoreblob/_vendor/storage/blob/_shared/policies.py b/sdk/eventhub/azure-eventhub-checkpointstoreblob/azure/eventhub/extensions/checkpointstoreblob/_vendor/storage/blob/_shared/policies.py index 07331541191e..117ecd32e7d2 100644 --- a/sdk/eventhub/azure-eventhub-checkpointstoreblob/azure/eventhub/extensions/checkpointstoreblob/_vendor/storage/blob/_shared/policies.py +++ b/sdk/eventhub/azure-eventhub-checkpointstoreblob/azure/eventhub/extensions/checkpointstoreblob/_vendor/storage/blob/_shared/policies.py @@ -350,7 +350,7 @@ def __init__(self, **kwargs): # pylint: disable=unused-argument @staticmethod def get_content_md5(data): - md5 = hashlib.md5() #nosec + md5 = hashlib.md5() # nosec if isinstance(data, bytes): md5.update(data) elif hasattr(data, 'read'): diff --git a/sdk/identity/azure-identity/tests/test_browser_credential.py b/sdk/identity/azure-identity/tests/test_browser_credential.py index e662b027d7d7..e07c69f30396 100644 --- a/sdk/identity/azure-identity/tests/test_browser_credential.py +++ b/sdk/identity/azure-identity/tests/test_browser_credential.py @@ -284,8 +284,8 @@ def test_redirect_server(): thread.start() # send a request, verify the server exposes the query - url = "http://127.0.0.1:{}/?{}={}".format(port, expected_param, expected_value) #nosec - response = urllib.request.urlopen(url) #nosec + url = "http://127.0.0.1:{}/?{}={}".format(port, expected_param, expected_value) # nosec + response = urllib.request.urlopen(url) # nosec assert response.code == 200 assert server.query_params[expected_param] == [expected_value] diff --git a/sdk/storage/azure-storage-blob/azure/storage/blob/_shared/policies.py b/sdk/storage/azure-storage-blob/azure/storage/blob/_shared/policies.py index 2ba9ea48377d..c9bc798d671a 100644 --- a/sdk/storage/azure-storage-blob/azure/storage/blob/_shared/policies.py +++ b/sdk/storage/azure-storage-blob/azure/storage/blob/_shared/policies.py @@ -322,7 +322,7 @@ def __init__(self, **kwargs): # pylint: disable=unused-argument @staticmethod def get_content_md5(data): - md5 = hashlib.md5() #nosec + md5 = hashlib.md5() # nosec if isinstance(data, bytes): md5.update(data) elif hasattr(data, 'read'): diff --git a/sdk/storage/azure-storage-blob/samples/blob_samples_client_side_encryption.py b/sdk/storage/azure-storage-blob/samples/blob_samples_client_side_encryption.py index 0d6ed51105b9..83bf2ff2e028 100644 --- a/sdk/storage/azure-storage-blob/samples/blob_samples_client_side_encryption.py +++ b/sdk/storage/azure-storage-blob/samples/blob_samples_client_side_encryption.py @@ -87,8 +87,8 @@ def wrap_key(self, key, algorithm='RSA'): if algorithm == 'RSA': return self.public_key.encrypt(key, OAEP( - mgf=MGF1(algorithm=SHA1()), #nosec - algorithm=SHA1(), #nosec + mgf=MGF1(algorithm=SHA1()), # nosec + algorithm=SHA1(), # nosec label=None) ) raise ValueError('Unknown key wrap algorithm.') @@ -97,8 +97,8 @@ def unwrap_key(self, key, algorithm): if algorithm == 'RSA': return self.private_key.decrypt(key, OAEP( - mgf=MGF1(algorithm=SHA1()), #nosec - algorithm=SHA1(), #nosec + mgf=MGF1(algorithm=SHA1()), # nosec + algorithm=SHA1(), # nosec label=None) ) raise ValueError('Unknown key wrap algorithm.') diff --git a/sdk/storage/azure-storage-blob/tests/encryption_test_helper.py b/sdk/storage/azure-storage-blob/tests/encryption_test_helper.py index 3f997142c7aa..a452a1071c87 100644 --- a/sdk/storage/azure-storage-blob/tests/encryption_test_helper.py +++ b/sdk/storage/azure-storage-blob/tests/encryption_test_helper.py @@ -65,8 +65,8 @@ def wrap_key(self, key, algorithm='RSA'): if algorithm == 'RSA': return self.public_key.encrypt(key, OAEP( - mgf=MGF1(algorithm=SHA1()), #nosec - algorithm=SHA1(), #nosec + mgf=MGF1(algorithm=SHA1()), # nosec + algorithm=SHA1(), # nosec label=None) ) @@ -76,8 +76,8 @@ def unwrap_key(self, key, algorithm): if algorithm == 'RSA': return self.private_key.decrypt(key, OAEP( - mgf=MGF1(algorithm=SHA1()), #nosec - algorithm=SHA1(), #nosec + mgf=MGF1(algorithm=SHA1()), # nosec + algorithm=SHA1(), # nosec label=None) ) diff --git a/sdk/storage/azure-storage-file-datalake/azure/storage/filedatalake/_shared/policies.py b/sdk/storage/azure-storage-file-datalake/azure/storage/filedatalake/_shared/policies.py index 2ba9ea48377d..c9bc798d671a 100644 --- a/sdk/storage/azure-storage-file-datalake/azure/storage/filedatalake/_shared/policies.py +++ b/sdk/storage/azure-storage-file-datalake/azure/storage/filedatalake/_shared/policies.py @@ -322,7 +322,7 @@ def __init__(self, **kwargs): # pylint: disable=unused-argument @staticmethod def get_content_md5(data): - md5 = hashlib.md5() #nosec + md5 = hashlib.md5() # nosec if isinstance(data, bytes): md5.update(data) elif hasattr(data, 'read'): diff --git a/sdk/storage/azure-storage-file-datalake/tests/testcase.py b/sdk/storage/azure-storage-file-datalake/tests/testcase.py index 40d6a8fe5776..2346e8cf9079 100644 --- a/sdk/storage/azure-storage-file-datalake/tests/testcase.py +++ b/sdk/storage/azure-storage-file-datalake/tests/testcase.py @@ -354,7 +354,7 @@ def _scrub(self, val): val = val.replace(old_value.encode(), new_value.encode()) elif isinstance(val, dict): val2 = str(val).replace(old_value, new_value) - val = eval(val2) #nosec + val = eval(val2) # nosec else: val = val.replace(old_value, new_value) return val diff --git a/sdk/storage/azure-storage-file-share/azure/storage/fileshare/_shared/policies.py b/sdk/storage/azure-storage-file-share/azure/storage/fileshare/_shared/policies.py index 2ba9ea48377d..c9bc798d671a 100644 --- a/sdk/storage/azure-storage-file-share/azure/storage/fileshare/_shared/policies.py +++ b/sdk/storage/azure-storage-file-share/azure/storage/fileshare/_shared/policies.py @@ -322,7 +322,7 @@ def __init__(self, **kwargs): # pylint: disable=unused-argument @staticmethod def get_content_md5(data): - md5 = hashlib.md5() #nosec + md5 = hashlib.md5() # nosec if isinstance(data, bytes): md5.update(data) elif hasattr(data, 'read'): diff --git a/sdk/storage/azure-storage-file-share/tests/encryption_test_helper.py b/sdk/storage/azure-storage-file-share/tests/encryption_test_helper.py index 3f997142c7aa..a452a1071c87 100644 --- a/sdk/storage/azure-storage-file-share/tests/encryption_test_helper.py +++ b/sdk/storage/azure-storage-file-share/tests/encryption_test_helper.py @@ -65,8 +65,8 @@ def wrap_key(self, key, algorithm='RSA'): if algorithm == 'RSA': return self.public_key.encrypt(key, OAEP( - mgf=MGF1(algorithm=SHA1()), #nosec - algorithm=SHA1(), #nosec + mgf=MGF1(algorithm=SHA1()), # nosec + algorithm=SHA1(), # nosec label=None) ) @@ -76,8 +76,8 @@ def unwrap_key(self, key, algorithm): if algorithm == 'RSA': return self.private_key.decrypt(key, OAEP( - mgf=MGF1(algorithm=SHA1()), #nosec - algorithm=SHA1(), #nosec + mgf=MGF1(algorithm=SHA1()), # nosec + algorithm=SHA1(), # nosec label=None) ) diff --git a/sdk/storage/azure-storage-queue/azure/storage/queue/_shared/policies.py b/sdk/storage/azure-storage-queue/azure/storage/queue/_shared/policies.py index 2ba9ea48377d..c9bc798d671a 100644 --- a/sdk/storage/azure-storage-queue/azure/storage/queue/_shared/policies.py +++ b/sdk/storage/azure-storage-queue/azure/storage/queue/_shared/policies.py @@ -322,7 +322,7 @@ def __init__(self, **kwargs): # pylint: disable=unused-argument @staticmethod def get_content_md5(data): - md5 = hashlib.md5() #nosec + md5 = hashlib.md5() # nosec if isinstance(data, bytes): md5.update(data) elif hasattr(data, 'read'): diff --git a/sdk/storage/azure-storage-queue/tests/encryption_test_helper.py b/sdk/storage/azure-storage-queue/tests/encryption_test_helper.py index 3f997142c7aa..a452a1071c87 100644 --- a/sdk/storage/azure-storage-queue/tests/encryption_test_helper.py +++ b/sdk/storage/azure-storage-queue/tests/encryption_test_helper.py @@ -65,8 +65,8 @@ def wrap_key(self, key, algorithm='RSA'): if algorithm == 'RSA': return self.public_key.encrypt(key, OAEP( - mgf=MGF1(algorithm=SHA1()), #nosec - algorithm=SHA1(), #nosec + mgf=MGF1(algorithm=SHA1()), # nosec + algorithm=SHA1(), # nosec label=None) ) @@ -76,8 +76,8 @@ def unwrap_key(self, key, algorithm): if algorithm == 'RSA': return self.private_key.decrypt(key, OAEP( - mgf=MGF1(algorithm=SHA1()), #nosec - algorithm=SHA1(), #nosec + mgf=MGF1(algorithm=SHA1()), # nosec + algorithm=SHA1(), # nosec label=None) ) From 010bc938120b1b3804bdade43292947be6c80d64 Mon Sep 17 00:00:00 2001 From: Krista Pratico Date: Mon, 15 Jun 2020 12:31:11 -0700 Subject: [PATCH 05/21] [formrecognizer] reduce time for recorded tests runs (#11970) * wip * wip * add transport wrapper for get clients * fix mypy * fix * refactor * pass through kwargs * add polling interval tests * feedback --- .../formrecognizer/_form_recognizer_client.py | 14 +- .../formrecognizer/_form_training_client.py | 20 +- .../azure/ai/formrecognizer/_helpers.py | 25 ++ .../aio/_form_recognizer_client_async.py | 14 +- .../aio/_form_training_client_async.py | 19 +- .../ai/formrecognizer/aio/_helpers_async.py | 31 ++ ...eceipt_from_url.test_polling_interval.yaml | 232 ++++++++++++++ ..._from_url_async.test_polling_interval.yaml | 184 +++++++++++ .../test_training.test_polling_interval.yaml | 287 ++++++++++++++++++ ..._training_async.test_polling_interval.yaml | 203 +++++++++++++ .../tests/test_content.py | 89 +++--- .../tests/test_content_async.py | 90 +++--- .../tests/test_content_from_url.py | 65 ++-- .../tests/test_content_from_url_async.py | 65 ++-- .../tests/test_copy_model.py | 19 +- .../tests/test_copy_model_async.py | 19 +- .../tests/test_custom_forms.py | 30 +- .../tests/test_custom_forms_async.py | 28 +- .../tests/test_custom_forms_from_url.py | 28 +- .../tests/test_custom_forms_from_url_async.py | 29 +- .../tests/test_mgmt.py | 30 +- .../tests/test_mgmt_async.py | 30 +- .../tests/test_receipt.py | 93 +++--- .../tests/test_receipt_async.py | 78 ++--- .../tests/test_receipt_from_url.py | 63 ++-- .../tests/test_receipt_from_url_async.py | 64 ++-- .../tests/test_training.py | 40 ++- .../tests/test_training_async.py | 41 ++- .../azure-ai-formrecognizer/tests/testcase.py | 117 ++++--- 29 files changed, 1551 insertions(+), 496 deletions(-) create mode 100644 sdk/formrecognizer/azure-ai-formrecognizer/azure/ai/formrecognizer/aio/_helpers_async.py create mode 100644 sdk/formrecognizer/azure-ai-formrecognizer/tests/recordings/test_receipt_from_url.test_polling_interval.yaml create mode 100644 sdk/formrecognizer/azure-ai-formrecognizer/tests/recordings/test_receipt_from_url_async.test_polling_interval.yaml create mode 100644 sdk/formrecognizer/azure-ai-formrecognizer/tests/recordings/test_training.test_polling_interval.yaml create mode 100644 sdk/formrecognizer/azure-ai-formrecognizer/tests/recordings/test_training_async.test_polling_interval.yaml diff --git a/sdk/formrecognizer/azure-ai-formrecognizer/azure/ai/formrecognizer/_form_recognizer_client.py b/sdk/formrecognizer/azure-ai-formrecognizer/azure/ai/formrecognizer/_form_recognizer_client.py index 315b3d328924..34889afbc6fa 100644 --- a/sdk/formrecognizer/azure-ai-formrecognizer/azure/ai/formrecognizer/_form_recognizer_client.py +++ b/sdk/formrecognizer/azure-ai-formrecognizer/azure/ai/formrecognizer/_form_recognizer_client.py @@ -66,11 +66,13 @@ def __init__(self, endpoint, credential, **kwargs): # type: (str, Union[AzureKeyCredential, TokenCredential], Any) -> None authentication_policy = get_authentication_policy(credential) + polling_interval = kwargs.pop("polling_interval", POLLING_INTERVAL) self._client = FormRecognizer( endpoint=endpoint, credential=credential, # type: ignore sdk_moniker=USER_AGENT, authentication_policy=authentication_policy, + polling_interval=polling_interval, **kwargs ) @@ -111,7 +113,7 @@ def begin_recognize_receipts(self, receipt, **kwargs): :caption: Recognize US sales receipt fields. """ - polling_interval = kwargs.pop("polling_interval", POLLING_INTERVAL) + polling_interval = kwargs.pop("polling_interval", self._client._config.polling_interval) continuation_token = kwargs.pop("continuation_token", None) content_type = kwargs.pop("content_type", None) if content_type == "application/json": @@ -162,7 +164,7 @@ def begin_recognize_receipts_from_url(self, receipt_url, **kwargs): :caption: Recognize US sales receipt fields from a URL. """ - polling_interval = kwargs.pop("polling_interval", POLLING_INTERVAL) + polling_interval = kwargs.pop("polling_interval", self._client._config.polling_interval) continuation_token = kwargs.pop("continuation_token", None) include_text_content = kwargs.pop("include_text_content", False) @@ -210,7 +212,7 @@ def begin_recognize_content(self, form, **kwargs): :caption: Recognize text and content/layout information from a form. """ - polling_interval = kwargs.pop("polling_interval", POLLING_INTERVAL) + polling_interval = kwargs.pop("polling_interval", self._client._config.polling_interval) continuation_token = kwargs.pop("continuation_token", None) content_type = kwargs.pop("content_type", None) if content_type == "application/json": @@ -246,7 +248,7 @@ def begin_recognize_content_from_url(self, form_url, **kwargs): :raises ~azure.core.exceptions.HttpResponseError: """ - polling_interval = kwargs.pop("polling_interval", POLLING_INTERVAL) + polling_interval = kwargs.pop("polling_interval", self._client._config.polling_interval) continuation_token = kwargs.pop("continuation_token", None) return self._client.begin_analyze_layout_async( @@ -296,7 +298,7 @@ def begin_recognize_custom_forms(self, model_id, form, **kwargs): raise ValueError("model_id cannot be None or empty.") cls = kwargs.pop("cls", None) - polling_interval = kwargs.pop("polling_interval", POLLING_INTERVAL) + polling_interval = kwargs.pop("polling_interval", self._client._config.polling_interval) continuation_token = kwargs.pop("continuation_token", None) content_type = kwargs.pop("content_type", None) if content_type == "application/json": @@ -348,7 +350,7 @@ def begin_recognize_custom_forms_from_url(self, model_id, form_url, **kwargs): raise ValueError("model_id cannot be None or empty.") cls = kwargs.pop("cls", None) - polling_interval = kwargs.pop("polling_interval", POLLING_INTERVAL) + polling_interval = kwargs.pop("polling_interval", self._client._config.polling_interval) continuation_token = kwargs.pop("continuation_token", None) include_text_content = kwargs.pop("include_text_content", False) diff --git a/sdk/formrecognizer/azure-ai-formrecognizer/azure/ai/formrecognizer/_form_training_client.py b/sdk/formrecognizer/azure-ai-formrecognizer/azure/ai/formrecognizer/_form_training_client.py index e83ed347d56c..acb99b178a21 100644 --- a/sdk/formrecognizer/azure-ai-formrecognizer/azure/ai/formrecognizer/_form_training_client.py +++ b/sdk/formrecognizer/azure-ai-formrecognizer/azure/ai/formrecognizer/_form_training_client.py @@ -17,6 +17,7 @@ from azure.core.tracing.decorator import distributed_trace from azure.core.polling import LROPoller from azure.core.polling.base_polling import LROBasePolling +from azure.core.pipeline import Pipeline from ._generated._form_recognizer_client import FormRecognizerClient as FormRecognizer from ._generated.models import ( TrainRequest, @@ -26,7 +27,7 @@ CopyOperationResult, CopyAuthorizationResult ) -from ._helpers import error_map, get_authentication_policy, POLLING_INTERVAL +from ._helpers import error_map, get_authentication_policy, POLLING_INTERVAL, TransportWrapper from ._models import ( CustomFormModelInfo, AccountProperties, @@ -78,11 +79,13 @@ def __init__(self, endpoint, credential, **kwargs): self._endpoint = endpoint self._credential = credential authentication_policy = get_authentication_policy(credential) + polling_interval = kwargs.pop("polling_interval", POLLING_INTERVAL) self._client = FormRecognizer( endpoint=self._endpoint, credential=self._credential, # type: ignore sdk_moniker=USER_AGENT, authentication_policy=authentication_policy, + polling_interval=polling_interval, **kwargs ) @@ -129,7 +132,7 @@ def callback(raw_response): cls = kwargs.pop("cls", None) continuation_token = kwargs.pop("continuation_token", None) - polling_interval = kwargs.pop("polling_interval", POLLING_INTERVAL) + polling_interval = kwargs.pop("polling_interval", self._client._config.polling_interval) deserialization_callback = cls if cls else callback if continuation_token: @@ -339,7 +342,7 @@ def begin_copy_model( if not model_id: raise ValueError("model_id cannot be None or empty.") - polling_interval = kwargs.pop("polling_interval", POLLING_INTERVAL) + polling_interval = kwargs.pop("polling_interval", self._client._config.polling_interval) continuation_token = kwargs.pop("continuation_token", None) def _copy_callback(raw_response, _, headers): # pylint: disable=unused-argument @@ -371,11 +374,20 @@ def get_form_recognizer_client(self, **kwargs): :rtype: ~azure.ai.formrecognizer.FormRecognizerClient :return: A FormRecognizerClient """ - return FormRecognizerClient( + + _pipeline = Pipeline( + transport=TransportWrapper(self._client._client._pipeline._transport), + policies=self._client._client._pipeline._impl_policies + ) # type: Pipeline + client = FormRecognizerClient( endpoint=self._endpoint, credential=self._credential, + pipeline=_pipeline, **kwargs ) + # need to share config, but can't pass as a keyword into client + client._client._config = self._client._client._config + return client def close(self): # type: () -> None diff --git a/sdk/formrecognizer/azure-ai-formrecognizer/azure/ai/formrecognizer/_helpers.py b/sdk/formrecognizer/azure-ai-formrecognizer/azure/ai/formrecognizer/_helpers.py index 38e5fd0ae13b..f474f894918c 100644 --- a/sdk/formrecognizer/azure-ai-formrecognizer/azure/ai/formrecognizer/_helpers.py +++ b/sdk/formrecognizer/azure-ai-formrecognizer/azure/ai/formrecognizer/_helpers.py @@ -7,6 +7,7 @@ import six from azure.core.credentials import AzureKeyCredential from azure.core.pipeline.policies import AzureKeyCredentialPolicy +from azure.core.pipeline.transport import HttpTransport from azure.core.exceptions import ( ResourceNotFoundError, ResourceExistsError, @@ -24,6 +25,30 @@ } +class TransportWrapper(HttpTransport): + """Wrapper class that ensures that an inner client created + by a `get_client` method does not close the outer transport for the parent + when used in a context manager. + """ + def __init__(self, transport): + self._transport = transport + + def send(self, request, **kwargs): + return self._transport.send(request, **kwargs) + + def open(self): + pass + + def close(self): + pass + + def __enter__(self): + pass + + def __exit__(self, *args): # pylint: disable=arguments-differ + pass + + def get_authentication_policy(credential): authentication_policy = None if credential is None: diff --git a/sdk/formrecognizer/azure-ai-formrecognizer/azure/ai/formrecognizer/aio/_form_recognizer_client_async.py b/sdk/formrecognizer/azure-ai-formrecognizer/azure/ai/formrecognizer/aio/_form_recognizer_client_async.py index 1f5c0d19d43b..c6f75d43d5db 100644 --- a/sdk/formrecognizer/azure-ai-formrecognizer/azure/ai/formrecognizer/aio/_form_recognizer_client_async.py +++ b/sdk/formrecognizer/azure-ai-formrecognizer/azure/ai/formrecognizer/aio/_form_recognizer_client_async.py @@ -71,11 +71,13 @@ def __init__( ) -> None: authentication_policy = get_authentication_policy(credential) + polling_interval = kwargs.pop("polling_interval", POLLING_INTERVAL) self._client = FormRecognizer( endpoint=endpoint, credential=credential, # type: ignore sdk_moniker=USER_AGENT, authentication_policy=authentication_policy, + polling_interval=polling_interval, **kwargs ) @@ -119,7 +121,7 @@ async def begin_recognize_receipts( :caption: Recognize US sales receipt fields. """ - polling_interval = kwargs.pop("polling_interval", POLLING_INTERVAL) + polling_interval = kwargs.pop("polling_interval", self._client._config.polling_interval) continuation_token = kwargs.pop("continuation_token", None) content_type = kwargs.pop("content_type", None) if content_type == "application/json": @@ -176,7 +178,7 @@ async def begin_recognize_receipts_from_url( :caption: Recognize US sales receipt fields from a URL. """ - polling_interval = kwargs.pop("polling_interval", POLLING_INTERVAL) + polling_interval = kwargs.pop("polling_interval", self._client._config.polling_interval) continuation_token = kwargs.pop("continuation_token", None) include_text_content = kwargs.pop("include_text_content", False) @@ -230,7 +232,7 @@ async def begin_recognize_content( :caption: Recognize text and content/layout information from a form. """ - polling_interval = kwargs.pop("polling_interval", POLLING_INTERVAL) + polling_interval = kwargs.pop("polling_interval", self._client._config.polling_interval) continuation_token = kwargs.pop("continuation_token", None) content_type = kwargs.pop("content_type", None) if content_type == "application/json": @@ -268,7 +270,7 @@ async def begin_recognize_content_from_url(self, form_url: str, **kwargs: Any) - :raises ~azure.core.exceptions.HttpResponseError: """ - polling_interval = kwargs.pop("polling_interval", POLLING_INTERVAL) + polling_interval = kwargs.pop("polling_interval", self._client._config.polling_interval) continuation_token = kwargs.pop("continuation_token", None) return await self._client.begin_analyze_layout_async( # type: ignore file_stream={"source": form_url}, @@ -324,7 +326,7 @@ async def begin_recognize_custom_forms( raise ValueError("model_id cannot be None or empty.") cls = kwargs.pop("cls", None) - polling_interval = kwargs.pop("polling_interval", POLLING_INTERVAL) + polling_interval = kwargs.pop("polling_interval", self._client._config.polling_interval) continuation_token = kwargs.pop("continuation_token", None) content_type = kwargs.pop("content_type", None) if content_type == "application/json": @@ -385,7 +387,7 @@ async def begin_recognize_custom_forms_from_url( raise ValueError("model_id cannot be None or empty.") cls = kwargs.pop("cls", None) - polling_interval = kwargs.pop("polling_interval", POLLING_INTERVAL) + polling_interval = kwargs.pop("polling_interval", self._client._config.polling_interval) continuation_token = kwargs.pop("continuation_token", None) include_text_content = kwargs.pop("include_text_content", False) diff --git a/sdk/formrecognizer/azure-ai-formrecognizer/azure/ai/formrecognizer/aio/_form_training_client_async.py b/sdk/formrecognizer/azure-ai-formrecognizer/azure/ai/formrecognizer/aio/_form_training_client_async.py index e4ebde191e69..8dd09b6db226 100644 --- a/sdk/formrecognizer/azure-ai-formrecognizer/azure/ai/formrecognizer/aio/_form_training_client_async.py +++ b/sdk/formrecognizer/azure-ai-formrecognizer/azure/ai/formrecognizer/aio/_form_training_client_async.py @@ -15,10 +15,12 @@ TYPE_CHECKING, ) from azure.core.polling import AsyncLROPoller +from azure.core.pipeline import AsyncPipeline from azure.core.polling.async_base_polling import AsyncLROBasePolling from azure.core.tracing.decorator import distributed_trace from azure.core.tracing.decorator_async import distributed_trace_async from ._form_recognizer_client_async import FormRecognizerClient +from ._helpers_async import AsyncTransportWrapper from .._generated.aio._form_recognizer_client_async import FormRecognizerClient as FormRecognizer from .._generated.models import ( TrainRequest, @@ -81,13 +83,14 @@ def __init__( ) -> None: self._endpoint = endpoint self._credential = credential - authentication_policy = get_authentication_policy(credential) + polling_interval = kwargs.pop("polling_interval", POLLING_INTERVAL) self._client = FormRecognizer( endpoint=self._endpoint, credential=self._credential, # type: ignore sdk_moniker=USER_AGENT, authentication_policy=authentication_policy, + polling_interval=polling_interval, **kwargs ) @@ -138,7 +141,7 @@ def callback(raw_response): cls = kwargs.pop("cls", None) continuation_token = kwargs.pop("continuation_token", None) - polling_interval = kwargs.pop("polling_interval", POLLING_INTERVAL) + polling_interval = kwargs.pop("polling_interval", self._client._config.polling_interval) deserialization_callback = cls if cls else callback if continuation_token: @@ -361,7 +364,7 @@ async def begin_copy_model( raise ValueError("model_id cannot be None or empty.") continuation_token = kwargs.pop("continuation_token", None) - polling_interval = kwargs.pop("polling_interval", POLLING_INTERVAL) + polling_interval = kwargs.pop("polling_interval", self._client._config.polling_interval) def _copy_callback(raw_response, _, headers): # pylint: disable=unused-argument copy_result = self._client._deserialize(CopyOperationResult, raw_response) @@ -395,11 +398,19 @@ def get_form_recognizer_client(self, **kwargs: Any) -> FormRecognizerClient: :rtype: ~azure.ai.formrecognizer.aio.FormRecognizerClient :return: A FormRecognizerClient """ - return FormRecognizerClient( + _pipeline = AsyncPipeline( + transport=AsyncTransportWrapper(self._client._client._pipeline._transport), + policies=self._client._client._pipeline._impl_policies + ) # type: AsyncPipeline + client = FormRecognizerClient( endpoint=self._endpoint, credential=self._credential, + pipeline=_pipeline, **kwargs ) + # need to share config, but can't pass as a keyword into client + client._client._config = self._client._client._config + return client async def __aenter__(self) -> "FormTrainingClient": await self._client.__aenter__() diff --git a/sdk/formrecognizer/azure-ai-formrecognizer/azure/ai/formrecognizer/aio/_helpers_async.py b/sdk/formrecognizer/azure-ai-formrecognizer/azure/ai/formrecognizer/aio/_helpers_async.py new file mode 100644 index 000000000000..f95037f25144 --- /dev/null +++ b/sdk/formrecognizer/azure-ai-formrecognizer/azure/ai/formrecognizer/aio/_helpers_async.py @@ -0,0 +1,31 @@ +# coding=utf-8 +# ------------------------------------ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT License. +# ------------------------------------ + +from azure.core.pipeline.transport import AsyncHttpTransport + + +class AsyncTransportWrapper(AsyncHttpTransport): + """Wrapper class that ensures that an inner client created + by a `get_client` method does not close the outer transport for the parent + when used in a context manager. + """ + def __init__(self, async_transport): + self._transport = async_transport + + async def send(self, request, **kwargs): + return await self._transport.send(request, **kwargs) + + async def open(self): + pass + + async def close(self): + pass + + async def __aenter__(self): + pass + + async def __aexit__(self, *args): # pylint: disable=arguments-differ + pass diff --git a/sdk/formrecognizer/azure-ai-formrecognizer/tests/recordings/test_receipt_from_url.test_polling_interval.yaml b/sdk/formrecognizer/azure-ai-formrecognizer/tests/recordings/test_receipt_from_url.test_polling_interval.yaml new file mode 100644 index 000000000000..65fdfcc1302f --- /dev/null +++ b/sdk/formrecognizer/azure-ai-formrecognizer/tests/recordings/test_receipt_from_url.test_polling_interval.yaml @@ -0,0 +1,232 @@ +interactions: +- request: + body: 'b''{"source": "https://raw.githubusercontent.com/Azure/azure-sdk-for-python/master/sdk/formrecognizer/azure-ai-formrecognizer/tests/sample_forms/receipt/contoso-allinone.jpg"}''' + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + Content-Length: + - '172' + Content-Type: + - application/json + User-Agent: + - azsdk-python-ai-formrecognizer/1.0.0b4 Python/3.7.3 (Windows-10-10.0.18362-SP0) + Python/3.7.3 (Windows-10-10.0.18362-SP0) + method: POST + uri: https://centraluseuap.api.cognitive.microsoft.com/formrecognizer/v2.0-preview/prebuilt/receipt/analyze?includeTextDetails=false + response: + body: + string: '' + headers: + apim-request-id: + - a710a6a5-4a54-46a1-9e88-f2a0b555765b + content-length: + - '0' + date: + - Fri, 12 Jun 2020 16:50:56 GMT + operation-location: + - https://centraluseuap.api.cognitive.microsoft.com/formrecognizer/v2.0-preview/prebuilt/receipt/analyzeResults/a710a6a5-4a54-46a1-9e88-f2a0b555765b + strict-transport-security: + - max-age=31536000; includeSubDomains; preload + x-content-type-options: + - nosniff + x-envoy-upstream-service-time: + - '1025' + status: + code: 202 + message: Accepted +- request: + body: null + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + User-Agent: + - azsdk-python-ai-formrecognizer/1.0.0b4 Python/3.7.3 (Windows-10-10.0.18362-SP0) + Python/3.7.3 (Windows-10-10.0.18362-SP0) + method: GET + uri: https://centraluseuap.api.cognitive.microsoft.com/formrecognizer/v2.0-preview/prebuilt/receipt/analyzeResults/a710a6a5-4a54-46a1-9e88-f2a0b555765b + response: + body: + string: '{"status": "succeeded", "createdDateTime": "2020-06-12T16:50:56Z", + "lastUpdatedDateTime": "2020-06-12T16:50:58Z", "analyzeResult": {"version": + "2.0.0", "readResults": [{"page": 1, "angle": 0.6893, "width": 1688, "height": + 3000, "unit": "pixel", "language": "en"}], "documentResults": [{"docType": + "prebuilt:receipt", "pageRange": [1, 1], "fields": {"ReceiptType": {"type": + "string", "valueString": "Itemized", "confidence": 0.692}, "MerchantName": + {"type": "string", "valueString": "Contoso Contoso", "text": "Contoso Contoso", + "boundingBox": [378.2, 292.4, 1117.7, 468.3, 1035.7, 812.7, 296.3, 636.8], + "page": 1, "confidence": 0.613}, "MerchantAddress": {"type": "string", "valueString": + "123 Main Street Redmond, WA 98052", "text": "123 Main Street Redmond, WA + 98052", "boundingBox": [302, 675.8, 848.1, 793.7, 809.9, 970.4, 263.9, 852.5], + "page": 1, "confidence": 0.99}, "MerchantPhoneNumber": {"type": "phoneNumber", + "valuePhoneNumber": "+19876543210", "text": "987-654-3210", "boundingBox": + [278, 1004, 656.3, 1054.7, 646.8, 1125.3, 268.5, 1074.7], "page": 1, "confidence": + 0.99}, "TransactionDate": {"type": "date", "valueDate": "2019-06-10", "text": + "6/10/2019", "boundingBox": [265.1, 1228.4, 525, 1247, 518.9, 1332.1, 259, + 1313.5], "page": 1, "confidence": 0.99}, "TransactionTime": {"type": "time", + "valueTime": "13:59:00", "text": "13:59", "boundingBox": [541, 1248, 677.3, + 1261.5, 668.9, 1346.5, 532.6, 1333], "page": 1, "confidence": 0.977}, "Items": + {"type": "array", "valueArray": [{"type": "object", "valueObject": {"Quantity": + {"type": "number", "text": "1", "boundingBox": [245.1, 1581.5, 300.9, 1585.1, + 295, 1676, 239.2, 1672.4], "page": 1, "confidence": 0.92}, "Name": {"type": + "string", "valueString": "Cappuccino", "text": "Cappuccino", "boundingBox": + [322, 1586, 654.2, 1601.1, 650, 1693, 317.8, 1678], "page": 1, "confidence": + 0.923}, "TotalPrice": {"type": "number", "valueNumber": 2.2, "text": "$2.20", + "boundingBox": [1107.7, 1584, 1263, 1574, 1268.3, 1656, 1113, 1666], "page": + 1, "confidence": 0.918}}}, {"type": "object", "valueObject": {"Quantity": + {"type": "number", "text": "1", "boundingBox": [232, 1834, 286.6, 1835, 285, + 1921, 230.4, 1920], "page": 1, "confidence": 0.858}, "Name": {"type": "string", + "valueString": "BACON & EGGS", "text": "BACON & EGGS", "boundingBox": [308, + 1836, 746, 1841.4, 745, 1925.4, 307, 1920], "page": 1, "confidence": 0.916}, + "TotalPrice": {"type": "number", "text": "$9.5", "boundingBox": [1133.9, 1955, + 1257, 1952, 1259.1, 2036, 1136, 2039], "page": 1, "confidence": 0.916}}}]}, + "Subtotal": {"type": "number", "valueNumber": 11.7, "text": "11.70", "boundingBox": + [1146, 2221, 1297.3, 2223, 1296, 2319, 1144.7, 2317], "page": 1, "confidence": + 0.955}, "Tax": {"type": "number", "valueNumber": 1.17, "text": "1.17", "boundingBox": + [1190, 2359, 1304, 2359, 1304, 2456, 1190, 2456], "page": 1, "confidence": + 0.979}, "Tip": {"type": "number", "valueNumber": 1.63, "text": "1.63", "boundingBox": + [1094, 2479, 1267.7, 2485, 1264, 2591, 1090.3, 2585], "page": 1, "confidence": + 0.941}, "Total": {"type": "number", "valueNumber": 14.5, "text": "$14.50", + "boundingBox": [1034.2, 2617, 1387.5, 2638.2, 1380, 2763, 1026.7, 2741.8], + "page": 1, "confidence": 0.985}}}]}}' + headers: + apim-request-id: + - 8d2cb919-ee61-49dd-92d5-899ad45eb4f0 + content-type: + - application/json; charset=utf-8 + date: + - Fri, 12 Jun 2020 16:51:02 GMT + strict-transport-security: + - max-age=31536000; includeSubDomains; preload + transfer-encoding: + - chunked + x-content-type-options: + - nosniff + x-envoy-upstream-service-time: + - '71' + status: + code: 200 + message: OK +- request: + body: 'b''{"source": "https://raw.githubusercontent.com/Azure/azure-sdk-for-python/master/sdk/formrecognizer/azure-ai-formrecognizer/tests/sample_forms/receipt/contoso-allinone.jpg"}''' + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + Content-Length: + - '172' + Content-Type: + - application/json + User-Agent: + - azsdk-python-ai-formrecognizer/1.0.0b4 Python/3.7.3 (Windows-10-10.0.18362-SP0) + Python/3.7.3 (Windows-10-10.0.18362-SP0) + method: POST + uri: https://centraluseuap.api.cognitive.microsoft.com/formrecognizer/v2.0-preview/prebuilt/receipt/analyze?includeTextDetails=false + response: + body: + string: '' + headers: + apim-request-id: + - c824e710-1289-403a-bcc9-1ecc5970fd69 + content-length: + - '0' + date: + - Fri, 12 Jun 2020 16:51:03 GMT + operation-location: + - https://centraluseuap.api.cognitive.microsoft.com/formrecognizer/v2.0-preview/prebuilt/receipt/analyzeResults/c824e710-1289-403a-bcc9-1ecc5970fd69 + strict-transport-security: + - max-age=31536000; includeSubDomains; preload + x-content-type-options: + - nosniff + x-envoy-upstream-service-time: + - '274' + status: + code: 202 + message: Accepted +- request: + body: null + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + User-Agent: + - azsdk-python-ai-formrecognizer/1.0.0b4 Python/3.7.3 (Windows-10-10.0.18362-SP0) + Python/3.7.3 (Windows-10-10.0.18362-SP0) + method: GET + uri: https://centraluseuap.api.cognitive.microsoft.com/formrecognizer/v2.0-preview/prebuilt/receipt/analyzeResults/c824e710-1289-403a-bcc9-1ecc5970fd69 + response: + body: + string: '{"status": "succeeded", "createdDateTime": "2020-06-12T16:51:03Z", + "lastUpdatedDateTime": "2020-06-12T16:51:04Z", "analyzeResult": {"version": + "2.0.0", "readResults": [{"page": 1, "angle": 0.6893, "width": 1688, "height": + 3000, "unit": "pixel", "language": "en"}], "documentResults": [{"docType": + "prebuilt:receipt", "pageRange": [1, 1], "fields": {"ReceiptType": {"type": + "string", "valueString": "Itemized", "confidence": 0.692}, "MerchantName": + {"type": "string", "valueString": "Contoso Contoso", "text": "Contoso Contoso", + "boundingBox": [378.2, 292.4, 1117.7, 468.3, 1035.7, 812.7, 296.3, 636.8], + "page": 1, "confidence": 0.613}, "MerchantAddress": {"type": "string", "valueString": + "123 Main Street Redmond, WA 98052", "text": "123 Main Street Redmond, WA + 98052", "boundingBox": [302, 675.8, 848.1, 793.7, 809.9, 970.4, 263.9, 852.5], + "page": 1, "confidence": 0.99}, "MerchantPhoneNumber": {"type": "phoneNumber", + "valuePhoneNumber": "+19876543210", "text": "987-654-3210", "boundingBox": + [278, 1004, 656.3, 1054.7, 646.8, 1125.3, 268.5, 1074.7], "page": 1, "confidence": + 0.99}, "TransactionDate": {"type": "date", "valueDate": "2019-06-10", "text": + "6/10/2019", "boundingBox": [265.1, 1228.4, 525, 1247, 518.9, 1332.1, 259, + 1313.5], "page": 1, "confidence": 0.99}, "TransactionTime": {"type": "time", + "valueTime": "13:59:00", "text": "13:59", "boundingBox": [541, 1248, 677.3, + 1261.5, 668.9, 1346.5, 532.6, 1333], "page": 1, "confidence": 0.977}, "Items": + {"type": "array", "valueArray": [{"type": "object", "valueObject": {"Quantity": + {"type": "number", "text": "1", "boundingBox": [245.1, 1581.5, 300.9, 1585.1, + 295, 1676, 239.2, 1672.4], "page": 1, "confidence": 0.92}, "Name": {"type": + "string", "valueString": "Cappuccino", "text": "Cappuccino", "boundingBox": + [322, 1586, 654.2, 1601.1, 650, 1693, 317.8, 1678], "page": 1, "confidence": + 0.923}, "TotalPrice": {"type": "number", "valueNumber": 2.2, "text": "$2.20", + "boundingBox": [1107.7, 1584, 1263, 1574, 1268.3, 1656, 1113, 1666], "page": + 1, "confidence": 0.918}}}, {"type": "object", "valueObject": {"Quantity": + {"type": "number", "text": "1", "boundingBox": [232, 1834, 286.6, 1835, 285, + 1921, 230.4, 1920], "page": 1, "confidence": 0.858}, "Name": {"type": "string", + "valueString": "BACON & EGGS", "text": "BACON & EGGS", "boundingBox": [308, + 1836, 746, 1841.4, 745, 1925.4, 307, 1920], "page": 1, "confidence": 0.916}, + "TotalPrice": {"type": "number", "text": "$9.5", "boundingBox": [1133.9, 1955, + 1257, 1952, 1259.1, 2036, 1136, 2039], "page": 1, "confidence": 0.916}}}]}, + "Subtotal": {"type": "number", "valueNumber": 11.7, "text": "11.70", "boundingBox": + [1146, 2221, 1297.3, 2223, 1296, 2319, 1144.7, 2317], "page": 1, "confidence": + 0.955}, "Tax": {"type": "number", "valueNumber": 1.17, "text": "1.17", "boundingBox": + [1190, 2359, 1304, 2359, 1304, 2456, 1190, 2456], "page": 1, "confidence": + 0.979}, "Tip": {"type": "number", "valueNumber": 1.63, "text": "1.63", "boundingBox": + [1094, 2479, 1267.7, 2485, 1264, 2591, 1090.3, 2585], "page": 1, "confidence": + 0.941}, "Total": {"type": "number", "valueNumber": 14.5, "text": "$14.50", + "boundingBox": [1034.2, 2617, 1387.5, 2638.2, 1380, 2763, 1026.7, 2741.8], + "page": 1, "confidence": 0.985}}}]}}' + headers: + apim-request-id: + - 9c817312-1988-4d3d-82ea-cf8a7a90f683 + content-type: + - application/json; charset=utf-8 + date: + - Fri, 12 Jun 2020 16:51:09 GMT + strict-transport-security: + - max-age=31536000; includeSubDomains; preload + transfer-encoding: + - chunked + x-content-type-options: + - nosniff + x-envoy-upstream-service-time: + - '23' + status: + code: 200 + message: OK +version: 1 diff --git a/sdk/formrecognizer/azure-ai-formrecognizer/tests/recordings/test_receipt_from_url_async.test_polling_interval.yaml b/sdk/formrecognizer/azure-ai-formrecognizer/tests/recordings/test_receipt_from_url_async.test_polling_interval.yaml new file mode 100644 index 000000000000..dc830a4c8ab7 --- /dev/null +++ b/sdk/formrecognizer/azure-ai-formrecognizer/tests/recordings/test_receipt_from_url_async.test_polling_interval.yaml @@ -0,0 +1,184 @@ +interactions: +- request: + body: 'b''{"source": "https://raw.githubusercontent.com/Azure/azure-sdk-for-python/master/sdk/formrecognizer/azure-ai-formrecognizer/tests/sample_forms/receipt/contoso-allinone.jpg"}''' + headers: + Content-Length: + - '172' + Content-Type: + - application/json + User-Agent: + - azsdk-python-ai-formrecognizer/1.0.0b4 Python/3.7.3 (Windows-10-10.0.18362-SP0) + Python/3.7.3 (Windows-10-10.0.18362-SP0) + method: POST + uri: https://centraluseuap.api.cognitive.microsoft.com/formrecognizer/v2.0-preview/prebuilt/receipt/analyze?includeTextDetails=false + response: + body: + string: '' + headers: + apim-request-id: daea113a-952a-44c6-b9ff-22690d2f8c46 + content-length: '0' + date: Fri, 12 Jun 2020 17:00:04 GMT + operation-location: https://centraluseuap.api.cognitive.microsoft.com/formrecognizer/v2.0-preview/prebuilt/receipt/analyzeResults/daea113a-952a-44c6-b9ff-22690d2f8c46 + strict-transport-security: max-age=31536000; includeSubDomains; preload + x-content-type-options: nosniff + x-envoy-upstream-service-time: '488' + status: + code: 202 + message: Accepted + url: https://centraluseuap.api.cognitive.microsoft.com//formrecognizer/v2.0-preview/prebuilt/receipt/analyze?includeTextDetails=false +- request: + body: null + headers: + User-Agent: + - azsdk-python-ai-formrecognizer/1.0.0b4 Python/3.7.3 (Windows-10-10.0.18362-SP0) + Python/3.7.3 (Windows-10-10.0.18362-SP0) + method: GET + uri: https://centraluseuap.api.cognitive.microsoft.com/formrecognizer/v2.0-preview/prebuilt/receipt/analyzeResults/daea113a-952a-44c6-b9ff-22690d2f8c46 + response: + body: + string: '{"status": "succeeded", "createdDateTime": "2020-06-12T17:00:04Z", + "lastUpdatedDateTime": "2020-06-12T17:00:06Z", "analyzeResult": {"version": + "2.0.0", "readResults": [{"page": 1, "angle": 0.6893, "width": 1688, "height": + 3000, "unit": "pixel", "language": "en"}], "documentResults": [{"docType": + "prebuilt:receipt", "pageRange": [1, 1], "fields": {"ReceiptType": {"type": + "string", "valueString": "Itemized", "confidence": 0.692}, "MerchantName": + {"type": "string", "valueString": "Contoso Contoso", "text": "Contoso Contoso", + "boundingBox": [378.2, 292.4, 1117.7, 468.3, 1035.7, 812.7, 296.3, 636.8], + "page": 1, "confidence": 0.613}, "MerchantAddress": {"type": "string", "valueString": + "123 Main Street Redmond, WA 98052", "text": "123 Main Street Redmond, WA + 98052", "boundingBox": [302, 675.8, 848.1, 793.7, 809.9, 970.4, 263.9, 852.5], + "page": 1, "confidence": 0.99}, "MerchantPhoneNumber": {"type": "phoneNumber", + "valuePhoneNumber": "+19876543210", "text": "987-654-3210", "boundingBox": + [278, 1004, 656.3, 1054.7, 646.8, 1125.3, 268.5, 1074.7], "page": 1, "confidence": + 0.99}, "TransactionDate": {"type": "date", "valueDate": "2019-06-10", "text": + "6/10/2019", "boundingBox": [265.1, 1228.4, 525, 1247, 518.9, 1332.1, 259, + 1313.5], "page": 1, "confidence": 0.99}, "TransactionTime": {"type": "time", + "valueTime": "13:59:00", "text": "13:59", "boundingBox": [541, 1248, 677.3, + 1261.5, 668.9, 1346.5, 532.6, 1333], "page": 1, "confidence": 0.977}, "Items": + {"type": "array", "valueArray": [{"type": "object", "valueObject": {"Quantity": + {"type": "number", "text": "1", "boundingBox": [245.1, 1581.5, 300.9, 1585.1, + 295, 1676, 239.2, 1672.4], "page": 1, "confidence": 0.92}, "Name": {"type": + "string", "valueString": "Cappuccino", "text": "Cappuccino", "boundingBox": + [322, 1586, 654.2, 1601.1, 650, 1693, 317.8, 1678], "page": 1, "confidence": + 0.923}, "TotalPrice": {"type": "number", "valueNumber": 2.2, "text": "$2.20", + "boundingBox": [1107.7, 1584, 1263, 1574, 1268.3, 1656, 1113, 1666], "page": + 1, "confidence": 0.918}}}, {"type": "object", "valueObject": {"Quantity": + {"type": "number", "text": "1", "boundingBox": [232, 1834, 286.6, 1835, 285, + 1921, 230.4, 1920], "page": 1, "confidence": 0.858}, "Name": {"type": "string", + "valueString": "BACON & EGGS", "text": "BACON & EGGS", "boundingBox": [308, + 1836, 746, 1841.4, 745, 1925.4, 307, 1920], "page": 1, "confidence": 0.916}, + "TotalPrice": {"type": "number", "text": "$9.5", "boundingBox": [1133.9, 1955, + 1257, 1952, 1259.1, 2036, 1136, 2039], "page": 1, "confidence": 0.916}}}]}, + "Subtotal": {"type": "number", "valueNumber": 11.7, "text": "11.70", "boundingBox": + [1146, 2221, 1297.3, 2223, 1296, 2319, 1144.7, 2317], "page": 1, "confidence": + 0.955}, "Tax": {"type": "number", "valueNumber": 1.17, "text": "1.17", "boundingBox": + [1190, 2359, 1304, 2359, 1304, 2456, 1190, 2456], "page": 1, "confidence": + 0.979}, "Tip": {"type": "number", "valueNumber": 1.63, "text": "1.63", "boundingBox": + [1094, 2479, 1267.7, 2485, 1264, 2591, 1090.3, 2585], "page": 1, "confidence": + 0.941}, "Total": {"type": "number", "valueNumber": 14.5, "text": "$14.50", + "boundingBox": [1034.2, 2617, 1387.5, 2638.2, 1380, 2763, 1026.7, 2741.8], + "page": 1, "confidence": 0.985}}}]}}' + headers: + apim-request-id: d2b56931-1870-4c76-ae23-89ebe5c56143 + content-type: application/json; charset=utf-8 + date: Fri, 12 Jun 2020 17:00:09 GMT + strict-transport-security: max-age=31536000; includeSubDomains; preload + transfer-encoding: chunked + x-content-type-options: nosniff + x-envoy-upstream-service-time: '38' + status: + code: 200 + message: OK + url: https://centraluseuap.api.cognitive.microsoft.com/formrecognizer/v2.0-preview/prebuilt/receipt/analyzeResults/daea113a-952a-44c6-b9ff-22690d2f8c46 +- request: + body: 'b''{"source": "https://raw.githubusercontent.com/Azure/azure-sdk-for-python/master/sdk/formrecognizer/azure-ai-formrecognizer/tests/sample_forms/receipt/contoso-allinone.jpg"}''' + headers: + Content-Length: + - '172' + Content-Type: + - application/json + User-Agent: + - azsdk-python-ai-formrecognizer/1.0.0b4 Python/3.7.3 (Windows-10-10.0.18362-SP0) + Python/3.7.3 (Windows-10-10.0.18362-SP0) + method: POST + uri: https://centraluseuap.api.cognitive.microsoft.com/formrecognizer/v2.0-preview/prebuilt/receipt/analyze?includeTextDetails=false + response: + body: + string: '' + headers: + apim-request-id: aefd083e-3ba3-4f84-beb5-719d583936d0 + content-length: '0' + date: Fri, 12 Jun 2020 17:00:10 GMT + operation-location: https://centraluseuap.api.cognitive.microsoft.com/formrecognizer/v2.0-preview/prebuilt/receipt/analyzeResults/aefd083e-3ba3-4f84-beb5-719d583936d0 + strict-transport-security: max-age=31536000; includeSubDomains; preload + x-content-type-options: nosniff + x-envoy-upstream-service-time: '336' + status: + code: 202 + message: Accepted + url: https://centraluseuap.api.cognitive.microsoft.com//formrecognizer/v2.0-preview/prebuilt/receipt/analyze?includeTextDetails=false +- request: + body: null + headers: + User-Agent: + - azsdk-python-ai-formrecognizer/1.0.0b4 Python/3.7.3 (Windows-10-10.0.18362-SP0) + Python/3.7.3 (Windows-10-10.0.18362-SP0) + method: GET + uri: https://centraluseuap.api.cognitive.microsoft.com/formrecognizer/v2.0-preview/prebuilt/receipt/analyzeResults/aefd083e-3ba3-4f84-beb5-719d583936d0 + response: + body: + string: '{"status": "succeeded", "createdDateTime": "2020-06-12T17:00:10Z", + "lastUpdatedDateTime": "2020-06-12T17:00:13Z", "analyzeResult": {"version": + "2.0.0", "readResults": [{"page": 1, "angle": 0.6893, "width": 1688, "height": + 3000, "unit": "pixel", "language": "en"}], "documentResults": [{"docType": + "prebuilt:receipt", "pageRange": [1, 1], "fields": {"ReceiptType": {"type": + "string", "valueString": "Itemized", "confidence": 0.692}, "MerchantName": + {"type": "string", "valueString": "Contoso Contoso", "text": "Contoso Contoso", + "boundingBox": [378.2, 292.4, 1117.7, 468.3, 1035.7, 812.7, 296.3, 636.8], + "page": 1, "confidence": 0.613}, "MerchantAddress": {"type": "string", "valueString": + "123 Main Street Redmond, WA 98052", "text": "123 Main Street Redmond, WA + 98052", "boundingBox": [302, 675.8, 848.1, 793.7, 809.9, 970.4, 263.9, 852.5], + "page": 1, "confidence": 0.99}, "MerchantPhoneNumber": {"type": "phoneNumber", + "valuePhoneNumber": "+19876543210", "text": "987-654-3210", "boundingBox": + [278, 1004, 656.3, 1054.7, 646.8, 1125.3, 268.5, 1074.7], "page": 1, "confidence": + 0.99}, "TransactionDate": {"type": "date", "valueDate": "2019-06-10", "text": + "6/10/2019", "boundingBox": [265.1, 1228.4, 525, 1247, 518.9, 1332.1, 259, + 1313.5], "page": 1, "confidence": 0.99}, "TransactionTime": {"type": "time", + "valueTime": "13:59:00", "text": "13:59", "boundingBox": [541, 1248, 677.3, + 1261.5, 668.9, 1346.5, 532.6, 1333], "page": 1, "confidence": 0.977}, "Items": + {"type": "array", "valueArray": [{"type": "object", "valueObject": {"Quantity": + {"type": "number", "text": "1", "boundingBox": [245.1, 1581.5, 300.9, 1585.1, + 295, 1676, 239.2, 1672.4], "page": 1, "confidence": 0.92}, "Name": {"type": + "string", "valueString": "Cappuccino", "text": "Cappuccino", "boundingBox": + [322, 1586, 654.2, 1601.1, 650, 1693, 317.8, 1678], "page": 1, "confidence": + 0.923}, "TotalPrice": {"type": "number", "valueNumber": 2.2, "text": "$2.20", + "boundingBox": [1107.7, 1584, 1263, 1574, 1268.3, 1656, 1113, 1666], "page": + 1, "confidence": 0.918}}}, {"type": "object", "valueObject": {"Quantity": + {"type": "number", "text": "1", "boundingBox": [232, 1834, 286.6, 1835, 285, + 1921, 230.4, 1920], "page": 1, "confidence": 0.858}, "Name": {"type": "string", + "valueString": "BACON & EGGS", "text": "BACON & EGGS", "boundingBox": [308, + 1836, 746, 1841.4, 745, 1925.4, 307, 1920], "page": 1, "confidence": 0.916}, + "TotalPrice": {"type": "number", "text": "$9.5", "boundingBox": [1133.9, 1955, + 1257, 1952, 1259.1, 2036, 1136, 2039], "page": 1, "confidence": 0.916}}}]}, + "Subtotal": {"type": "number", "valueNumber": 11.7, "text": "11.70", "boundingBox": + [1146, 2221, 1297.3, 2223, 1296, 2319, 1144.7, 2317], "page": 1, "confidence": + 0.955}, "Tax": {"type": "number", "valueNumber": 1.17, "text": "1.17", "boundingBox": + [1190, 2359, 1304, 2359, 1304, 2456, 1190, 2456], "page": 1, "confidence": + 0.979}, "Tip": {"type": "number", "valueNumber": 1.63, "text": "1.63", "boundingBox": + [1094, 2479, 1267.7, 2485, 1264, 2591, 1090.3, 2585], "page": 1, "confidence": + 0.941}, "Total": {"type": "number", "valueNumber": 14.5, "text": "$14.50", + "boundingBox": [1034.2, 2617, 1387.5, 2638.2, 1380, 2763, 1026.7, 2741.8], + "page": 1, "confidence": 0.985}}}]}}' + headers: + apim-request-id: 0f0d31ca-86d6-4fd4-9407-fdaa170bc62b + content-type: application/json; charset=utf-8 + date: Fri, 12 Jun 2020 17:00:17 GMT + strict-transport-security: max-age=31536000; includeSubDomains; preload + transfer-encoding: chunked + x-content-type-options: nosniff + x-envoy-upstream-service-time: '212' + status: + code: 200 + message: OK + url: https://centraluseuap.api.cognitive.microsoft.com/formrecognizer/v2.0-preview/prebuilt/receipt/analyzeResults/aefd083e-3ba3-4f84-beb5-719d583936d0 +version: 1 diff --git a/sdk/formrecognizer/azure-ai-formrecognizer/tests/recordings/test_training.test_polling_interval.yaml b/sdk/formrecognizer/azure-ai-formrecognizer/tests/recordings/test_training.test_polling_interval.yaml new file mode 100644 index 000000000000..51579c749a4d --- /dev/null +++ b/sdk/formrecognizer/azure-ai-formrecognizer/tests/recordings/test_training.test_polling_interval.yaml @@ -0,0 +1,287 @@ +interactions: +- request: + body: 'b''b\''{"source": "containersasurl", "sourceFilter": {"prefix": "", "includeSubFolders": + false}, "useLabelFile": false}\''''' + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + Content-Length: + - '288' + Content-Type: + - application/json + User-Agent: + - azsdk-python-ai-formrecognizer/1.0.0b4 Python/3.7.3 (Windows-10-10.0.18362-SP0) + Python/3.7.3 (Windows-10-10.0.18362-SP0) + method: POST + uri: https://centraluseuap.api.cognitive.microsoft.com/formrecognizer/v2.0-preview/custom/models + response: + body: + string: '' + headers: + apim-request-id: + - d39ac0fa-ba19-4498-9b41-2e4fe0a9db5e + content-length: + - '0' + date: + - Fri, 12 Jun 2020 17:14:17 GMT + location: + - https://centraluseuap.api.cognitive.microsoft.com/formrecognizer/v2.0-preview/custom/models/bb293dcb-3705-4a5d-8453-5139686c2721 + strict-transport-security: + - max-age=31536000; includeSubDomains; preload + x-content-type-options: + - nosniff + x-envoy-upstream-service-time: + - '1221' + status: + code: 201 + message: Created +- request: + body: null + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + User-Agent: + - azsdk-python-ai-formrecognizer/1.0.0b4 Python/3.7.3 (Windows-10-10.0.18362-SP0) + Python/3.7.3 (Windows-10-10.0.18362-SP0) + method: GET + uri: https://centraluseuap.api.cognitive.microsoft.com/formrecognizer/v2.0-preview/custom/models/bb293dcb-3705-4a5d-8453-5139686c2721?includeKeys=true + response: + body: + string: '{"modelInfo": {"modelId": "bb293dcb-3705-4a5d-8453-5139686c2721", "status": + "creating", "createdDateTime": "2020-06-12T17:14:16Z", "lastUpdatedDateTime": + "2020-06-12T17:14:16Z"}}' + headers: + apim-request-id: + - 1c623540-200a-4ff9-b93f-cabbe6768db0 + content-type: + - application/json; charset=utf-8 + date: + - Fri, 12 Jun 2020 17:14:23 GMT + strict-transport-security: + - max-age=31536000; includeSubDomains; preload + transfer-encoding: + - chunked + x-content-type-options: + - nosniff + x-envoy-upstream-service-time: + - '148' + status: + code: 200 + message: OK +- request: + body: null + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + User-Agent: + - azsdk-python-ai-formrecognizer/1.0.0b4 Python/3.7.3 (Windows-10-10.0.18362-SP0) + Python/3.7.3 (Windows-10-10.0.18362-SP0) + method: GET + uri: https://centraluseuap.api.cognitive.microsoft.com/formrecognizer/v2.0-preview/custom/models/bb293dcb-3705-4a5d-8453-5139686c2721?includeKeys=true + response: + body: + string: '{"modelInfo": {"modelId": "bb293dcb-3705-4a5d-8453-5139686c2721", "status": + "ready", "createdDateTime": "2020-06-12T17:14:16Z", "lastUpdatedDateTime": + "2020-06-12T17:14:28Z"}, "keys": {"clusters": {"0": ["Additional Notes:", + "Address:", "Company Name:", "Company Phone:", "Dated As:", "Details", "Email:", + "Hero Limited", "Name:", "Phone:", "Purchase Order", "Purchase Order #:", + "Quantity", "SUBTOTAL", "Seattle, WA 93849 Phone:", "Shipped From", "Shipped + To", "TAX", "TOTAL", "Total", "Unit Price", "Vendor Name:", "Website:"]}}, + "trainResult": {"trainingDocuments": [{"documentName": "Form_1.jpg", "pages": + 1, "errors": [], "status": "succeeded"}, {"documentName": "Form_2.jpg", "pages": + 1, "errors": [], "status": "succeeded"}, {"documentName": "Form_3.jpg", "pages": + 1, "errors": [], "status": "succeeded"}, {"documentName": "Form_4.jpg", "pages": + 1, "errors": [], "status": "succeeded"}, {"documentName": "Form_5.jpg", "pages": + 1, "errors": [], "status": "succeeded"}], "errors": []}}' + headers: + apim-request-id: + - d15e05cb-2953-45ca-8b70-2517f1a7f9e7 + content-type: + - application/json; charset=utf-8 + date: + - Fri, 12 Jun 2020 17:14:29 GMT + strict-transport-security: + - max-age=31536000; includeSubDomains; preload + transfer-encoding: + - chunked + x-content-type-options: + - nosniff + x-envoy-upstream-service-time: + - '149' + status: + code: 200 + message: OK +- request: + body: 'b''b\''{"source": "containersasurl", "sourceFilter": {"prefix": "", "includeSubFolders": + false}, "useLabelFile": false}\''''' + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + Content-Length: + - '288' + Content-Type: + - application/json + User-Agent: + - azsdk-python-ai-formrecognizer/1.0.0b4 Python/3.7.3 (Windows-10-10.0.18362-SP0) + Python/3.7.3 (Windows-10-10.0.18362-SP0) + method: POST + uri: https://centraluseuap.api.cognitive.microsoft.com/formrecognizer/v2.0-preview/custom/models + response: + body: + string: '' + headers: + apim-request-id: + - 30022b63-b2d4-4772-9926-50c933b943c2 + content-length: + - '0' + date: + - Fri, 12 Jun 2020 17:14:30 GMT + location: + - https://centraluseuap.api.cognitive.microsoft.com/formrecognizer/v2.0-preview/custom/models/b428e552-b46a-4a2e-a8cd-f8f5359656d5 + strict-transport-security: + - max-age=31536000; includeSubDomains; preload + x-content-type-options: + - nosniff + x-envoy-upstream-service-time: + - '212' + status: + code: 201 + message: Created +- request: + body: null + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + User-Agent: + - azsdk-python-ai-formrecognizer/1.0.0b4 Python/3.7.3 (Windows-10-10.0.18362-SP0) + Python/3.7.3 (Windows-10-10.0.18362-SP0) + method: GET + uri: https://centraluseuap.api.cognitive.microsoft.com/formrecognizer/v2.0-preview/custom/models/b428e552-b46a-4a2e-a8cd-f8f5359656d5?includeKeys=true + response: + body: + string: '{"modelInfo": {"modelId": "b428e552-b46a-4a2e-a8cd-f8f5359656d5", "status": + "creating", "createdDateTime": "2020-06-12T17:14:30Z", "lastUpdatedDateTime": + "2020-06-12T17:14:30Z"}}' + headers: + apim-request-id: + - 17cf4951-a81f-4ee1-81b1-4b1f037ec32b + content-type: + - application/json; charset=utf-8 + date: + - Fri, 12 Jun 2020 17:14:35 GMT + strict-transport-security: + - max-age=31536000; includeSubDomains; preload + transfer-encoding: + - chunked + x-content-type-options: + - nosniff + x-envoy-upstream-service-time: + - '163' + status: + code: 200 + message: OK +- request: + body: null + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + User-Agent: + - azsdk-python-ai-formrecognizer/1.0.0b4 Python/3.7.3 (Windows-10-10.0.18362-SP0) + Python/3.7.3 (Windows-10-10.0.18362-SP0) + method: GET + uri: https://centraluseuap.api.cognitive.microsoft.com/formrecognizer/v2.0-preview/custom/models/b428e552-b46a-4a2e-a8cd-f8f5359656d5?includeKeys=true + response: + body: + string: '{"modelInfo": {"modelId": "b428e552-b46a-4a2e-a8cd-f8f5359656d5", "status": + "creating", "createdDateTime": "2020-06-12T17:14:30Z", "lastUpdatedDateTime": + "2020-06-12T17:14:30Z"}}' + headers: + apim-request-id: + - bf02c76e-cfe7-4dfc-9c9f-9dfa8404619f + content-type: + - application/json; charset=utf-8 + date: + - Fri, 12 Jun 2020 17:14:40 GMT + strict-transport-security: + - max-age=31536000; includeSubDomains; preload + transfer-encoding: + - chunked + x-content-type-options: + - nosniff + x-envoy-upstream-service-time: + - '160' + status: + code: 200 + message: OK +- request: + body: null + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + User-Agent: + - azsdk-python-ai-formrecognizer/1.0.0b4 Python/3.7.3 (Windows-10-10.0.18362-SP0) + Python/3.7.3 (Windows-10-10.0.18362-SP0) + method: GET + uri: https://centraluseuap.api.cognitive.microsoft.com/formrecognizer/v2.0-preview/custom/models/b428e552-b46a-4a2e-a8cd-f8f5359656d5?includeKeys=true + response: + body: + string: '{"modelInfo": {"modelId": "b428e552-b46a-4a2e-a8cd-f8f5359656d5", "status": + "ready", "createdDateTime": "2020-06-12T17:14:30Z", "lastUpdatedDateTime": + "2020-06-12T17:14:43Z"}, "keys": {"clusters": {"0": ["Additional Notes:", + "Address:", "Company Name:", "Company Phone:", "Dated As:", "Details", "Email:", + "Hero Limited", "Name:", "Phone:", "Purchase Order", "Purchase Order #:", + "Quantity", "SUBTOTAL", "Seattle, WA 93849 Phone:", "Shipped From", "Shipped + To", "TAX", "TOTAL", "Total", "Unit Price", "Vendor Name:", "Website:"]}}, + "trainResult": {"trainingDocuments": [{"documentName": "Form_1.jpg", "pages": + 1, "errors": [], "status": "succeeded"}, {"documentName": "Form_2.jpg", "pages": + 1, "errors": [], "status": "succeeded"}, {"documentName": "Form_3.jpg", "pages": + 1, "errors": [], "status": "succeeded"}, {"documentName": "Form_4.jpg", "pages": + 1, "errors": [], "status": "succeeded"}, {"documentName": "Form_5.jpg", "pages": + 1, "errors": [], "status": "succeeded"}], "errors": []}}' + headers: + apim-request-id: + - a1205f89-6b1b-41f5-b6f4-d09b7dc4cb0f + content-type: + - application/json; charset=utf-8 + date: + - Fri, 12 Jun 2020 17:14:45 GMT + strict-transport-security: + - max-age=31536000; includeSubDomains; preload + transfer-encoding: + - chunked + x-content-type-options: + - nosniff + x-envoy-upstream-service-time: + - '195' + status: + code: 200 + message: OK +version: 1 diff --git a/sdk/formrecognizer/azure-ai-formrecognizer/tests/recordings/test_training_async.test_polling_interval.yaml b/sdk/formrecognizer/azure-ai-formrecognizer/tests/recordings/test_training_async.test_polling_interval.yaml new file mode 100644 index 000000000000..3ca99f843569 --- /dev/null +++ b/sdk/formrecognizer/azure-ai-formrecognizer/tests/recordings/test_training_async.test_polling_interval.yaml @@ -0,0 +1,203 @@ +interactions: +- request: + body: 'b''b\''{"source": "containersasurl", "sourceFilter": {"prefix": "", "includeSubFolders": + false}, "useLabelFile": false}\''''' + headers: + Content-Length: + - '288' + Content-Type: + - application/json + User-Agent: + - azsdk-python-ai-formrecognizer/1.0.0b4 Python/3.7.3 (Windows-10-10.0.18362-SP0) + Python/3.7.3 (Windows-10-10.0.18362-SP0) + method: POST + uri: https://centraluseuap.api.cognitive.microsoft.com/formrecognizer/v2.0-preview/custom/models + response: + body: + string: '' + headers: + apim-request-id: 266ae45e-1e1a-4c42-a21a-f73ca5bbefb8 + content-length: '0' + date: Fri, 12 Jun 2020 17:17:20 GMT + location: https://centraluseuap.api.cognitive.microsoft.com/formrecognizer/v2.0-preview/custom/models/280b1966-c58c-400b-aa00-967fbc90cbf6 + strict-transport-security: max-age=31536000; includeSubDomains; preload + x-content-type-options: nosniff + x-envoy-upstream-service-time: '523' + status: + code: 201 + message: Created + url: https://centraluseuap.api.cognitive.microsoft.com//formrecognizer/v2.0-preview/custom/models +- request: + body: null + headers: + User-Agent: + - azsdk-python-ai-formrecognizer/1.0.0b4 Python/3.7.3 (Windows-10-10.0.18362-SP0) + Python/3.7.3 (Windows-10-10.0.18362-SP0) + method: GET + uri: https://centraluseuap.api.cognitive.microsoft.com/formrecognizer/v2.0-preview/custom/models/280b1966-c58c-400b-aa00-967fbc90cbf6?includeKeys=true + response: + body: + string: '{"modelInfo": {"modelId": "280b1966-c58c-400b-aa00-967fbc90cbf6", "status": + "creating", "createdDateTime": "2020-06-12T17:17:20Z", "lastUpdatedDateTime": + "2020-06-12T17:17:20Z"}}' + headers: + apim-request-id: 7f1bf53e-0d76-4bec-84fe-ac6d448fba4e + content-type: application/json; charset=utf-8 + date: Fri, 12 Jun 2020 17:17:26 GMT + strict-transport-security: max-age=31536000; includeSubDomains; preload + transfer-encoding: chunked + x-content-type-options: nosniff + x-envoy-upstream-service-time: '67' + status: + code: 200 + message: OK + url: https://centraluseuap.api.cognitive.microsoft.com/formrecognizer/v2.0-preview/custom/models/280b1966-c58c-400b-aa00-967fbc90cbf6?includeKeys=true +- request: + body: null + headers: + User-Agent: + - azsdk-python-ai-formrecognizer/1.0.0b4 Python/3.7.3 (Windows-10-10.0.18362-SP0) + Python/3.7.3 (Windows-10-10.0.18362-SP0) + method: GET + uri: https://centraluseuap.api.cognitive.microsoft.com/formrecognizer/v2.0-preview/custom/models/280b1966-c58c-400b-aa00-967fbc90cbf6?includeKeys=true + response: + body: + string: '{"modelInfo": {"modelId": "280b1966-c58c-400b-aa00-967fbc90cbf6", "status": + "ready", "createdDateTime": "2020-06-12T17:17:20Z", "lastUpdatedDateTime": + "2020-06-12T17:17:30Z"}, "keys": {"clusters": {"0": ["Additional Notes:", + "Address:", "Company Name:", "Company Phone:", "Dated As:", "Details", "Email:", + "Hero Limited", "Name:", "Phone:", "Purchase Order", "Purchase Order #:", + "Quantity", "SUBTOTAL", "Seattle, WA 93849 Phone:", "Shipped From", "Shipped + To", "TAX", "TOTAL", "Total", "Unit Price", "Vendor Name:", "Website:"]}}, + "trainResult": {"trainingDocuments": [{"documentName": "Form_1.jpg", "pages": + 1, "errors": [], "status": "succeeded"}, {"documentName": "Form_2.jpg", "pages": + 1, "errors": [], "status": "succeeded"}, {"documentName": "Form_3.jpg", "pages": + 1, "errors": [], "status": "succeeded"}, {"documentName": "Form_4.jpg", "pages": + 1, "errors": [], "status": "succeeded"}, {"documentName": "Form_5.jpg", "pages": + 1, "errors": [], "status": "succeeded"}], "errors": []}}' + headers: + apim-request-id: e5ce8d1a-e6ce-4637-ad2d-c81861343bd2 + content-type: application/json; charset=utf-8 + date: Fri, 12 Jun 2020 17:17:33 GMT + strict-transport-security: max-age=31536000; includeSubDomains; preload + transfer-encoding: chunked + x-content-type-options: nosniff + x-envoy-upstream-service-time: '157' + status: + code: 200 + message: OK + url: https://centraluseuap.api.cognitive.microsoft.com/formrecognizer/v2.0-preview/custom/models/280b1966-c58c-400b-aa00-967fbc90cbf6?includeKeys=true +- request: + body: 'b''b\''{"source": "containersasurl", "sourceFilter": {"prefix": "", "includeSubFolders": + false}, "useLabelFile": false}\''''' + headers: + Content-Length: + - '288' + Content-Type: + - application/json + User-Agent: + - azsdk-python-ai-formrecognizer/1.0.0b4 Python/3.7.3 (Windows-10-10.0.18362-SP0) + Python/3.7.3 (Windows-10-10.0.18362-SP0) + method: POST + uri: https://centraluseuap.api.cognitive.microsoft.com/formrecognizer/v2.0-preview/custom/models + response: + body: + string: '' + headers: + apim-request-id: fb75dd82-85ff-4a90-a429-74c065ad7df4 + content-length: '0' + date: Fri, 12 Jun 2020 17:17:33 GMT + location: https://centraluseuap.api.cognitive.microsoft.com/formrecognizer/v2.0-preview/custom/models/6f6b1513-1eca-4d9a-9f79-cdc500551d01 + strict-transport-security: max-age=31536000; includeSubDomains; preload + x-content-type-options: nosniff + x-envoy-upstream-service-time: '188' + status: + code: 201 + message: Created + url: https://centraluseuap.api.cognitive.microsoft.com//formrecognizer/v2.0-preview/custom/models +- request: + body: null + headers: + User-Agent: + - azsdk-python-ai-formrecognizer/1.0.0b4 Python/3.7.3 (Windows-10-10.0.18362-SP0) + Python/3.7.3 (Windows-10-10.0.18362-SP0) + method: GET + uri: https://centraluseuap.api.cognitive.microsoft.com/formrecognizer/v2.0-preview/custom/models/6f6b1513-1eca-4d9a-9f79-cdc500551d01?includeKeys=true + response: + body: + string: '{"modelInfo": {"modelId": "6f6b1513-1eca-4d9a-9f79-cdc500551d01", "status": + "creating", "createdDateTime": "2020-06-12T17:17:33Z", "lastUpdatedDateTime": + "2020-06-12T17:17:33Z"}}' + headers: + apim-request-id: a26cf20e-3646-41c7-a707-22719fceaf6a + content-type: application/json; charset=utf-8 + date: Fri, 12 Jun 2020 17:17:44 GMT + strict-transport-security: max-age=31536000; includeSubDomains; preload + transfer-encoding: chunked + x-content-type-options: nosniff + x-envoy-upstream-service-time: '5227' + status: + code: 200 + message: OK + url: https://centraluseuap.api.cognitive.microsoft.com/formrecognizer/v2.0-preview/custom/models/6f6b1513-1eca-4d9a-9f79-cdc500551d01?includeKeys=true +- request: + body: null + headers: + User-Agent: + - azsdk-python-ai-formrecognizer/1.0.0b4 Python/3.7.3 (Windows-10-10.0.18362-SP0) + Python/3.7.3 (Windows-10-10.0.18362-SP0) + method: GET + uri: https://centraluseuap.api.cognitive.microsoft.com/formrecognizer/v2.0-preview/custom/models/6f6b1513-1eca-4d9a-9f79-cdc500551d01?includeKeys=true + response: + body: + string: '{"modelInfo": {"modelId": "6f6b1513-1eca-4d9a-9f79-cdc500551d01", "status": + "creating", "createdDateTime": "2020-06-12T17:17:33Z", "lastUpdatedDateTime": + "2020-06-12T17:17:33Z"}}' + headers: + apim-request-id: fb1c69cf-7ef4-4140-bff4-a40efc38492d + content-type: application/json; charset=utf-8 + date: Fri, 12 Jun 2020 17:17:49 GMT + strict-transport-security: max-age=31536000; includeSubDomains; preload + transfer-encoding: chunked + x-content-type-options: nosniff + x-envoy-upstream-service-time: '203' + status: + code: 200 + message: OK + url: https://centraluseuap.api.cognitive.microsoft.com/formrecognizer/v2.0-preview/custom/models/6f6b1513-1eca-4d9a-9f79-cdc500551d01?includeKeys=true +- request: + body: null + headers: + User-Agent: + - azsdk-python-ai-formrecognizer/1.0.0b4 Python/3.7.3 (Windows-10-10.0.18362-SP0) + Python/3.7.3 (Windows-10-10.0.18362-SP0) + method: GET + uri: https://centraluseuap.api.cognitive.microsoft.com/formrecognizer/v2.0-preview/custom/models/6f6b1513-1eca-4d9a-9f79-cdc500551d01?includeKeys=true + response: + body: + string: '{"modelInfo": {"modelId": "6f6b1513-1eca-4d9a-9f79-cdc500551d01", "status": + "ready", "createdDateTime": "2020-06-12T17:17:33Z", "lastUpdatedDateTime": + "2020-06-12T17:17:53Z"}, "keys": {"clusters": {"0": ["Additional Notes:", + "Address:", "Company Name:", "Company Phone:", "Dated As:", "Details", "Email:", + "Hero Limited", "Name:", "Phone:", "Purchase Order", "Purchase Order #:", + "Quantity", "SUBTOTAL", "Seattle, WA 93849 Phone:", "Shipped From", "Shipped + To", "TAX", "TOTAL", "Total", "Unit Price", "Vendor Name:", "Website:"]}}, + "trainResult": {"trainingDocuments": [{"documentName": "Form_1.jpg", "pages": + 1, "errors": [], "status": "succeeded"}, {"documentName": "Form_2.jpg", "pages": + 1, "errors": [], "status": "succeeded"}, {"documentName": "Form_3.jpg", "pages": + 1, "errors": [], "status": "succeeded"}, {"documentName": "Form_4.jpg", "pages": + 1, "errors": [], "status": "succeeded"}, {"documentName": "Form_5.jpg", "pages": + 1, "errors": [], "status": "succeeded"}], "errors": []}}' + headers: + apim-request-id: b4e7cf16-37d8-4853-b0b3-ae8ec59768f5 + content-type: application/json; charset=utf-8 + date: Fri, 12 Jun 2020 17:17:54 GMT + strict-transport-security: max-age=31536000; includeSubDomains; preload + transfer-encoding: chunked + x-content-type-options: nosniff + x-envoy-upstream-service-time: '17' + status: + code: 200 + message: OK + url: https://centraluseuap.api.cognitive.microsoft.com/formrecognizer/v2.0-preview/custom/models/6f6b1513-1eca-4d9a-9f79-cdc500551d01?includeKeys=true +version: 1 diff --git a/sdk/formrecognizer/azure-ai-formrecognizer/tests/test_content.py b/sdk/formrecognizer/azure-ai-formrecognizer/tests/test_content.py index 1f8bb1c8d568..1c3f693af9e7 100644 --- a/sdk/formrecognizer/azure-ai-formrecognizer/tests/test_content.py +++ b/sdk/formrecognizer/azure-ai-formrecognizer/tests/test_content.py @@ -5,6 +5,7 @@ # ------------------------------------ import pytest +import functools from io import BytesIO from azure.core.exceptions import ServiceRequestError, ClientAuthenticationError, HttpResponseError from azure.core.credentials import AzureKeyCredential @@ -12,6 +13,10 @@ from azure.ai.formrecognizer._response_handlers import prepare_content_result from azure.ai.formrecognizer import FormRecognizerClient, FormContentType from testcase import FormRecognizerTest, GlobalFormRecognizerAccountPreparer +from testcase import GlobalClientPreparer as _GlobalClientPreparer + + +GlobalClientPreparer = functools.partial(_GlobalClientPreparer, FormRecognizerClient) class TestContentFromStream(FormRecognizerTest): @@ -25,8 +30,8 @@ def test_content_bad_endpoint(self, resource_group, location, form_recognizer_ac poller = client.begin_recognize_content(myfile) @GlobalFormRecognizerAccountPreparer() - def test_content_authentication_successful_key(self, resource_group, location, form_recognizer_account, form_recognizer_account_key): - client = FormRecognizerClient(form_recognizer_account, AzureKeyCredential(form_recognizer_account_key)) + @GlobalClientPreparer() + def test_content_authentication_successful_key(self, client): with open(self.invoice_pdf, "rb") as fd: myfile = fd.read() poller = client.begin_recognize_content(myfile) @@ -39,8 +44,8 @@ def test_content_authentication_bad_key(self, resource_group, location, form_rec poller = client.begin_recognize_content(b"xx", content_type="application/pdf") @GlobalFormRecognizerAccountPreparer() - def test_passing_enum_content_type(self, resource_group, location, form_recognizer_account, form_recognizer_account_key): - client = FormRecognizerClient(form_recognizer_account, AzureKeyCredential(form_recognizer_account_key)) + @GlobalClientPreparer() + def test_passing_enum_content_type(self, client): with open(self.invoice_pdf, "rb") as fd: myfile = fd.read() poller = client.begin_recognize_content( @@ -51,8 +56,8 @@ def test_passing_enum_content_type(self, resource_group, location, form_recogniz self.assertIsNotNone(result) @GlobalFormRecognizerAccountPreparer() - def test_damaged_file_passed_as_bytes(self, resource_group, location, form_recognizer_account, form_recognizer_account_key): - client = FormRecognizerClient(form_recognizer_account, AzureKeyCredential(form_recognizer_account_key)) + @GlobalClientPreparer() + def test_damaged_file_passed_as_bytes(self, client): damaged_pdf = b"\x25\x50\x44\x46\x55\x55\x55" # still has correct bytes to be recognized as PDF with self.assertRaises(HttpResponseError): poller = client.begin_recognize_content( @@ -60,8 +65,8 @@ def test_damaged_file_passed_as_bytes(self, resource_group, location, form_recog ) @GlobalFormRecognizerAccountPreparer() - def test_damaged_file_bytes_fails_autodetect_content_type(self, resource_group, location, form_recognizer_account, form_recognizer_account_key): - client = FormRecognizerClient(form_recognizer_account, AzureKeyCredential(form_recognizer_account_key)) + @GlobalClientPreparer() + def test_damaged_file_bytes_fails_autodetect_content_type(self, client): damaged_pdf = b"\x50\x44\x46\x55\x55\x55" # doesn't match any magic file numbers with self.assertRaises(ValueError): poller = client.begin_recognize_content( @@ -69,8 +74,8 @@ def test_damaged_file_bytes_fails_autodetect_content_type(self, resource_group, ) @GlobalFormRecognizerAccountPreparer() - def test_damaged_file_passed_as_bytes_io(self, resource_group, location, form_recognizer_account, form_recognizer_account_key): - client = FormRecognizerClient(form_recognizer_account, AzureKeyCredential(form_recognizer_account_key)) + @GlobalClientPreparer() + def test_damaged_file_passed_as_bytes_io(self, client): damaged_pdf = BytesIO(b"\x25\x50\x44\x46\x55\x55\x55") # still has correct bytes to be recognized as PDF with self.assertRaises(HttpResponseError): poller = client.begin_recognize_content( @@ -78,8 +83,8 @@ def test_damaged_file_passed_as_bytes_io(self, resource_group, location, form_re ) @GlobalFormRecognizerAccountPreparer() - def test_damaged_file_bytes_io_fails_autodetect(self, resource_group, location, form_recognizer_account, form_recognizer_account_key): - client = FormRecognizerClient(form_recognizer_account, AzureKeyCredential(form_recognizer_account_key)) + @GlobalClientPreparer() + def test_damaged_file_bytes_io_fails_autodetect(self, client): damaged_pdf = BytesIO(b"\x50\x44\x46\x55\x55\x55") # doesn't match any magic file numbers with self.assertRaises(ValueError): poller = client.begin_recognize_content( @@ -87,9 +92,8 @@ def test_damaged_file_bytes_io_fails_autodetect(self, resource_group, location, ) @GlobalFormRecognizerAccountPreparer() - def test_blank_page(self, resource_group, location, form_recognizer_account, form_recognizer_account_key): - client = FormRecognizerClient(form_recognizer_account, AzureKeyCredential(form_recognizer_account_key)) - + @GlobalClientPreparer() + def test_blank_page(self, client): with open(self.blank_pdf, "rb") as stream: poller = client.begin_recognize_content( stream, @@ -98,8 +102,8 @@ def test_blank_page(self, resource_group, location, form_recognizer_account, for self.assertIsNotNone(result) @GlobalFormRecognizerAccountPreparer() - def test_passing_bad_content_type_param_passed(self, resource_group, location, form_recognizer_account, form_recognizer_account_key): - client = FormRecognizerClient(form_recognizer_account, AzureKeyCredential(form_recognizer_account_key)) + @GlobalClientPreparer() + def test_passing_bad_content_type_param_passed(self, client): with open(self.invoice_pdf, "rb") as fd: myfile = fd.read() with self.assertRaises(ValueError): @@ -109,16 +113,14 @@ def test_passing_bad_content_type_param_passed(self, resource_group, location, f ) @GlobalFormRecognizerAccountPreparer() - def test_content_stream_passing_url(self, resource_group, location, form_recognizer_account, form_recognizer_account_key): - client = FormRecognizerClient(form_recognizer_account, AzureKeyCredential(form_recognizer_account_key)) - + @GlobalClientPreparer() + def test_content_stream_passing_url(self, client): with self.assertRaises(TypeError): poller = client.begin_recognize_content("https://badurl.jpg", content_type="application/json") @GlobalFormRecognizerAccountPreparer() - def test_auto_detect_unsupported_stream_content(self, resource_group, location, form_recognizer_account, form_recognizer_account_key): - client = FormRecognizerClient(form_recognizer_account, AzureKeyCredential(form_recognizer_account_key)) - + @GlobalClientPreparer() + def test_auto_detect_unsupported_stream_content(self, client): with open(self.unsupported_content_py, "rb") as fd: myfile = fd.read() @@ -128,8 +130,8 @@ def test_auto_detect_unsupported_stream_content(self, resource_group, location, ) @GlobalFormRecognizerAccountPreparer() - def test_content_stream_transform_pdf(self, resource_group, location, form_recognizer_account, form_recognizer_account_key): - client = FormRecognizerClient(form_recognizer_account, AzureKeyCredential(form_recognizer_account_key)) + @GlobalClientPreparer() + def test_content_stream_transform_pdf(self, client): with open(self.invoice_pdf, "rb") as fd: myform = fd.read() @@ -152,9 +154,8 @@ def callback(raw_response, _, headers): self.assertFormPagesTransformCorrect(layout, read_results, page_results) @GlobalFormRecognizerAccountPreparer() - def test_content_stream_pdf(self, resource_group, location, form_recognizer_account, form_recognizer_account_key): - client = FormRecognizerClient(form_recognizer_account, - AzureKeyCredential(form_recognizer_account_key)) + @GlobalClientPreparer() + def test_content_stream_pdf(self, client): with open(self.invoice_pdf, "rb") as fd: myform = fd.read() @@ -169,8 +170,8 @@ def test_content_stream_pdf(self, resource_group, location, form_recognizer_acco self.assertEqual(layout.tables[0].page_number, 1) @GlobalFormRecognizerAccountPreparer() - def test_content_stream_transform_jpg(self, resource_group, location, form_recognizer_account, form_recognizer_account_key): - client = FormRecognizerClient(form_recognizer_account, AzureKeyCredential(form_recognizer_account_key)) + @GlobalClientPreparer() + def test_content_stream_transform_jpg(self, client): with open(self.form_jpg, "rb") as fd: myform = fd.read() @@ -193,9 +194,8 @@ def callback(raw_response, _, headers): self.assertFormPagesTransformCorrect(layout, read_results, page_results) @GlobalFormRecognizerAccountPreparer() - def test_content_stream_jpg(self, resource_group, location, form_recognizer_account, form_recognizer_account_key): - client = FormRecognizerClient(form_recognizer_account, - AzureKeyCredential(form_recognizer_account_key)) + @GlobalClientPreparer() + def test_content_stream_jpg(self, client): with open(self.form_jpg, "rb") as stream: poller = client.begin_recognize_content(stream) result = poller.result() @@ -211,8 +211,8 @@ def test_content_stream_jpg(self, resource_group, location, form_recognizer_acco self.assertEqual(layout.tables[1].page_number, 1) @GlobalFormRecognizerAccountPreparer() - def test_content_multipage(self, resource_group, location, form_recognizer_account, form_recognizer_account_key): - client = FormRecognizerClient(form_recognizer_account, AzureKeyCredential(form_recognizer_account_key)) + @GlobalClientPreparer() + def test_content_multipage(self, client): with open(self.multipage_invoice_pdf, "rb") as fd: invoice = fd.read() poller = client.begin_recognize_content(invoice) @@ -222,8 +222,8 @@ def test_content_multipage(self, resource_group, location, form_recognizer_accou self.assertFormPagesHasValues(result) @GlobalFormRecognizerAccountPreparer() - def test_content_multipage_transform(self, resource_group, location, form_recognizer_account, form_recognizer_account_key): - client = FormRecognizerClient(form_recognizer_account, AzureKeyCredential(form_recognizer_account_key)) + @GlobalClientPreparer() + def test_content_multipage_transform(self, client): with open(self.multipage_invoice_pdf, "rb") as fd: myform = fd.read() @@ -246,10 +246,9 @@ def callback(raw_response, _, headers): self.assertFormPagesTransformCorrect(layout, read_results, page_results) @GlobalFormRecognizerAccountPreparer() + @GlobalClientPreparer() @pytest.mark.live_test_only - def test_content_continuation_token(self, resource_group, location, form_recognizer_account, form_recognizer_account_key): - client = FormRecognizerClient(form_recognizer_account, - AzureKeyCredential(form_recognizer_account_key)) + def test_content_continuation_token(self, client): with open(self.form_jpg, "rb") as fd: myfile = fd.read() initial_poller = client.begin_recognize_content(myfile) @@ -261,9 +260,8 @@ def test_content_continuation_token(self, resource_group, location, form_recogni initial_poller.wait() # necessary so azure-devtools doesn't throw assertion error @GlobalFormRecognizerAccountPreparer() - def test_content_multipage_table_span_pdf(self, resource_group, location, form_recognizer_account, form_recognizer_account_key): - client = FormRecognizerClient(form_recognizer_account, - AzureKeyCredential(form_recognizer_account_key)) + @GlobalClientPreparer() + def test_content_multipage_table_span_pdf(self, client): with open(self.multipage_table_pdf, "rb") as stream: poller = client.begin_recognize_content(stream) @@ -287,9 +285,8 @@ def test_content_multipage_table_span_pdf(self, resource_group, location, form_r self.assertFormPagesHasValues(result) @GlobalFormRecognizerAccountPreparer() - def test_content_multipage_table_span_transform(self, resource_group, location, form_recognizer_account, - form_recognizer_account_key): - client = FormRecognizerClient(form_recognizer_account, AzureKeyCredential(form_recognizer_account_key)) + @GlobalClientPreparer() + def test_content_multipage_table_span_transform(self, client): with open(self.multipage_table_pdf, "rb") as fd: myform = fd.read() diff --git a/sdk/formrecognizer/azure-ai-formrecognizer/tests/test_content_async.py b/sdk/formrecognizer/azure-ai-formrecognizer/tests/test_content_async.py index 59dccb633efd..9c13de807c60 100644 --- a/sdk/formrecognizer/azure-ai-formrecognizer/tests/test_content_async.py +++ b/sdk/formrecognizer/azure-ai-formrecognizer/tests/test_content_async.py @@ -5,6 +5,7 @@ # ------------------------------------ import pytest +import functools from io import BytesIO from azure.core.exceptions import ServiceRequestError, ClientAuthenticationError, HttpResponseError from azure.core.credentials import AzureKeyCredential @@ -14,6 +15,10 @@ from azure.ai.formrecognizer import FormContentType from testcase import GlobalFormRecognizerAccountPreparer from asynctestcase import AsyncFormRecognizerTest +from testcase import GlobalClientPreparer as _GlobalClientPreparer + + +GlobalClientPreparer = functools.partial(_GlobalClientPreparer, FormRecognizerClient) class TestContentFromStreamAsync(AsyncFormRecognizerTest): @@ -28,8 +33,8 @@ async def test_content_bad_endpoint(self, resource_group, location, form_recogni result = await poller.result() @GlobalFormRecognizerAccountPreparer() - async def test_content_authentication_successful_key(self, resource_group, location, form_recognizer_account, form_recognizer_account_key): - client = FormRecognizerClient(form_recognizer_account, AzureKeyCredential(form_recognizer_account_key)) + @GlobalClientPreparer() + async def test_content_authentication_successful_key(self, client): with open(self.invoice_pdf, "rb") as fd: myfile = fd.read() poller = await client.begin_recognize_content(myfile) @@ -43,8 +48,8 @@ async def test_content_authentication_bad_key(self, resource_group, location, fo result = await poller.result() @GlobalFormRecognizerAccountPreparer() - async def test_passing_enum_content_type(self, resource_group, location, form_recognizer_account, form_recognizer_account_key): - client = FormRecognizerClient(form_recognizer_account, AzureKeyCredential(form_recognizer_account_key)) + @GlobalClientPreparer() + async def test_passing_enum_content_type(self, client): with open(self.invoice_pdf, "rb") as fd: myfile = fd.read() poller = await client.begin_recognize_content( @@ -55,8 +60,8 @@ async def test_passing_enum_content_type(self, resource_group, location, form_re self.assertIsNotNone(result) @GlobalFormRecognizerAccountPreparer() - async def test_damaged_file_passed_as_bytes(self, resource_group, location, form_recognizer_account, form_recognizer_account_key): - client = FormRecognizerClient(form_recognizer_account, AzureKeyCredential(form_recognizer_account_key)) + @GlobalClientPreparer() + async def test_damaged_file_passed_as_bytes(self, client): damaged_pdf = b"\x25\x50\x44\x46\x55\x55\x55" # still has correct bytes to be recognized as PDF with self.assertRaises(HttpResponseError): poller = await client.begin_recognize_content( @@ -65,8 +70,8 @@ async def test_damaged_file_passed_as_bytes(self, resource_group, location, form result = await poller.result() @GlobalFormRecognizerAccountPreparer() - async def test_damaged_file_bytes_fails_autodetect_content_type(self, resource_group, location, form_recognizer_account, form_recognizer_account_key): - client = FormRecognizerClient(form_recognizer_account, AzureKeyCredential(form_recognizer_account_key)) + @GlobalClientPreparer() + async def test_damaged_file_bytes_fails_autodetect_content_type(self, client): damaged_pdf = b"\x50\x44\x46\x55\x55\x55" # doesn't match any magic file numbers with self.assertRaises(ValueError): poller = await client.begin_recognize_content( @@ -75,8 +80,8 @@ async def test_damaged_file_bytes_fails_autodetect_content_type(self, resource_g result = await poller.result() @GlobalFormRecognizerAccountPreparer() - async def test_damaged_file_passed_as_bytes_io(self, resource_group, location, form_recognizer_account, form_recognizer_account_key): - client = FormRecognizerClient(form_recognizer_account, AzureKeyCredential(form_recognizer_account_key)) + @GlobalClientPreparer() + async def test_damaged_file_passed_as_bytes_io(self, client): damaged_pdf = BytesIO(b"\x25\x50\x44\x46\x55\x55\x55") # still has correct bytes to be recognized as PDF with self.assertRaises(HttpResponseError): poller = await client.begin_recognize_content( @@ -85,8 +90,8 @@ async def test_damaged_file_passed_as_bytes_io(self, resource_group, location, f result = await poller.result() @GlobalFormRecognizerAccountPreparer() - async def test_damaged_file_bytes_io_fails_autodetect(self, resource_group, location, form_recognizer_account, form_recognizer_account_key): - client = FormRecognizerClient(form_recognizer_account, AzureKeyCredential(form_recognizer_account_key)) + @GlobalClientPreparer() + async def test_damaged_file_bytes_io_fails_autodetect(self, client): damaged_pdf = BytesIO(b"\x50\x44\x46\x55\x55\x55") # doesn't match any magic file numbers with self.assertRaises(ValueError): poller = await client.begin_recognize_content( @@ -95,9 +100,8 @@ async def test_damaged_file_bytes_io_fails_autodetect(self, resource_group, loca result = await poller.result() @GlobalFormRecognizerAccountPreparer() - async def test_blank_page(self, resource_group, location, form_recognizer_account, form_recognizer_account_key): - client = FormRecognizerClient(form_recognizer_account, AzureKeyCredential(form_recognizer_account_key)) - + @GlobalClientPreparer() + async def test_blank_page(self, client): with open(self.blank_pdf, "rb") as fd: blank = fd.read() poller = await client.begin_recognize_content( @@ -107,8 +111,8 @@ async def test_blank_page(self, resource_group, location, form_recognizer_accoun self.assertIsNotNone(result) @GlobalFormRecognizerAccountPreparer() - async def test_passing_bad_content_type_param_passed(self, resource_group, location, form_recognizer_account, form_recognizer_account_key): - client = FormRecognizerClient(form_recognizer_account, AzureKeyCredential(form_recognizer_account_key)) + @GlobalClientPreparer() + async def test_passing_bad_content_type_param_passed(self, client): with open(self.invoice_pdf, "rb") as fd: myfile = fd.read() with self.assertRaises(ValueError): @@ -119,17 +123,15 @@ async def test_passing_bad_content_type_param_passed(self, resource_group, locat result = await poller.result() @GlobalFormRecognizerAccountPreparer() - async def test_content_stream_passing_url(self, resource_group, location, form_recognizer_account, form_recognizer_account_key): - client = FormRecognizerClient(form_recognizer_account, AzureKeyCredential(form_recognizer_account_key)) - + @GlobalClientPreparer() + async def test_content_stream_passing_url(self, client): with self.assertRaises(TypeError): poller = await client.begin_recognize_content("https://badurl.jpg", content_type="application/json") result = await poller.result() @GlobalFormRecognizerAccountPreparer() - async def test_auto_detect_unsupported_stream_content(self, resource_group, location, form_recognizer_account, form_recognizer_account_key): - client = FormRecognizerClient(form_recognizer_account, AzureKeyCredential(form_recognizer_account_key)) - + @GlobalClientPreparer() + async def test_auto_detect_unsupported_stream_content(self, client): with open(self.unsupported_content_py, "rb") as fd: myfile = fd.read() @@ -140,8 +142,8 @@ async def test_auto_detect_unsupported_stream_content(self, resource_group, loca result = await poller.result() @GlobalFormRecognizerAccountPreparer() - async def test_content_stream_transform_pdf(self, resource_group, location, form_recognizer_account, form_recognizer_account_key): - client = FormRecognizerClient(form_recognizer_account, AzureKeyCredential(form_recognizer_account_key)) + @GlobalClientPreparer() + async def test_content_stream_transform_pdf(self, client): with open(self.invoice_pdf, "rb") as fd: myform = fd.read() @@ -164,9 +166,8 @@ def callback(raw_response, _, headers): self.assertFormPagesTransformCorrect(layout, read_results, page_results) @GlobalFormRecognizerAccountPreparer() - async def test_content_stream_pdf(self, resource_group, location, form_recognizer_account, form_recognizer_account_key): - client = FormRecognizerClient(form_recognizer_account, - AzureKeyCredential(form_recognizer_account_key)) + @GlobalClientPreparer() + async def test_content_stream_pdf(self, client): with open(self.invoice_pdf, "rb") as fd: myform = fd.read() @@ -181,8 +182,8 @@ async def test_content_stream_pdf(self, resource_group, location, form_recognize self.assertEqual(layout.tables[0].page_number, 1) @GlobalFormRecognizerAccountPreparer() - async def test_content_stream_transform_jpg(self, resource_group, location, form_recognizer_account, form_recognizer_account_key): - client = FormRecognizerClient(form_recognizer_account, AzureKeyCredential(form_recognizer_account_key)) + @GlobalClientPreparer() + async def test_content_stream_transform_jpg(self, client): with open(self.form_jpg, "rb") as fd: myform = fd.read() @@ -205,9 +206,8 @@ def callback(raw_response, _, headers): self.assertFormPagesTransformCorrect(layout, read_results, page_results) @GlobalFormRecognizerAccountPreparer() - async def test_content_stream_jpg(self, resource_group, location, form_recognizer_account, form_recognizer_account_key): - client = FormRecognizerClient(form_recognizer_account, - AzureKeyCredential(form_recognizer_account_key)) + @GlobalClientPreparer() + async def test_content_stream_jpg(self, client): with open(self.form_jpg, "rb") as fd: myform = fd.read() @@ -225,8 +225,8 @@ async def test_content_stream_jpg(self, resource_group, location, form_recognize self.assertEqual(layout.tables[1].page_number, 1) @GlobalFormRecognizerAccountPreparer() - async def test_content_multipage(self, resource_group, location, form_recognizer_account, form_recognizer_account_key): - client = FormRecognizerClient(form_recognizer_account, AzureKeyCredential(form_recognizer_account_key)) + @GlobalClientPreparer() + async def test_content_multipage(self, client): with open(self.multipage_invoice_pdf, "rb") as fd: invoice = fd.read() poller = await client.begin_recognize_content(invoice) @@ -236,8 +236,8 @@ async def test_content_multipage(self, resource_group, location, form_recognizer self.assertFormPagesHasValues(result) @GlobalFormRecognizerAccountPreparer() - async def test_content_multipage_transform(self, resource_group, location, form_recognizer_account, form_recognizer_account_key): - client = FormRecognizerClient(form_recognizer_account, AzureKeyCredential(form_recognizer_account_key)) + @GlobalClientPreparer() + async def test_content_multipage_transform(self, client): with open(self.multipage_invoice_pdf, "rb") as fd: myform = fd.read() @@ -260,10 +260,9 @@ def callback(raw_response, _, headers): self.assertFormPagesTransformCorrect(layout, read_results, page_results) @GlobalFormRecognizerAccountPreparer() + @GlobalClientPreparer() @pytest.mark.live_test_only - async def test_content_continuation_token(self, resource_group, location, form_recognizer_account, form_recognizer_account_key): - client = FormRecognizerClient(form_recognizer_account, - AzureKeyCredential(form_recognizer_account_key)) + async def test_content_continuation_token(self, client): with open(self.form_jpg, "rb") as fd: myfile = fd.read() initial_poller = await client.begin_recognize_content(myfile) @@ -276,10 +275,8 @@ async def test_content_continuation_token(self, resource_group, location, form_r @GlobalFormRecognizerAccountPreparer() - async def test_content_multipage_table_span_pdf(self, resource_group, location, form_recognizer_account, form_recognizer_account_key): - client = FormRecognizerClient(form_recognizer_account, - AzureKeyCredential(form_recognizer_account_key)) - + @GlobalClientPreparer() + async def test_content_multipage_table_span_pdf(self, client): with open(self.multipage_table_pdf, "rb") as fd: myfile = fd.read() poller = await client.begin_recognize_content(myfile) @@ -303,9 +300,8 @@ async def test_content_multipage_table_span_pdf(self, resource_group, location, self.assertFormPagesHasValues(result) @GlobalFormRecognizerAccountPreparer() - async def test_content_multipage_table_span_transform(self, resource_group, location, form_recognizer_account, - form_recognizer_account_key): - client = FormRecognizerClient(form_recognizer_account, AzureKeyCredential(form_recognizer_account_key)) + @GlobalClientPreparer() + async def test_content_multipage_table_span_transform(self, client): with open(self.multipage_table_pdf, "rb") as fd: myform = fd.read() diff --git a/sdk/formrecognizer/azure-ai-formrecognizer/tests/test_content_from_url.py b/sdk/formrecognizer/azure-ai-formrecognizer/tests/test_content_from_url.py index 8c45cbe9758a..caeef8752c99 100644 --- a/sdk/formrecognizer/azure-ai-formrecognizer/tests/test_content_from_url.py +++ b/sdk/formrecognizer/azure-ai-formrecognizer/tests/test_content_from_url.py @@ -5,12 +5,17 @@ # ------------------------------------ import pytest +import functools from azure.core.exceptions import HttpResponseError, ServiceRequestError, ClientAuthenticationError from azure.core.credentials import AzureKeyCredential from azure.ai.formrecognizer._generated.models import AnalyzeOperationResult from azure.ai.formrecognizer._response_handlers import prepare_content_result from azure.ai.formrecognizer import FormRecognizerClient from testcase import FormRecognizerTest, GlobalFormRecognizerAccountPreparer +from testcase import GlobalClientPreparer as _GlobalClientPreparer + + +GlobalClientPreparer = functools.partial(_GlobalClientPreparer, FormRecognizerClient) class TestContentFromUrl(FormRecognizerTest): @@ -22,8 +27,8 @@ def test_content_url_bad_endpoint(self, resource_group, location, form_recognize poller = client.begin_recognize_content_from_url(self.invoice_url_pdf) @GlobalFormRecognizerAccountPreparer() - def test_content_url_auth_successful_key(self, resource_group, location, form_recognizer_account, form_recognizer_account_key): - client = FormRecognizerClient(form_recognizer_account, AzureKeyCredential(form_recognizer_account_key)) + @GlobalClientPreparer() + def test_content_url_auth_successful_key(self, client): poller = client.begin_recognize_content_from_url(self.invoice_url_pdf) result = poller.result() @@ -34,23 +39,21 @@ def test_content_url_auth_bad_key(self, resource_group, location, form_recognize poller = client.begin_recognize_content_from_url(self.invoice_url_pdf) @GlobalFormRecognizerAccountPreparer() - def test_content_bad_url(self, resource_group, location, form_recognizer_account, form_recognizer_account_key): - client = FormRecognizerClient(form_recognizer_account, AzureKeyCredential(form_recognizer_account_key)) - + @GlobalClientPreparer() + def test_content_bad_url(self, client): with self.assertRaises(HttpResponseError): poller = client.begin_recognize_content_from_url("https://badurl.jpg") @GlobalFormRecognizerAccountPreparer() - def test_content_url_pass_stream(self, resource_group, location, form_recognizer_account, form_recognizer_account_key): - client = FormRecognizerClient(form_recognizer_account, AzureKeyCredential(form_recognizer_account_key)) + @GlobalClientPreparer() + def test_content_url_pass_stream(self, client): with open(self.receipt_jpg, "rb") as receipt: with self.assertRaises(HttpResponseError): poller = client.begin_recognize_content_from_url(receipt) @GlobalFormRecognizerAccountPreparer() - def test_content_url_transform_pdf(self, resource_group, location, form_recognizer_account, form_recognizer_account_key): - client = FormRecognizerClient(form_recognizer_account, AzureKeyCredential(form_recognizer_account_key)) - + @GlobalClientPreparer() + def test_content_url_transform_pdf(self, client): responses = [] def callback(raw_response, _, headers): @@ -70,10 +73,8 @@ def callback(raw_response, _, headers): self.assertFormPagesTransformCorrect(layout, read_results, page_results) @GlobalFormRecognizerAccountPreparer() - def test_content_url_pdf(self, resource_group, location, form_recognizer_account, form_recognizer_account_key): - client = FormRecognizerClient(form_recognizer_account, - AzureKeyCredential(form_recognizer_account_key)) - + @GlobalClientPreparer() + def test_content_url_pdf(self, client): poller = client.begin_recognize_content_from_url(self.invoice_url_pdf) result = poller.result() self.assertEqual(len(result), 1) @@ -85,9 +86,8 @@ def test_content_url_pdf(self, resource_group, location, form_recognizer_account self.assertEqual(layout.tables[0].page_number, 1) @GlobalFormRecognizerAccountPreparer() - def test_content_url_transform_jpg(self, resource_group, location, form_recognizer_account, form_recognizer_account_key): - client = FormRecognizerClient(form_recognizer_account, AzureKeyCredential(form_recognizer_account_key)) - + @GlobalClientPreparer() + def test_content_url_transform_jpg(self, client): responses = [] def callback(raw_response, _, headers): @@ -107,10 +107,8 @@ def callback(raw_response, _, headers): self.assertFormPagesTransformCorrect(layout, read_results, page_results) @GlobalFormRecognizerAccountPreparer() - def test_content_url_jpg(self, resource_group, location, form_recognizer_account, form_recognizer_account_key): - client = FormRecognizerClient(form_recognizer_account, - AzureKeyCredential(form_recognizer_account_key)) - + @GlobalClientPreparer() + def test_content_url_jpg(self, client): poller = client.begin_recognize_content_from_url(self.form_url_jpg) result = poller.result() self.assertEqual(len(result), 1) @@ -125,8 +123,8 @@ def test_content_url_jpg(self, resource_group, location, form_recognizer_account self.assertEqual(layout.tables[1].page_number, 1) @GlobalFormRecognizerAccountPreparer() - def test_content_multipage_url(self, resource_group, location, form_recognizer_account, form_recognizer_account_key): - client = FormRecognizerClient(form_recognizer_account, AzureKeyCredential(form_recognizer_account_key)) + @GlobalClientPreparer() + def test_content_multipage_url(self, client): poller = client.begin_recognize_content_from_url(self.multipage_url_pdf) result = poller.result() @@ -134,8 +132,8 @@ def test_content_multipage_url(self, resource_group, location, form_recognizer_a self.assertFormPagesHasValues(result) @GlobalFormRecognizerAccountPreparer() - def test_content_multipage_transform_url(self, resource_group, location, form_recognizer_account, form_recognizer_account_key): - client = FormRecognizerClient(form_recognizer_account, AzureKeyCredential(form_recognizer_account_key)) + @GlobalClientPreparer() + def test_content_multipage_transform_url(self, client): responses = [] def callback(raw_response, _, headers): @@ -155,10 +153,9 @@ def callback(raw_response, _, headers): self.assertFormPagesTransformCorrect(layout, read_results, page_results) @GlobalFormRecognizerAccountPreparer() + @GlobalClientPreparer() @pytest.mark.live_test_only - def test_content_continuation_token(self, resource_group, location, form_recognizer_account, form_recognizer_account_key): - client = FormRecognizerClient(form_recognizer_account, - AzureKeyCredential(form_recognizer_account_key)) + def test_content_continuation_token(self, client): initial_poller = client.begin_recognize_content_from_url(self.form_url_jpg) cont_token = initial_poller.continuation_token() @@ -168,10 +165,8 @@ def test_content_continuation_token(self, resource_group, location, form_recogni initial_poller.wait() # necessary so azure-devtools doesn't throw assertion error @GlobalFormRecognizerAccountPreparer() - def test_content_multipage_table_span_pdf(self, resource_group, location, form_recognizer_account, form_recognizer_account_key): - client = FormRecognizerClient(form_recognizer_account, - AzureKeyCredential(form_recognizer_account_key)) - + @GlobalClientPreparer() + def test_content_multipage_table_span_pdf(self, client): poller = client.begin_recognize_content_from_url(self.multipage_table_url_pdf) result = poller.result() self.assertEqual(len(result), 2) @@ -193,10 +188,8 @@ def test_content_multipage_table_span_pdf(self, resource_group, location, form_r self.assertFormPagesHasValues(result) @GlobalFormRecognizerAccountPreparer() - def test_content_multipage_table_span_transform(self, resource_group, location, form_recognizer_account, - form_recognizer_account_key): - client = FormRecognizerClient(form_recognizer_account, AzureKeyCredential(form_recognizer_account_key)) - + @GlobalClientPreparer() + def test_content_multipage_table_span_transform(self, client): responses = [] def callback(raw_response, _, headers): diff --git a/sdk/formrecognizer/azure-ai-formrecognizer/tests/test_content_from_url_async.py b/sdk/formrecognizer/azure-ai-formrecognizer/tests/test_content_from_url_async.py index 48536e0072f7..c40afea312f3 100644 --- a/sdk/formrecognizer/azure-ai-formrecognizer/tests/test_content_from_url_async.py +++ b/sdk/formrecognizer/azure-ai-formrecognizer/tests/test_content_from_url_async.py @@ -5,6 +5,7 @@ # ------------------------------------ import pytest +import functools from azure.core.exceptions import HttpResponseError, ServiceRequestError, ClientAuthenticationError from azure.core.credentials import AzureKeyCredential from azure.ai.formrecognizer._generated.models import AnalyzeOperationResult @@ -12,6 +13,10 @@ from azure.ai.formrecognizer.aio import FormRecognizerClient from testcase import GlobalFormRecognizerAccountPreparer from asynctestcase import AsyncFormRecognizerTest +from testcase import GlobalClientPreparer as _GlobalClientPreparer + + +GlobalClientPreparer = functools.partial(_GlobalClientPreparer, FormRecognizerClient) class TestContentFromUrlAsync(AsyncFormRecognizerTest): @@ -24,8 +29,8 @@ async def test_content_url_bad_endpoint(self, resource_group, location, form_rec result = await poller.result() @GlobalFormRecognizerAccountPreparer() - async def test_content_url_auth_successful_key(self, resource_group, location, form_recognizer_account, form_recognizer_account_key): - client = FormRecognizerClient(form_recognizer_account, AzureKeyCredential(form_recognizer_account_key)) + @GlobalClientPreparer() + async def test_content_url_auth_successful_key(self, client): poller = await client.begin_recognize_content_from_url(self.invoice_url_pdf) result = await poller.result() @@ -37,16 +42,15 @@ async def test_content_url_auth_bad_key(self, resource_group, location, form_rec result = await poller.result() @GlobalFormRecognizerAccountPreparer() - async def test_content_bad_url(self, resource_group, location, form_recognizer_account, form_recognizer_account_key): - client = FormRecognizerClient(form_recognizer_account, AzureKeyCredential(form_recognizer_account_key)) - + @GlobalClientPreparer() + async def test_content_bad_url(self, client): with self.assertRaises(HttpResponseError): poller = await client.begin_recognize_content_from_url("https://badurl.jpg") result = await poller.result() @GlobalFormRecognizerAccountPreparer() - async def test_content_url_pass_stream(self, resource_group, location, form_recognizer_account, form_recognizer_account_key): - client = FormRecognizerClient(form_recognizer_account, AzureKeyCredential(form_recognizer_account_key)) + @GlobalClientPreparer() + async def test_content_url_pass_stream(self, client): with open(self.receipt_jpg, "rb") as fd: receipt = fd.read(4) # makes the recording smaller @@ -55,9 +59,8 @@ async def test_content_url_pass_stream(self, resource_group, location, form_reco result = await poller.result() @GlobalFormRecognizerAccountPreparer() - async def test_content_url_transform_pdf(self, resource_group, location, form_recognizer_account, form_recognizer_account_key): - client = FormRecognizerClient(form_recognizer_account, AzureKeyCredential(form_recognizer_account_key)) - + @GlobalClientPreparer() + async def test_content_url_transform_pdf(self, client): responses = [] def callback(raw_response, _, headers): @@ -77,10 +80,8 @@ def callback(raw_response, _, headers): self.assertFormPagesTransformCorrect(layout, read_results, page_results) @GlobalFormRecognizerAccountPreparer() - async def test_content_url_pdf(self, resource_group, location, form_recognizer_account, form_recognizer_account_key): - client = FormRecognizerClient(form_recognizer_account, - AzureKeyCredential(form_recognizer_account_key)) - + @GlobalClientPreparer() + async def test_content_url_pdf(self, client): poller = await client.begin_recognize_content_from_url(self.invoice_url_pdf) result = await poller.result() self.assertEqual(len(result), 1) @@ -92,9 +93,8 @@ async def test_content_url_pdf(self, resource_group, location, form_recognizer_a self.assertEqual(layout.tables[0].page_number, 1) @GlobalFormRecognizerAccountPreparer() - async def test_content_url_transform_jpg(self, resource_group, location, form_recognizer_account, form_recognizer_account_key): - client = FormRecognizerClient(form_recognizer_account, AzureKeyCredential(form_recognizer_account_key)) - + @GlobalClientPreparer() + async def test_content_url_transform_jpg(self, client): responses = [] def callback(raw_response, _, headers): @@ -114,10 +114,8 @@ def callback(raw_response, _, headers): self.assertFormPagesTransformCorrect(layout, read_results, page_results) @GlobalFormRecognizerAccountPreparer() - async def test_content_url_jpg(self, resource_group, location, form_recognizer_account, form_recognizer_account_key): - client = FormRecognizerClient(form_recognizer_account, - AzureKeyCredential(form_recognizer_account_key)) - + @GlobalClientPreparer() + async def test_content_url_jpg(self, client): poller = await client.begin_recognize_content_from_url(self.form_url_jpg) result = await poller.result() self.assertEqual(len(result), 1) @@ -132,16 +130,16 @@ async def test_content_url_jpg(self, resource_group, location, form_recognizer_a self.assertEqual(layout.tables[1].page_number, 1) @GlobalFormRecognizerAccountPreparer() - async def test_content_multipage_url(self, resource_group, location, form_recognizer_account, form_recognizer_account_key): - client = FormRecognizerClient(form_recognizer_account, AzureKeyCredential(form_recognizer_account_key)) + @GlobalClientPreparer() + async def test_content_multipage_url(self, client): poller = await client.begin_recognize_content_from_url(self.multipage_url_pdf) result = await poller.result() self.assertEqual(len(result), 3) self.assertFormPagesHasValues(result) @GlobalFormRecognizerAccountPreparer() - async def test_content_multipage_transform_url(self, resource_group, location, form_recognizer_account, form_recognizer_account_key): - client = FormRecognizerClient(form_recognizer_account, AzureKeyCredential(form_recognizer_account_key)) + @GlobalClientPreparer() + async def test_content_multipage_transform_url(self, client): responses = [] def callback(raw_response, _, headers): @@ -161,10 +159,9 @@ def callback(raw_response, _, headers): self.assertFormPagesTransformCorrect(layout, read_results, page_results) @GlobalFormRecognizerAccountPreparer() + @GlobalClientPreparer() @pytest.mark.live_test_only - async def test_content_continuation_token(self, resource_group, location, form_recognizer_account, form_recognizer_account_key): - client = FormRecognizerClient(form_recognizer_account, - AzureKeyCredential(form_recognizer_account_key)) + async def test_content_continuation_token(self, client): initial_poller = await client.begin_recognize_content_from_url(self.form_url_jpg) cont_token = initial_poller.continuation_token() @@ -174,10 +171,8 @@ async def test_content_continuation_token(self, resource_group, location, form_r await initial_poller.wait() # necessary so azure-devtools doesn't throw assertion error @GlobalFormRecognizerAccountPreparer() - async def test_content_multipage_table_span_pdf(self, resource_group, location, form_recognizer_account, form_recognizer_account_key): - client = FormRecognizerClient(form_recognizer_account, - AzureKeyCredential(form_recognizer_account_key)) - + @GlobalClientPreparer() + async def test_content_multipage_table_span_pdf(self, client): poller = await client.begin_recognize_content_from_url(self.multipage_table_url_pdf) result = await poller.result() self.assertEqual(len(result), 2) @@ -199,10 +194,8 @@ async def test_content_multipage_table_span_pdf(self, resource_group, location, self.assertFormPagesHasValues(result) @GlobalFormRecognizerAccountPreparer() - async def test_content_multipage_table_span_transform(self, resource_group, location, form_recognizer_account, - form_recognizer_account_key): - client = FormRecognizerClient(form_recognizer_account, AzureKeyCredential(form_recognizer_account_key)) - + @GlobalClientPreparer() + async def test_content_multipage_table_span_transform(self, client): responses = [] def callback(raw_response, _, headers): diff --git a/sdk/formrecognizer/azure-ai-formrecognizer/tests/test_copy_model.py b/sdk/formrecognizer/azure-ai-formrecognizer/tests/test_copy_model.py index b5e2cedf9e38..8904bbe29745 100644 --- a/sdk/formrecognizer/azure-ai-formrecognizer/tests/test_copy_model.py +++ b/sdk/formrecognizer/azure-ai-formrecognizer/tests/test_copy_model.py @@ -11,27 +11,28 @@ from azure.ai.formrecognizer import CustomFormModelInfo from azure.ai.formrecognizer import FormTrainingClient from testcase import FormRecognizerTest, GlobalFormRecognizerAccountPreparer -from testcase import GlobalTrainingAccountPreparer as _GlobalTrainingAccountPreparer +from testcase import GlobalClientPreparer as _GlobalClientPreparer -GlobalTrainingAccountPreparer = functools.partial(_GlobalTrainingAccountPreparer, FormTrainingClient) + +GlobalClientPreparer = functools.partial(_GlobalClientPreparer, FormTrainingClient) class TestCopyModel(FormRecognizerTest): @GlobalFormRecognizerAccountPreparer() - @GlobalTrainingAccountPreparer() + @GlobalClientPreparer(training=True) def test_copy_model_none_model_id(self, client, container_sas_url): with self.assertRaises(ValueError): client.begin_copy_model(model_id=None, target={}) @GlobalFormRecognizerAccountPreparer() - @GlobalTrainingAccountPreparer() + @GlobalClientPreparer(training=True) def test_copy_model_empty_model_id(self, client, container_sas_url): with self.assertRaises(ValueError): client.begin_copy_model(model_id="", target={}) @GlobalFormRecognizerAccountPreparer() - @GlobalTrainingAccountPreparer(copy=True) + @GlobalClientPreparer(training=True, copy=True) def test_copy_model_successful(self, client, container_sas_url, location, resource_id): poller = client.begin_training(container_sas_url, use_training_labels=False) @@ -52,7 +53,7 @@ def test_copy_model_successful(self, client, container_sas_url, location, resour self.assertIsNotNone(copied_model) @GlobalFormRecognizerAccountPreparer() - @GlobalTrainingAccountPreparer(copy=True) + @GlobalClientPreparer(training=True, copy=True) def test_copy_model_fail(self, client, container_sas_url, location, resource_id): poller = client.begin_training(container_sas_url, use_training_labels=False) @@ -66,7 +67,7 @@ def test_copy_model_fail(self, client, container_sas_url, location, resource_id) copy = poller.result() @GlobalFormRecognizerAccountPreparer() - @GlobalTrainingAccountPreparer(copy=True) + @GlobalClientPreparer(training=True, copy=True) def test_copy_model_transform(self, client, container_sas_url, location, resource_id): poller = client.begin_training(container_sas_url, use_training_labels=False) @@ -93,7 +94,7 @@ def callback(response, _, headers): self.assertEqual(copy.model_id, target["modelId"]) @GlobalFormRecognizerAccountPreparer() - @GlobalTrainingAccountPreparer(copy=True) + @GlobalClientPreparer(training=True, copy=True) def test_copy_authorization(self, client, container_sas_url, location, resource_id): target = client.get_copy_authorization(resource_region="eastus", resource_id=resource_id) @@ -105,7 +106,7 @@ def test_copy_authorization(self, client, container_sas_url, location, resource_ self.assertEqual(target["resourceId"], resource_id) @GlobalFormRecognizerAccountPreparer() - @GlobalTrainingAccountPreparer(copy=True) + @GlobalClientPreparer(training=True, copy=True) @pytest.mark.live_test_only def test_copy_continuation_token(self, client, container_sas_url, location, resource_id): diff --git a/sdk/formrecognizer/azure-ai-formrecognizer/tests/test_copy_model_async.py b/sdk/formrecognizer/azure-ai-formrecognizer/tests/test_copy_model_async.py index e9c12e069677..18ff025e80de 100644 --- a/sdk/formrecognizer/azure-ai-formrecognizer/tests/test_copy_model_async.py +++ b/sdk/formrecognizer/azure-ai-formrecognizer/tests/test_copy_model_async.py @@ -11,28 +11,29 @@ from azure.ai.formrecognizer import CustomFormModelInfo from azure.ai.formrecognizer.aio import FormTrainingClient from testcase import GlobalFormRecognizerAccountPreparer -from testcase import GlobalTrainingAccountPreparer as _GlobalTrainingAccountPreparer from asynctestcase import AsyncFormRecognizerTest +from testcase import GlobalClientPreparer as _GlobalClientPreparer -GlobalTrainingAccountPreparer = functools.partial(_GlobalTrainingAccountPreparer, FormTrainingClient) + +GlobalClientPreparer = functools.partial(_GlobalClientPreparer, FormTrainingClient) class TestCopyModelAsync(AsyncFormRecognizerTest): @GlobalFormRecognizerAccountPreparer() - @GlobalTrainingAccountPreparer() + @GlobalClientPreparer(training=True) async def test_copy_model_none_model_id(self, client, container_sas_url): with self.assertRaises(ValueError): await client.begin_copy_model(model_id=None, target={}) @GlobalFormRecognizerAccountPreparer() - @GlobalTrainingAccountPreparer() + @GlobalClientPreparer(training=True) async def test_copy_model_empty_model_id(self, client, container_sas_url): with self.assertRaises(ValueError): await client.begin_copy_model(model_id="", target={}) @GlobalFormRecognizerAccountPreparer() - @GlobalTrainingAccountPreparer(copy=True) + @GlobalClientPreparer(training=True, copy=True) async def test_copy_model_successful(self, client, container_sas_url, location, resource_id): training_poller = await client.begin_training(container_sas_url, use_training_labels=False) @@ -53,7 +54,7 @@ async def test_copy_model_successful(self, client, container_sas_url, location, self.assertIsNotNone(copied_model) @GlobalFormRecognizerAccountPreparer() - @GlobalTrainingAccountPreparer(copy=True) + @GlobalClientPreparer(training=True, copy=True) async def test_copy_model_fail(self, client, container_sas_url, location, resource_id): training_poller = await client.begin_training(container_sas_url, use_training_labels=False) @@ -67,7 +68,7 @@ async def test_copy_model_fail(self, client, container_sas_url, location, resour copy = await poller.result() @GlobalFormRecognizerAccountPreparer() - @GlobalTrainingAccountPreparer(copy=True) + @GlobalClientPreparer(training=True, copy=True) async def test_copy_model_transform(self, client, container_sas_url, location, resource_id): training_poller = await client.begin_training(container_sas_url, use_training_labels=False) @@ -94,7 +95,7 @@ def callback(response, _, headers): self.assertEqual(copy.model_id, target["modelId"]) @GlobalFormRecognizerAccountPreparer() - @GlobalTrainingAccountPreparer(copy=True) + @GlobalClientPreparer(training=True, copy=True) async def test_copy_authorization(self, client, container_sas_url, location, resource_id): target = await client.get_copy_authorization(resource_region="eastus", resource_id=resource_id) @@ -106,7 +107,7 @@ async def test_copy_authorization(self, client, container_sas_url, location, res self.assertEqual(target["resourceId"], resource_id) @GlobalFormRecognizerAccountPreparer() - @GlobalTrainingAccountPreparer(copy=True) + @GlobalClientPreparer(training=True, copy=True) @pytest.mark.live_test_only async def test_copy_continuation_token(self, client, container_sas_url, location, resource_id): diff --git a/sdk/formrecognizer/azure-ai-formrecognizer/tests/test_custom_forms.py b/sdk/formrecognizer/azure-ai-formrecognizer/tests/test_custom_forms.py index 9a5d6aab553e..752d24666cf1 100644 --- a/sdk/formrecognizer/azure-ai-formrecognizer/tests/test_custom_forms.py +++ b/sdk/formrecognizer/azure-ai-formrecognizer/tests/test_custom_forms.py @@ -12,10 +12,10 @@ from azure.ai.formrecognizer._generated.models import AnalyzeOperationResult from azure.ai.formrecognizer._response_handlers import prepare_form_result from testcase import FormRecognizerTest, GlobalFormRecognizerAccountPreparer -from testcase import GlobalTrainingAccountPreparer as _GlobalTrainingAccountPreparer +from testcase import GlobalClientPreparer as _GlobalClientPreparer -GlobalTrainingAccountPreparer = functools.partial(_GlobalTrainingAccountPreparer, FormTrainingClient) +GlobalClientPreparer = functools.partial(_GlobalClientPreparer, FormTrainingClient) class TestCustomForms(FormRecognizerTest): @@ -49,14 +49,12 @@ def test_authentication_bad_key(self, resource_group, location, form_recognizer_ @GlobalFormRecognizerAccountPreparer() def test_passing_unsupported_url_content_type(self, resource_group, location, form_recognizer_account, form_recognizer_account_key): client = FormRecognizerClient(form_recognizer_account, AzureKeyCredential(form_recognizer_account_key)) - with self.assertRaises(TypeError): poller = client.begin_recognize_custom_forms(model_id="xx", form="https://badurl.jpg", content_type="application/json") @GlobalFormRecognizerAccountPreparer() def test_auto_detect_unsupported_stream_content(self, resource_group, location, form_recognizer_account, form_recognizer_account_key): client = FormRecognizerClient(form_recognizer_account, AzureKeyCredential(form_recognizer_account_key)) - with open(self.unsupported_content_py, "rb") as fd: myfile = fd.read() @@ -67,7 +65,7 @@ def test_auto_detect_unsupported_stream_content(self, resource_group, location, ) @GlobalFormRecognizerAccountPreparer() - @GlobalTrainingAccountPreparer() + @GlobalClientPreparer(training=True) def test_custom_form_damaged_file(self, client, container_sas_url): fr_client = client.get_form_recognizer_client() @@ -82,7 +80,7 @@ def test_custom_form_damaged_file(self, client, container_sas_url): form = poller.result() @GlobalFormRecognizerAccountPreparer() - @GlobalTrainingAccountPreparer() + @GlobalClientPreparer(training=True) def test_custom_form_unlabeled(self, client, container_sas_url): fr_client = client.get_form_recognizer_client() @@ -107,7 +105,7 @@ def test_custom_form_unlabeled(self, client, container_sas_url): self.assertIsNotNone(field.label_data.text) @GlobalFormRecognizerAccountPreparer() - @GlobalTrainingAccountPreparer(multipage=True) + @GlobalClientPreparer(training=True, multipage=True) def test_custom_form_multipage_unlabeled(self, client, container_sas_url): fr_client = client.get_form_recognizer_client() @@ -135,7 +133,7 @@ def test_custom_form_multipage_unlabeled(self, client, container_sas_url): self.assertIsNotNone(field.label_data.text) @GlobalFormRecognizerAccountPreparer() - @GlobalTrainingAccountPreparer() + @GlobalClientPreparer(training=True) def test_custom_form_labeled(self, client, container_sas_url): fr_client = client.get_form_recognizer_client() @@ -160,7 +158,7 @@ def test_custom_form_labeled(self, client, container_sas_url): self.assertIsNotNone(field.value_data.bounding_box) @GlobalFormRecognizerAccountPreparer() - @GlobalTrainingAccountPreparer(multipage=True) + @GlobalClientPreparer(training=True, multipage=True) def test_custom_form_multipage_labeled(self, client, container_sas_url): fr_client = client.get_form_recognizer_client() @@ -190,7 +188,7 @@ def test_custom_form_multipage_labeled(self, client, container_sas_url): self.assertIsNotNone(field.value_data.bounding_box) @GlobalFormRecognizerAccountPreparer() - @GlobalTrainingAccountPreparer() + @GlobalClientPreparer(training=True) def test_custom_form_unlabeled_transform(self, client, container_sas_url): fr_client = client.get_form_recognizer_client() @@ -227,7 +225,7 @@ def callback(raw_response, _, headers): self.assertUnlabeledFormFieldDictTransformCorrect(recognized_form[0].fields, actual_fields, read_results) @GlobalFormRecognizerAccountPreparer() - @GlobalTrainingAccountPreparer(multipage=True) + @GlobalClientPreparer(training=True, multipage=True) def test_custom_form_multipage_unlabeled_transform(self, client, container_sas_url): fr_client = client.get_form_recognizer_client() @@ -265,7 +263,7 @@ def callback(raw_response, _, headers): self.assertUnlabeledFormFieldDictTransformCorrect(form.fields, actual.key_value_pairs, read_results) @GlobalFormRecognizerAccountPreparer() - @GlobalTrainingAccountPreparer() + @GlobalClientPreparer(training=True) def test_custom_form_labeled_transform(self, client, container_sas_url): fr_client = client.get_form_recognizer_client() @@ -302,7 +300,7 @@ def callback(raw_response, _, headers): self.assertLabeledFormFieldDictTransformCorrect(recognized_form[0].fields, actual_fields, read_results) @GlobalFormRecognizerAccountPreparer() - @GlobalTrainingAccountPreparer(multipage=True) + @GlobalClientPreparer(training=True, multipage=True) def test_custom_form_multipage_labeled_transform(self, client, container_sas_url): fr_client = client.get_form_recognizer_client() @@ -341,7 +339,7 @@ def callback(raw_response, _, headers): self.assertLabeledFormFieldDictTransformCorrect(form.fields, actual.fields, read_results) @GlobalFormRecognizerAccountPreparer() - @GlobalTrainingAccountPreparer() + @GlobalClientPreparer(training=True) @pytest.mark.live_test_only def test_custom_form_continuation_token(self, client, container_sas_url): fr_client = client.get_form_recognizer_client() @@ -366,7 +364,7 @@ def test_custom_form_continuation_token(self, client, container_sas_url): initial_poller.wait() # necessary so azure-devtools doesn't throw assertion error @GlobalFormRecognizerAccountPreparer() - @GlobalTrainingAccountPreparer(multipage2=True) + @GlobalClientPreparer(training=True, multipage2=True) def test_custom_form_multipage_vendor_set_unlabeled_transform(self, client, container_sas_url): fr_client = client.get_form_recognizer_client() @@ -405,7 +403,7 @@ def callback(raw_response, _, headers): self.assertLabeledFormFieldDictTransformCorrect(form.fields, actual.fields, read_results) @GlobalFormRecognizerAccountPreparer() - @GlobalTrainingAccountPreparer(multipage2=True) + @GlobalClientPreparer(training=True, multipage2=True) def test_custom_form_multipage_vendor_set_labeled_transform(self, client, container_sas_url): fr_client = client.get_form_recognizer_client() diff --git a/sdk/formrecognizer/azure-ai-formrecognizer/tests/test_custom_forms_async.py b/sdk/formrecognizer/azure-ai-formrecognizer/tests/test_custom_forms_async.py index 903e35b6c4df..7b08ea902f42 100644 --- a/sdk/formrecognizer/azure-ai-formrecognizer/tests/test_custom_forms_async.py +++ b/sdk/formrecognizer/azure-ai-formrecognizer/tests/test_custom_forms_async.py @@ -13,11 +13,11 @@ from azure.ai.formrecognizer._generated.models import AnalyzeOperationResult from azure.ai.formrecognizer._response_handlers import prepare_form_result from testcase import GlobalFormRecognizerAccountPreparer -from testcase import GlobalTrainingAccountPreparer as _GlobalTrainingAccountPreparer from asynctestcase import AsyncFormRecognizerTest +from testcase import GlobalClientPreparer as _GlobalClientPreparer -GlobalTrainingAccountPreparer = functools.partial(_GlobalTrainingAccountPreparer, FormTrainingClient) +GlobalClientPreparer = functools.partial(_GlobalClientPreparer, FormTrainingClient) class TestCustomFormsAsync(AsyncFormRecognizerTest): @@ -73,7 +73,7 @@ async def test_auto_detect_unsupported_stream_content(self, resource_group, loca result = await poller.result() @GlobalFormRecognizerAccountPreparer() - @GlobalTrainingAccountPreparer() + @GlobalClientPreparer(training=True) async def test_custom_form_damaged_file(self, client, container_sas_url): fr_client = client.get_form_recognizer_client() @@ -88,7 +88,7 @@ async def test_custom_form_damaged_file(self, client, container_sas_url): result = await poller.result() @GlobalFormRecognizerAccountPreparer() - @GlobalTrainingAccountPreparer() + @GlobalClientPreparer(training=True) async def test_custom_form_unlabeled(self, client, container_sas_url): fr_client = client.get_form_recognizer_client() @@ -110,7 +110,7 @@ async def test_custom_form_unlabeled(self, client, container_sas_url): self.assertIsNotNone(field.label_data.text) @GlobalFormRecognizerAccountPreparer() - @GlobalTrainingAccountPreparer(multipage=True) + @GlobalClientPreparer(training=True, multipage=True) async def test_custom_form_multipage_unlabeled(self, client, container_sas_url): fr_client = client.get_form_recognizer_client() @@ -140,7 +140,7 @@ async def test_custom_form_multipage_unlabeled(self, client, container_sas_url): self.assertIsNotNone(field.label_data.text) @GlobalFormRecognizerAccountPreparer() - @GlobalTrainingAccountPreparer() + @GlobalClientPreparer(training=True) async def test_custom_form_labeled(self, client, container_sas_url): fr_client = client.get_form_recognizer_client() @@ -162,7 +162,7 @@ async def test_custom_form_labeled(self, client, container_sas_url): self.assertIsNotNone(field.value_data.bounding_box) @GlobalFormRecognizerAccountPreparer() - @GlobalTrainingAccountPreparer(multipage=True) + @GlobalClientPreparer(training=True, multipage=True) async def test_custom_form_multipage_labeled(self, client, container_sas_url): fr_client = client.get_form_recognizer_client() @@ -193,7 +193,7 @@ async def test_custom_form_multipage_labeled(self, client, container_sas_url): @GlobalFormRecognizerAccountPreparer() - @GlobalTrainingAccountPreparer() + @GlobalClientPreparer(training=True) async def test_form_unlabeled_transform(self, client, container_sas_url): fr_client = client.get_form_recognizer_client() @@ -231,7 +231,7 @@ def callback(raw_response, _, headers): self.assertUnlabeledFormFieldDictTransformCorrect(recognized_form[0].fields, actual_fields, read_results) @GlobalFormRecognizerAccountPreparer() - @GlobalTrainingAccountPreparer(multipage=True) + @GlobalClientPreparer(training=True, multipage=True) async def test_custom_forms_multipage_unlabeled_transform(self, client, container_sas_url): fr_client = client.get_form_recognizer_client() @@ -270,7 +270,7 @@ def callback(raw_response, _, headers): @GlobalFormRecognizerAccountPreparer() - @GlobalTrainingAccountPreparer() + @GlobalClientPreparer(training=True) async def test_form_labeled_transform(self, client, container_sas_url): fr_client = client.get_form_recognizer_client() @@ -308,7 +308,7 @@ def callback(raw_response, _, headers): self.assertLabeledFormFieldDictTransformCorrect(recognized_form[0].fields, actual_fields, read_results) @GlobalFormRecognizerAccountPreparer() - @GlobalTrainingAccountPreparer(multipage=True) + @GlobalClientPreparer(training=True, multipage=True) async def test_custom_forms_multipage_labeled_transform(self, client, container_sas_url): fr_client = client.get_form_recognizer_client() @@ -348,7 +348,7 @@ def callback(raw_response, _, headers): self.assertLabeledFormFieldDictTransformCorrect(form.fields, actual.fields, read_results) @GlobalFormRecognizerAccountPreparer() - @GlobalTrainingAccountPreparer() + @GlobalClientPreparer(training=True) @pytest.mark.live_test_only async def test_custom_form_continuation_token(self, client, container_sas_url): fr_client = client.get_form_recognizer_client() @@ -374,7 +374,7 @@ async def test_custom_form_continuation_token(self, client, container_sas_url): await initial_poller.wait() # necessary so azure-devtools doesn't throw assertion error @GlobalFormRecognizerAccountPreparer() - @GlobalTrainingAccountPreparer(multipage2=True) + @GlobalClientPreparer(training=True, multipage2=True) async def test_custom_form_multipage_vendor_set_unlabeled_transform(self, client, container_sas_url): fr_client = client.get_form_recognizer_client() @@ -413,7 +413,7 @@ def callback(raw_response, _, headers): self.assertLabeledFormFieldDictTransformCorrect(form.fields, actual.fields, read_results) @GlobalFormRecognizerAccountPreparer() - @GlobalTrainingAccountPreparer(multipage2=True) + @GlobalClientPreparer(training=True, multipage2=True) async def test_custom_form_multipage_vendor_set_labeled_transform(self, client, container_sas_url): fr_client = client.get_form_recognizer_client() diff --git a/sdk/formrecognizer/azure-ai-formrecognizer/tests/test_custom_forms_from_url.py b/sdk/formrecognizer/azure-ai-formrecognizer/tests/test_custom_forms_from_url.py index f20b84dd71a3..de974ac79afd 100644 --- a/sdk/formrecognizer/azure-ai-formrecognizer/tests/test_custom_forms_from_url.py +++ b/sdk/formrecognizer/azure-ai-formrecognizer/tests/test_custom_forms_from_url.py @@ -12,10 +12,10 @@ from azure.ai.formrecognizer._generated.models import AnalyzeOperationResult from azure.ai.formrecognizer._response_handlers import prepare_form_result from testcase import FormRecognizerTest, GlobalFormRecognizerAccountPreparer -from testcase import GlobalTrainingAccountPreparer as _GlobalTrainingAccountPreparer +from testcase import GlobalClientPreparer as _GlobalClientPreparer -GlobalTrainingAccountPreparer = functools.partial(_GlobalTrainingAccountPreparer, FormTrainingClient) +GlobalClientPreparer = functools.partial(_GlobalClientPreparer, FormTrainingClient) class TestCustomFormsFromUrl(FormRecognizerTest): @@ -63,7 +63,7 @@ def test_pass_stream_into_url(self, resource_group, location, form_recognizer_ac ) @GlobalFormRecognizerAccountPreparer() - @GlobalTrainingAccountPreparer() + @GlobalClientPreparer(training=True) def test_custom_form_bad_url(self, client, container_sas_url): fr_client = client.get_form_recognizer_client() @@ -78,7 +78,7 @@ def test_custom_form_bad_url(self, client, container_sas_url): form = poller.result() @GlobalFormRecognizerAccountPreparer() - @GlobalTrainingAccountPreparer() + @GlobalClientPreparer(training=True) def test_custom_form_unlabeled(self, client, container_sas_url): fr_client = client.get_form_recognizer_client() @@ -98,7 +98,7 @@ def test_custom_form_unlabeled(self, client, container_sas_url): self.assertIsNotNone(field.label_data.text) @GlobalFormRecognizerAccountPreparer() - @GlobalTrainingAccountPreparer(multipage=True, blob_sas_url=True) + @GlobalClientPreparer(training=True, multipage=True, blob_sas_url=True) def test_form_multipage_unlabeled(self, client, container_sas_url, blob_sas_url): fr_client = client.get_form_recognizer_client() @@ -124,7 +124,7 @@ def test_form_multipage_unlabeled(self, client, container_sas_url, blob_sas_url) self.assertIsNotNone(field.label_data.text) @GlobalFormRecognizerAccountPreparer() - @GlobalTrainingAccountPreparer() + @GlobalClientPreparer(training=True) def test_custom_form_labeled(self, client, container_sas_url): fr_client = client.get_form_recognizer_client() @@ -143,7 +143,7 @@ def test_custom_form_labeled(self, client, container_sas_url): self.assertIsNotNone(field.value_data.bounding_box) @GlobalFormRecognizerAccountPreparer() - @GlobalTrainingAccountPreparer(multipage=True, blob_sas_url=True) + @GlobalClientPreparer(training=True, multipage=True, blob_sas_url=True) def test_form_multipage_labeled(self, client, container_sas_url, blob_sas_url): fr_client = client.get_form_recognizer_client() @@ -170,7 +170,7 @@ def test_form_multipage_labeled(self, client, container_sas_url, blob_sas_url): @GlobalFormRecognizerAccountPreparer() - @GlobalTrainingAccountPreparer() + @GlobalClientPreparer(training=True) def test_custom_form_unlabeled_transform(self, client, container_sas_url): fr_client = client.get_form_recognizer_client() @@ -204,7 +204,7 @@ def callback(raw_response, _, headers): self.assertUnlabeledFormFieldDictTransformCorrect(recognized_form[0].fields, actual_fields, read_results) @GlobalFormRecognizerAccountPreparer() - @GlobalTrainingAccountPreparer(multipage=True, blob_sas_url=True) + @GlobalClientPreparer(training=True, multipage=True, blob_sas_url=True) def test_custom_form_multipage_unlabeled_transform(self, client, container_sas_url, blob_sas_url): fr_client = client.get_form_recognizer_client() @@ -240,7 +240,7 @@ def callback(raw_response, _, headers): @GlobalFormRecognizerAccountPreparer() - @GlobalTrainingAccountPreparer() + @GlobalClientPreparer(training=True) def test_form_labeled_transform(self, client, container_sas_url): fr_client = client.get_form_recognizer_client() @@ -274,7 +274,7 @@ def callback(raw_response, _, headers): self.assertLabeledFormFieldDictTransformCorrect(recognized_form[0].fields, actual_fields, read_results) @GlobalFormRecognizerAccountPreparer() - @GlobalTrainingAccountPreparer(multipage=True, blob_sas_url=True) + @GlobalClientPreparer(training=True, multipage=True, blob_sas_url=True) def test_custom_form_multipage_labeled_transform(self, client, container_sas_url, blob_sas_url): fr_client = client.get_form_recognizer_client() @@ -310,7 +310,7 @@ def callback(raw_response, _, headers): self.assertLabeledFormFieldDictTransformCorrect(form.fields, actual.fields, read_results) @GlobalFormRecognizerAccountPreparer() - @GlobalTrainingAccountPreparer() + @GlobalClientPreparer(training=True) @pytest.mark.live_test_only def test_custom_form_continuation_token(self, client, container_sas_url): fr_client = client.get_form_recognizer_client() @@ -334,7 +334,7 @@ def test_custom_form_continuation_token(self, client, container_sas_url): initial_poller.wait() # necessary so azure-devtools doesn't throw assertion error @GlobalFormRecognizerAccountPreparer() - @GlobalTrainingAccountPreparer(multipage2=True, blob_sas_url=True) + @GlobalClientPreparer(training=True, multipage2=True, blob_sas_url=True) def test_custom_form_multipage_vendor_set_unlabeled_transform(self, client, container_sas_url, blob_sas_url): fr_client = client.get_form_recognizer_client() @@ -370,7 +370,7 @@ def callback(raw_response, _, headers): self.assertLabeledFormFieldDictTransformCorrect(form.fields, actual.fields, read_results) @GlobalFormRecognizerAccountPreparer() - @GlobalTrainingAccountPreparer(multipage2=True, blob_sas_url=True) + @GlobalClientPreparer(training=True, multipage2=True, blob_sas_url=True) def test_custom_form_multipage_vendor_set_labeled_transform(self, client, container_sas_url, blob_sas_url): fr_client = client.get_form_recognizer_client() diff --git a/sdk/formrecognizer/azure-ai-formrecognizer/tests/test_custom_forms_from_url_async.py b/sdk/formrecognizer/azure-ai-formrecognizer/tests/test_custom_forms_from_url_async.py index 34fc8660cf32..da5a03505a28 100644 --- a/sdk/formrecognizer/azure-ai-formrecognizer/tests/test_custom_forms_from_url_async.py +++ b/sdk/formrecognizer/azure-ai-formrecognizer/tests/test_custom_forms_from_url_async.py @@ -12,10 +12,11 @@ from azure.ai.formrecognizer._generated.models import AnalyzeOperationResult from azure.ai.formrecognizer._response_handlers import prepare_form_result from testcase import GlobalFormRecognizerAccountPreparer -from testcase import GlobalTrainingAccountPreparer as _GlobalTrainingAccountPreparer from asynctestcase import AsyncFormRecognizerTest +from testcase import GlobalClientPreparer as _GlobalClientPreparer -GlobalTrainingAccountPreparer = functools.partial(_GlobalTrainingAccountPreparer, FormTrainingClient) + +GlobalClientPreparer = functools.partial(_GlobalClientPreparer, FormTrainingClient) class TestCustomFormsFromUrlAsync(AsyncFormRecognizerTest): @@ -67,7 +68,7 @@ async def test_pass_stream_into_url(self, resource_group, location, form_recogni result = await poller.result() @GlobalFormRecognizerAccountPreparer() - @GlobalTrainingAccountPreparer() + @GlobalClientPreparer(training=True) async def test_form_bad_url(self, client, container_sas_url): fr_client = client.get_form_recognizer_client() @@ -82,7 +83,7 @@ async def test_form_bad_url(self, client, container_sas_url): result = await poller.result() @GlobalFormRecognizerAccountPreparer() - @GlobalTrainingAccountPreparer() + @GlobalClientPreparer(training=True) async def test_form_unlabeled(self, client, container_sas_url): fr_client = client.get_form_recognizer_client() @@ -102,7 +103,7 @@ async def test_form_unlabeled(self, client, container_sas_url): self.assertIsNotNone(field.label_data.text) @GlobalFormRecognizerAccountPreparer() - @GlobalTrainingAccountPreparer(multipage=True, blob_sas_url=True) + @GlobalClientPreparer(training=True, multipage=True, blob_sas_url=True) async def test_custom_form_multipage_unlabeled(self, client, container_sas_url, blob_sas_url): fr_client = client.get_form_recognizer_client() @@ -128,7 +129,7 @@ async def test_custom_form_multipage_unlabeled(self, client, container_sas_url, self.assertIsNotNone(field.label_data.text) @GlobalFormRecognizerAccountPreparer() - @GlobalTrainingAccountPreparer() + @GlobalClientPreparer(training=True) async def test_form_labeled(self, client, container_sas_url): fr_client = client.get_form_recognizer_client() @@ -147,7 +148,7 @@ async def test_form_labeled(self, client, container_sas_url): self.assertIsNotNone(field.value_data.bounding_box) @GlobalFormRecognizerAccountPreparer() - @GlobalTrainingAccountPreparer(multipage=True, blob_sas_url=True) + @GlobalClientPreparer(training=True, multipage=True, blob_sas_url=True) async def test_form_multipage_labeled(self, client, container_sas_url, blob_sas_url): fr_client = client.get_form_recognizer_client() @@ -173,7 +174,7 @@ async def test_form_multipage_labeled(self, client, container_sas_url, blob_sas_ self.assertIsNotNone(field.value_data.bounding_box) @GlobalFormRecognizerAccountPreparer() - @GlobalTrainingAccountPreparer() + @GlobalClientPreparer(training=True) async def test_form_unlabeled_transform(self, client, container_sas_url): fr_client = client.get_form_recognizer_client() @@ -208,7 +209,7 @@ def callback(raw_response, _, headers): self.assertUnlabeledFormFieldDictTransformCorrect(recognized_form[0].fields, actual_fields, read_results) @GlobalFormRecognizerAccountPreparer() - @GlobalTrainingAccountPreparer(multipage=True, blob_sas_url=True) + @GlobalClientPreparer(training=True, multipage=True, blob_sas_url=True) async def test_multipage_unlabeled_transform(self, client, container_sas_url, blob_sas_url): fr_client = client.get_form_recognizer_client() @@ -244,7 +245,7 @@ def callback(raw_response, _, headers): self.assertUnlabeledFormFieldDictTransformCorrect(form.fields, actual.key_value_pairs, read_results) @GlobalFormRecognizerAccountPreparer() - @GlobalTrainingAccountPreparer() + @GlobalClientPreparer(training=True) async def test_form_labeled_transform(self, client, container_sas_url): fr_client = client.get_form_recognizer_client() @@ -279,7 +280,7 @@ def callback(raw_response, _, headers): self.assertLabeledFormFieldDictTransformCorrect(recognized_form[0].fields, actual_fields, read_results) @GlobalFormRecognizerAccountPreparer() - @GlobalTrainingAccountPreparer(multipage=True, blob_sas_url=True) + @GlobalClientPreparer(training=True, multipage=True, blob_sas_url=True) async def test_multipage_labeled_transform(self, client, container_sas_url, blob_sas_url): fr_client = client.get_form_recognizer_client() @@ -316,7 +317,7 @@ def callback(raw_response, _, headers): self.assertLabeledFormFieldDictTransformCorrect(form.fields, actual.fields, read_results) @GlobalFormRecognizerAccountPreparer() - @GlobalTrainingAccountPreparer() + @GlobalClientPreparer(training=True) @pytest.mark.live_test_only async def test_custom_form_continuation_token(self, client, container_sas_url): fr_client = client.get_form_recognizer_client() @@ -339,7 +340,7 @@ async def test_custom_form_continuation_token(self, client, container_sas_url): await initial_poller.wait() # necessary so azure-devtools doesn't throw assertion error @GlobalFormRecognizerAccountPreparer() - @GlobalTrainingAccountPreparer(multipage2=True, blob_sas_url=True) + @GlobalClientPreparer(training=True, multipage2=True, blob_sas_url=True) async def test_custom_form_multipage_vendor_set_unlabeled_transform(self, client, container_sas_url, blob_sas_url): fr_client = client.get_form_recognizer_client() @@ -375,7 +376,7 @@ def callback(raw_response, _, headers): self.assertLabeledFormFieldDictTransformCorrect(form.fields, actual.fields, read_results) @GlobalFormRecognizerAccountPreparer() - @GlobalTrainingAccountPreparer(multipage2=True, blob_sas_url=True) + @GlobalClientPreparer(training=True, multipage2=True, blob_sas_url=True) async def test_custom_form_multipage_vendor_set_labeled_transform(self, client, container_sas_url, blob_sas_url): fr_client = client.get_form_recognizer_client() diff --git a/sdk/formrecognizer/azure-ai-formrecognizer/tests/test_mgmt.py b/sdk/formrecognizer/azure-ai-formrecognizer/tests/test_mgmt.py index 0de73c057d2d..668f8080e82f 100644 --- a/sdk/formrecognizer/azure-ai-formrecognizer/tests/test_mgmt.py +++ b/sdk/formrecognizer/azure-ai-formrecognizer/tests/test_mgmt.py @@ -8,12 +8,12 @@ from azure.core.credentials import AzureKeyCredential from azure.core.exceptions import ResourceNotFoundError, ClientAuthenticationError from azure.core.pipeline.transport import RequestsTransport -from azure.ai.formrecognizer import FormTrainingClient, FormRecognizerClient +from azure.ai.formrecognizer import FormTrainingClient from testcase import FormRecognizerTest, GlobalFormRecognizerAccountPreparer -from testcase import GlobalTrainingAccountPreparer as _GlobalTrainingAccountPreparer +from testcase import GlobalClientPreparer as _GlobalClientPreparer -GlobalTrainingAccountPreparer = functools.partial(_GlobalTrainingAccountPreparer, FormTrainingClient) +GlobalClientPreparer = functools.partial(_GlobalClientPreparer, FormTrainingClient) class TestManagement(FormRecognizerTest): @@ -31,14 +31,14 @@ def test_get_model_auth_bad_key(self, resource_group, location, form_recognizer_ result = client.get_custom_model("xx") @GlobalFormRecognizerAccountPreparer() - def test_get_model_empty_model_id(self, resource_group, location, form_recognizer_account, form_recognizer_account_key): - client = FormTrainingClient(form_recognizer_account, AzureKeyCredential(form_recognizer_account_key)) + @GlobalClientPreparer() + def test_get_model_empty_model_id(self, client): with self.assertRaises(ValueError): result = client.get_custom_model("") @GlobalFormRecognizerAccountPreparer() - def test_get_model_none_model_id(self, resource_group, location, form_recognizer_account, form_recognizer_account_key): - client = FormTrainingClient(form_recognizer_account, AzureKeyCredential(form_recognizer_account_key)) + @GlobalClientPreparer() + def test_get_model_none_model_id(self, client): with self.assertRaises(ValueError): result = client.get_custom_model(None) @@ -57,27 +57,27 @@ def test_delete_model_auth_bad_key(self, resource_group, location, form_recogniz client.delete_model("xx") @GlobalFormRecognizerAccountPreparer() - def test_delete_model_none_model_id(self, resource_group, location, form_recognizer_account, form_recognizer_account_key): - client = FormTrainingClient(form_recognizer_account, AzureKeyCredential(form_recognizer_account_key)) + @GlobalClientPreparer() + def test_delete_model_none_model_id(self, client): with self.assertRaises(ValueError): result = client.delete_model(None) @GlobalFormRecognizerAccountPreparer() - def test_delete_model_empty_model_id(self, resource_group, location, form_recognizer_account, form_recognizer_account_key): - client = FormTrainingClient(form_recognizer_account, AzureKeyCredential(form_recognizer_account_key)) + @GlobalClientPreparer() + def test_delete_model_empty_model_id(self, client): with self.assertRaises(ValueError): result = client.delete_model("") @GlobalFormRecognizerAccountPreparer() - def test_account_properties(self, resource_group, location, form_recognizer_account, form_recognizer_account_key): - client = FormTrainingClient(form_recognizer_account, AzureKeyCredential(form_recognizer_account_key)) + @GlobalClientPreparer() + def test_account_properties(self, client): properties = client.get_account_properties() self.assertIsNotNone(properties.custom_model_limit) self.assertIsNotNone(properties.custom_model_count) @GlobalFormRecognizerAccountPreparer() - @GlobalTrainingAccountPreparer() + @GlobalClientPreparer(training=True) def test_mgmt_model_labeled(self, client, container_sas_url): poller = client.begin_training(container_sas_url, use_training_labels=True) @@ -113,7 +113,7 @@ def test_mgmt_model_labeled(self, client, container_sas_url): client.get_custom_model(labeled_model_from_train.model_id) @GlobalFormRecognizerAccountPreparer() - @GlobalTrainingAccountPreparer() + @GlobalClientPreparer(training=True) def test_mgmt_model_unlabeled(self, client, container_sas_url): poller = client.begin_training(container_sas_url, use_training_labels=False) diff --git a/sdk/formrecognizer/azure-ai-formrecognizer/tests/test_mgmt_async.py b/sdk/formrecognizer/azure-ai-formrecognizer/tests/test_mgmt_async.py index e2d03690e520..013552bc98d4 100644 --- a/sdk/formrecognizer/azure-ai-formrecognizer/tests/test_mgmt_async.py +++ b/sdk/formrecognizer/azure-ai-formrecognizer/tests/test_mgmt_async.py @@ -9,13 +9,13 @@ from azure.core.pipeline.transport import AioHttpTransport from azure.core.credentials import AzureKeyCredential from azure.core.exceptions import ResourceNotFoundError, ClientAuthenticationError -from azure.ai.formrecognizer.aio import FormTrainingClient, FormRecognizerClient +from azure.ai.formrecognizer.aio import FormTrainingClient from testcase import FormRecognizerTest, GlobalFormRecognizerAccountPreparer -from testcase import GlobalTrainingAccountPreparer as _GlobalTrainingAccountPreparer from asynctestcase import AsyncFormRecognizerTest +from testcase import GlobalClientPreparer as _GlobalClientPreparer -GlobalTrainingAccountPreparer = functools.partial(_GlobalTrainingAccountPreparer, FormTrainingClient) +GlobalClientPreparer = functools.partial(_GlobalClientPreparer, FormTrainingClient) class TestManagementAsync(AsyncFormRecognizerTest): @@ -42,14 +42,14 @@ async def test_get_model_auth_bad_key(self, resource_group, location, form_recog result = await client.get_custom_model("xx") @GlobalFormRecognizerAccountPreparer() - async def test_get_model_empty_model_id(self, resource_group, location, form_recognizer_account, form_recognizer_account_key): - client = FormTrainingClient(form_recognizer_account, AzureKeyCredential(form_recognizer_account_key)) + @GlobalClientPreparer() + async def test_get_model_empty_model_id(self, client): with self.assertRaises(ValueError): result = await client.get_custom_model("") @GlobalFormRecognizerAccountPreparer() - async def test_get_model_none_model_id(self, resource_group, location, form_recognizer_account, form_recognizer_account_key): - client = FormTrainingClient(form_recognizer_account, AzureKeyCredential(form_recognizer_account_key)) + @GlobalClientPreparer() + async def test_get_model_none_model_id(self, client): with self.assertRaises(ValueError): result = await client.get_custom_model(None) @@ -68,27 +68,27 @@ async def test_delete_model_auth_bad_key(self, resource_group, location, form_re result = await client.delete_model("xx") @GlobalFormRecognizerAccountPreparer() - async def test_delete_model_none_model_id(self, resource_group, location, form_recognizer_account, form_recognizer_account_key): - client = FormTrainingClient(form_recognizer_account, AzureKeyCredential(form_recognizer_account_key)) + @GlobalClientPreparer() + async def test_delete_model_none_model_id(self, client): with self.assertRaises(ValueError): result = await client.delete_model(None) @GlobalFormRecognizerAccountPreparer() - async def test_delete_model_empty_model_id(self, resource_group, location, form_recognizer_account, form_recognizer_account_key): - client = FormTrainingClient(form_recognizer_account, AzureKeyCredential(form_recognizer_account_key)) + @GlobalClientPreparer() + async def test_delete_model_empty_model_id(self, client): with self.assertRaises(ValueError): result = await client.delete_model("") @GlobalFormRecognizerAccountPreparer() - async def test_account_properties(self, resource_group, location, form_recognizer_account, form_recognizer_account_key): - client = FormTrainingClient(form_recognizer_account, AzureKeyCredential(form_recognizer_account_key)) + @GlobalClientPreparer() + async def test_account_properties(self, client): properties = await client.get_account_properties() self.assertIsNotNone(properties.custom_model_limit) self.assertIsNotNone(properties.custom_model_count) @GlobalFormRecognizerAccountPreparer() - @GlobalTrainingAccountPreparer() + @GlobalClientPreparer(training=True) async def test_mgmt_model_labeled(self, client, container_sas_url): poller = await client.begin_training(container_sas_url, use_training_labels=True) @@ -123,7 +123,7 @@ async def test_mgmt_model_labeled(self, client, container_sas_url): await client.get_custom_model(labeled_model_from_train.model_id) @GlobalFormRecognizerAccountPreparer() - @GlobalTrainingAccountPreparer() + @GlobalClientPreparer(training=True) async def test_mgmt_model_unlabeled(self, client, container_sas_url): poller = await client.begin_training(container_sas_url, use_training_labels=False) unlabeled_model_from_train = await poller.result() diff --git a/sdk/formrecognizer/azure-ai-formrecognizer/tests/test_receipt.py b/sdk/formrecognizer/azure-ai-formrecognizer/tests/test_receipt.py index 4e5c0d2f6579..8f3f56f1d6b5 100644 --- a/sdk/formrecognizer/azure-ai-formrecognizer/tests/test_receipt.py +++ b/sdk/formrecognizer/azure-ai-formrecognizer/tests/test_receipt.py @@ -5,6 +5,7 @@ # ------------------------------------ import pytest +import functools from io import BytesIO from datetime import date, time from azure.core.exceptions import ClientAuthenticationError, ServiceRequestError, HttpResponseError @@ -13,6 +14,10 @@ from azure.ai.formrecognizer._response_handlers import prepare_receipt from azure.ai.formrecognizer import FormRecognizerClient, FormContentType from testcase import FormRecognizerTest, GlobalFormRecognizerAccountPreparer +from testcase import GlobalClientPreparer as _GlobalClientPreparer + + +GlobalClientPreparer = functools.partial(_GlobalClientPreparer, FormRecognizerClient) class TestReceiptFromStream(FormRecognizerTest): @@ -26,8 +31,8 @@ def test_receipt_bad_endpoint(self, resource_group, location, form_recognizer_ac poller = client.begin_recognize_receipts(myfile) @GlobalFormRecognizerAccountPreparer() - def test_authentication_successful_key(self, resource_group, location, form_recognizer_account, form_recognizer_account_key): - client = FormRecognizerClient(form_recognizer_account, AzureKeyCredential(form_recognizer_account_key)) + @GlobalClientPreparer() + def test_authentication_successful_key(self, client): with open(self.receipt_jpg, "rb") as fd: myfile = fd.read() poller = client.begin_recognize_receipts(myfile) @@ -40,8 +45,8 @@ def test_authentication_bad_key(self, resource_group, location, form_recognizer_ poller = client.begin_recognize_receipts(b"xx", content_type="image/jpeg") @GlobalFormRecognizerAccountPreparer() - def test_passing_enum_content_type(self, resource_group, location, form_recognizer_account, form_recognizer_account_key): - client = FormRecognizerClient(form_recognizer_account, AzureKeyCredential(form_recognizer_account_key)) + @GlobalClientPreparer() + def test_passing_enum_content_type(self, client): with open(self.receipt_png, "rb") as fd: myfile = fd.read() poller = client.begin_recognize_receipts( @@ -52,56 +57,56 @@ def test_passing_enum_content_type(self, resource_group, location, form_recogniz self.assertIsNotNone(result) @GlobalFormRecognizerAccountPreparer() - def test_damaged_file_passed_as_bytes(self, resource_group, location, form_recognizer_account, form_recognizer_account_key): - client = FormRecognizerClient(form_recognizer_account, AzureKeyCredential(form_recognizer_account_key)) + @GlobalClientPreparer() + def test_damaged_file_passed_as_bytes(self, client): damaged_pdf = b"\x25\x50\x44\x46\x55\x55\x55" # still has correct bytes to be recognized as PDF with self.assertRaises(HttpResponseError): poller = client.begin_recognize_receipts( - damaged_pdf, + damaged_pdf ) @GlobalFormRecognizerAccountPreparer() - def test_damaged_file_bytes_fails_autodetect_content_type(self, resource_group, location, form_recognizer_account, form_recognizer_account_key): - client = FormRecognizerClient(form_recognizer_account, AzureKeyCredential(form_recognizer_account_key)) + @GlobalClientPreparer() + def test_damaged_file_bytes_fails_autodetect_content_type(self, client): damaged_pdf = b"\x50\x44\x46\x55\x55\x55" # doesn't match any magic file numbers with self.assertRaises(ValueError): poller = client.begin_recognize_receipts( - damaged_pdf, + damaged_pdf ) @GlobalFormRecognizerAccountPreparer() - def test_damaged_file_passed_as_bytes_io(self, resource_group, location, form_recognizer_account, form_recognizer_account_key): - client = FormRecognizerClient(form_recognizer_account, AzureKeyCredential(form_recognizer_account_key)) + @GlobalClientPreparer() + def test_damaged_file_passed_as_bytes_io(self, client): damaged_pdf = BytesIO(b"\x25\x50\x44\x46\x55\x55\x55") # still has correct bytes to be recognized as PDF with self.assertRaises(HttpResponseError): poller = client.begin_recognize_receipts( - damaged_pdf, + damaged_pdf ) @GlobalFormRecognizerAccountPreparer() - def test_damaged_file_bytes_io_fails_autodetect(self, resource_group, location, form_recognizer_account, form_recognizer_account_key): - client = FormRecognizerClient(form_recognizer_account, AzureKeyCredential(form_recognizer_account_key)) + @GlobalClientPreparer() + def test_damaged_file_bytes_io_fails_autodetect(self, client): damaged_pdf = BytesIO(b"\x50\x44\x46\x55\x55\x55") # doesn't match any magic file numbers with self.assertRaises(ValueError): poller = client.begin_recognize_receipts( - damaged_pdf, + damaged_pdf ) @GlobalFormRecognizerAccountPreparer() - def test_blank_page(self, resource_group, location, form_recognizer_account, form_recognizer_account_key): - client = FormRecognizerClient(form_recognizer_account, AzureKeyCredential(form_recognizer_account_key)) + @GlobalClientPreparer() + def test_blank_page(self, client): with open(self.blank_pdf, "rb") as fd: blank = fd.read() poller = client.begin_recognize_receipts( - blank, + blank ) result = poller.result() self.assertIsNotNone(result) @GlobalFormRecognizerAccountPreparer() - def test_passing_bad_content_type_param_passed(self, resource_group, location, form_recognizer_account, form_recognizer_account_key): - client = FormRecognizerClient(form_recognizer_account, AzureKeyCredential(form_recognizer_account_key)) + @GlobalClientPreparer() + def test_passing_bad_content_type_param_passed(self, client): with open(self.receipt_jpg, "rb") as fd: myfile = fd.read() with self.assertRaises(ValueError): @@ -111,28 +116,26 @@ def test_passing_bad_content_type_param_passed(self, resource_group, location, f ) @GlobalFormRecognizerAccountPreparer() - def test_passing_unsupported_url_content_type(self, resource_group, location, form_recognizer_account, form_recognizer_account_key): - client = FormRecognizerClient(form_recognizer_account, AzureKeyCredential(form_recognizer_account_key)) - + @GlobalClientPreparer() + def test_passing_unsupported_url_content_type(self, client): with self.assertRaises(TypeError): poller = client.begin_recognize_receipts("https://badurl.jpg", content_type="application/json") @GlobalFormRecognizerAccountPreparer() - def test_auto_detect_unsupported_stream_content(self, resource_group, location, form_recognizer_account, form_recognizer_account_key): - client = FormRecognizerClient(form_recognizer_account, AzureKeyCredential(form_recognizer_account_key)) + @GlobalClientPreparer() + def test_auto_detect_unsupported_stream_content(self, client): with open(self.unsupported_content_py, "rb") as fd: myfile = fd.read() with self.assertRaises(ValueError): poller = client.begin_recognize_receipts( - myfile, + myfile ) @GlobalFormRecognizerAccountPreparer() - def test_receipt_stream_transform_png(self, resource_group, location, form_recognizer_account, form_recognizer_account_key): - client = FormRecognizerClient(form_recognizer_account, AzureKeyCredential(form_recognizer_account_key)) - + @GlobalClientPreparer() + def test_receipt_stream_transform_png(self, client): responses = [] def callback(raw_response, _, headers): @@ -185,9 +188,8 @@ def callback(raw_response, _, headers): self.assertFormPagesTransformCorrect(receipt.pages, read_results) @GlobalFormRecognizerAccountPreparer() - def test_receipt_stream_transform_jpg(self, resource_group, location, form_recognizer_account, form_recognizer_account_key): - client = FormRecognizerClient(form_recognizer_account, AzureKeyCredential(form_recognizer_account_key)) - + @GlobalClientPreparer() + def test_receipt_stream_transform_jpg(self, client): responses = [] def callback(raw_response, _, headers): @@ -241,8 +243,8 @@ def callback(raw_response, _, headers): self.assertFormPagesTransformCorrect(receipt.pages, read_results) @GlobalFormRecognizerAccountPreparer() - def test_receipt_jpg(self, resource_group, location, form_recognizer_account, form_recognizer_account_key): - client = FormRecognizerClient(form_recognizer_account, AzureKeyCredential(form_recognizer_account_key)) + @GlobalClientPreparer() + def test_receipt_jpg(self, client): with open(self.receipt_jpg, "rb") as fd: receipt = fd.read() @@ -270,8 +272,8 @@ def test_receipt_jpg(self, resource_group, location, form_recognizer_account, fo self.assertReceiptItemsHasValues(receipt.fields['Items'].value, receipt.page_range.first_page_number, False) @GlobalFormRecognizerAccountPreparer() - def test_receipt_png(self, resource_group, location, form_recognizer_account, form_recognizer_account_key): - client = FormRecognizerClient(form_recognizer_account, AzureKeyCredential(form_recognizer_account_key)) + @GlobalClientPreparer() + def test_receipt_png(self, client): with open(self.receipt_png, "rb") as stream: poller = client.begin_recognize_receipts(stream) @@ -294,8 +296,8 @@ def test_receipt_png(self, resource_group, location, form_recognizer_account, fo self.assertEqual(receipt_type.value, 'Itemized') @GlobalFormRecognizerAccountPreparer() - def test_receipt_jpg_include_text_content(self, resource_group, location, form_recognizer_account, form_recognizer_account_key): - client = FormRecognizerClient(form_recognizer_account, AzureKeyCredential(form_recognizer_account_key)) + @GlobalClientPreparer() + def test_receipt_jpg_include_text_content(self, client): with open(self.receipt_jpg, "rb") as fd: receipt = fd.read() poller = client.begin_recognize_receipts(receipt, include_text_content=True) @@ -314,8 +316,9 @@ def test_receipt_jpg_include_text_content(self, resource_group, location, form_r self.assertTextContentHasValues(value.value_data.text_content, receipt.page_range.first_page_number) @GlobalFormRecognizerAccountPreparer() - def test_receipt_multipage(self, resource_group, location, form_recognizer_account, form_recognizer_account_key): - client = FormRecognizerClient(form_recognizer_account, AzureKeyCredential(form_recognizer_account_key)) + @GlobalClientPreparer() + def test_receipt_multipage(self, client): + with open(self.multipage_invoice_pdf, "rb") as fd: receipt = fd.read() poller = client.begin_recognize_receipts(receipt, include_text_content=True) @@ -348,8 +351,8 @@ def test_receipt_multipage(self, resource_group, location, form_recognizer_accou self.assertEqual(receipt_type.value, 'Itemized') @GlobalFormRecognizerAccountPreparer() - def test_receipt_multipage_transform(self, resource_group, location, form_recognizer_account, form_recognizer_account_key): - client = FormRecognizerClient(form_recognizer_account, AzureKeyCredential(form_recognizer_account_key)) + @GlobalClientPreparer() + def test_receipt_multipage_transform(self, client): responses = [] @@ -408,9 +411,9 @@ def callback(raw_response, _, headers): self.assertFormPagesTransformCorrect(returned_model, read_results) @GlobalFormRecognizerAccountPreparer() + @GlobalClientPreparer() @pytest.mark.live_test_only - def test_receipt_continuation_token(self, resource_group, location, form_recognizer_account, form_recognizer_account_key): - client = FormRecognizerClient(form_recognizer_account, AzureKeyCredential(form_recognizer_account_key)) + def test_receipt_continuation_token(self, client): with open(self.receipt_jpg, "rb") as fd: receipt = fd.read() diff --git a/sdk/formrecognizer/azure-ai-formrecognizer/tests/test_receipt_async.py b/sdk/formrecognizer/azure-ai-formrecognizer/tests/test_receipt_async.py index b30744a32ab2..07c9c099b473 100644 --- a/sdk/formrecognizer/azure-ai-formrecognizer/tests/test_receipt_async.py +++ b/sdk/formrecognizer/azure-ai-formrecognizer/tests/test_receipt_async.py @@ -5,6 +5,7 @@ # ------------------------------------ import pytest +import functools from io import BytesIO from datetime import date, time from azure.core.exceptions import ServiceRequestError, ClientAuthenticationError, HttpResponseError @@ -15,6 +16,10 @@ from azure.ai.formrecognizer import FormContentType from testcase import GlobalFormRecognizerAccountPreparer from asynctestcase import AsyncFormRecognizerTest +from testcase import GlobalClientPreparer as _GlobalClientPreparer + + +GlobalClientPreparer = functools.partial(_GlobalClientPreparer, FormRecognizerClient) class TestReceiptFromStreamAsync(AsyncFormRecognizerTest): @@ -29,8 +34,8 @@ async def test_receipt_bad_endpoint(self, resource_group, location, form_recogni result = await poller.result() @GlobalFormRecognizerAccountPreparer() - async def test_authentication_successful_key(self, resource_group, location, form_recognizer_account, form_recognizer_account_key): - client = FormRecognizerClient(form_recognizer_account, AzureKeyCredential(form_recognizer_account_key)) + @GlobalClientPreparer() + async def test_authentication_successful_key(self, client): with open(self.receipt_jpg, "rb") as fd: myfile = fd.read() poller = await client.begin_recognize_receipts(myfile) @@ -44,8 +49,8 @@ async def test_authentication_bad_key(self, resource_group, location, form_recog result = await poller.result() @GlobalFormRecognizerAccountPreparer() - async def test_passing_enum_content_type(self, resource_group, location, form_recognizer_account, form_recognizer_account_key): - client = FormRecognizerClient(form_recognizer_account, AzureKeyCredential(form_recognizer_account_key)) + @GlobalClientPreparer() + async def test_passing_enum_content_type(self, client): with open(self.receipt_png, "rb") as fd: myfile = fd.read() poller = await client.begin_recognize_receipts( @@ -56,8 +61,8 @@ async def test_passing_enum_content_type(self, resource_group, location, form_re self.assertIsNotNone(result) @GlobalFormRecognizerAccountPreparer() - async def test_damaged_file_passed_as_bytes(self, resource_group, location, form_recognizer_account, form_recognizer_account_key): - client = FormRecognizerClient(form_recognizer_account, AzureKeyCredential(form_recognizer_account_key)) + @GlobalClientPreparer() + async def test_damaged_file_passed_as_bytes(self, client): damaged_pdf = b"\x25\x50\x44\x46\x55\x55\x55" # still has correct bytes to be recognized as PDF with self.assertRaises(HttpResponseError): poller = await client.begin_recognize_receipts( @@ -66,8 +71,8 @@ async def test_damaged_file_passed_as_bytes(self, resource_group, location, form result = await poller.result() @GlobalFormRecognizerAccountPreparer() - async def test_damaged_file_bytes_fails_autodetect_content_type(self, resource_group, location, form_recognizer_account, form_recognizer_account_key): - client = FormRecognizerClient(form_recognizer_account, AzureKeyCredential(form_recognizer_account_key)) + @GlobalClientPreparer() + async def test_damaged_file_bytes_fails_autodetect_content_type(self, client): damaged_pdf = b"\x50\x44\x46\x55\x55\x55" # doesn't match any magic file numbers with self.assertRaises(ValueError): poller = await client.begin_recognize_receipts( @@ -76,8 +81,8 @@ async def test_damaged_file_bytes_fails_autodetect_content_type(self, resource_g result = await poller.result() @GlobalFormRecognizerAccountPreparer() - async def test_damaged_file_passed_as_bytes_io(self, resource_group, location, form_recognizer_account, form_recognizer_account_key): - client = FormRecognizerClient(form_recognizer_account, AzureKeyCredential(form_recognizer_account_key)) + @GlobalClientPreparer() + async def test_damaged_file_passed_as_bytes_io(self, client): damaged_pdf = BytesIO(b"\x25\x50\x44\x46\x55\x55\x55") # still has correct bytes to be recognized as PDF with self.assertRaises(HttpResponseError): poller = await client.begin_recognize_receipts( @@ -96,8 +101,8 @@ async def test_damaged_file_bytes_io_fails_autodetect(self, resource_group, loca result = await poller.result() @GlobalFormRecognizerAccountPreparer() - async def test_blank_page(self, resource_group, location, form_recognizer_account, form_recognizer_account_key): - client = FormRecognizerClient(form_recognizer_account, AzureKeyCredential(form_recognizer_account_key)) + @GlobalClientPreparer() + async def test_blank_page(self, client): with open(self.blank_pdf, "rb") as fd: blank = fd.read() @@ -108,8 +113,8 @@ async def test_blank_page(self, resource_group, location, form_recognizer_accoun self.assertIsNotNone(result) @GlobalFormRecognizerAccountPreparer() - async def test_passing_bad_content_type_param_passed(self, resource_group, location, form_recognizer_account, form_recognizer_account_key): - client = FormRecognizerClient(form_recognizer_account, AzureKeyCredential(form_recognizer_account_key)) + @GlobalClientPreparer() + async def test_passing_bad_content_type_param_passed(self, client): with open(self.receipt_jpg, "rb") as fd: myfile = fd.read() with self.assertRaises(ValueError): @@ -120,17 +125,15 @@ async def test_passing_bad_content_type_param_passed(self, resource_group, locat result = await poller.result() @GlobalFormRecognizerAccountPreparer() - async def test_passing_unsupported_url_content_type(self, resource_group, location, form_recognizer_account, form_recognizer_account_key): - client = FormRecognizerClient(form_recognizer_account, AzureKeyCredential(form_recognizer_account_key)) - + @GlobalClientPreparer() + async def test_passing_unsupported_url_content_type(self, client): with self.assertRaises(TypeError): poller = await client.begin_recognize_receipts("https://badurl.jpg", content_type="application/json") result = await poller.result() @GlobalFormRecognizerAccountPreparer() - async def test_auto_detect_unsupported_stream_content(self, resource_group, location, form_recognizer_account, form_recognizer_account_key): - client = FormRecognizerClient(form_recognizer_account, AzureKeyCredential(form_recognizer_account_key)) - + @GlobalClientPreparer() + async def test_auto_detect_unsupported_stream_content(self, client): with open(self.unsupported_content_py, "rb") as fd: myfile = fd.read() @@ -141,8 +144,8 @@ async def test_auto_detect_unsupported_stream_content(self, resource_group, loca result = await poller.result() @GlobalFormRecognizerAccountPreparer() - async def test_receipt_stream_transform_png(self, resource_group, location, form_recognizer_account, form_recognizer_account_key): - client = FormRecognizerClient(form_recognizer_account, AzureKeyCredential(form_recognizer_account_key)) + @GlobalClientPreparer() + async def test_receipt_stream_transform_png(self, client): responses = [] @@ -196,9 +199,8 @@ def callback(raw_response, _, headers): self.assertFormPagesTransformCorrect(receipt.pages, read_results) @GlobalFormRecognizerAccountPreparer() - async def test_receipt_stream_transform_jpg(self, resource_group, location, form_recognizer_account, form_recognizer_account_key): - client = FormRecognizerClient(form_recognizer_account, AzureKeyCredential(form_recognizer_account_key)) - + @GlobalClientPreparer() + async def test_receipt_stream_transform_jpg(self, client): responses = [] def callback(raw_response, _, headers): @@ -252,8 +254,8 @@ def callback(raw_response, _, headers): self.assertFormPagesTransformCorrect(receipt.pages, read_results) @GlobalFormRecognizerAccountPreparer() - async def test_receipt_jpg(self, resource_group, location, form_recognizer_account, form_recognizer_account_key): - client = FormRecognizerClient(form_recognizer_account, AzureKeyCredential(form_recognizer_account_key)) + @GlobalClientPreparer() + async def test_receipt_jpg(self, client): with open(self.receipt_jpg, "rb") as fd: receipt = fd.read() @@ -281,9 +283,8 @@ async def test_receipt_jpg(self, resource_group, location, form_recognizer_accou self.assertReceiptItemsHasValues(receipt.fields["Items"].value, receipt.page_range.first_page_number, False) @GlobalFormRecognizerAccountPreparer() - async def test_receipt_png(self, resource_group, location, form_recognizer_account, form_recognizer_account_key): - client = FormRecognizerClient(form_recognizer_account, AzureKeyCredential(form_recognizer_account_key)) - + @GlobalClientPreparer() + async def test_receipt_png(self, client): with open(self.receipt_png, "rb") as fd: receipt = fd.read() @@ -306,8 +307,8 @@ async def test_receipt_png(self, resource_group, location, form_recognizer_accou self.assertEqual(receipt_type.value, 'Itemized') @GlobalFormRecognizerAccountPreparer() - async def test_receipt_jpg_include_text_content(self, resource_group, location, form_recognizer_account, form_recognizer_account_key): - client = FormRecognizerClient(form_recognizer_account, AzureKeyCredential(form_recognizer_account_key)) + @GlobalClientPreparer() + async def test_receipt_jpg_include_text_content(self, client): with open(self.receipt_jpg, "rb") as fd: receipt = fd.read() poller = await client.begin_recognize_receipts(receipt, include_text_content=True) @@ -326,8 +327,8 @@ async def test_receipt_jpg_include_text_content(self, resource_group, location, self.assertTextContentHasValues(value.value_data.text_content, receipt.page_range.first_page_number) @GlobalFormRecognizerAccountPreparer() - async def test_receipt_multipage(self, resource_group, location, form_recognizer_account, form_recognizer_account_key): - client = FormRecognizerClient(form_recognizer_account, AzureKeyCredential(form_recognizer_account_key)) + @GlobalClientPreparer() + async def test_receipt_multipage(self, client): with open(self.multipage_invoice_pdf, "rb") as fd: receipt = fd.read() poller = await client.begin_recognize_receipts(receipt, include_text_content=True) @@ -360,9 +361,8 @@ async def test_receipt_multipage(self, resource_group, location, form_recognizer self.assertEqual(receipt_type.value, 'Itemized') @GlobalFormRecognizerAccountPreparer() - async def test_receipt_multipage_transform(self, resource_group, location, form_recognizer_account, form_recognizer_account_key): - client = FormRecognizerClient(form_recognizer_account, AzureKeyCredential(form_recognizer_account_key)) - + @GlobalClientPreparer() + async def test_receipt_multipage_transform(self, client): responses = [] def callback(raw_response, _, headers): @@ -420,9 +420,9 @@ def callback(raw_response, _, headers): self.assertFormPagesTransformCorrect(returned_model, read_results) @GlobalFormRecognizerAccountPreparer() + @GlobalClientPreparer() @pytest.mark.live_test_only - async def test_receipt_continuation_token(self, resource_group, location, form_recognizer_account, form_recognizer_account_key): - client = FormRecognizerClient(form_recognizer_account, AzureKeyCredential(form_recognizer_account_key)) + async def test_receipt_continuation_token(self, client): with open(self.receipt_jpg, "rb") as fd: receipt = fd.read() diff --git a/sdk/formrecognizer/azure-ai-formrecognizer/tests/test_receipt_from_url.py b/sdk/formrecognizer/azure-ai-formrecognizer/tests/test_receipt_from_url.py index 31c4924fc5ad..c3f6945d9190 100644 --- a/sdk/formrecognizer/azure-ai-formrecognizer/tests/test_receipt_from_url.py +++ b/sdk/formrecognizer/azure-ai-formrecognizer/tests/test_receipt_from_url.py @@ -5,6 +5,7 @@ # ------------------------------------ import pytest +import functools from datetime import date, time from azure.core.exceptions import HttpResponseError, ServiceRequestError, ClientAuthenticationError from azure.core.credentials import AzureKeyCredential @@ -12,10 +13,26 @@ from azure.ai.formrecognizer._response_handlers import prepare_receipt from azure.ai.formrecognizer import FormRecognizerClient from testcase import FormRecognizerTest, GlobalFormRecognizerAccountPreparer +from testcase import GlobalClientPreparer as _GlobalClientPreparer + + +GlobalClientPreparer = functools.partial(_GlobalClientPreparer, FormRecognizerClient) class TestReceiptFromUrl(FormRecognizerTest): + @GlobalFormRecognizerAccountPreparer() + def test_polling_interval(self, resource_group, location, form_recognizer_account, form_recognizer_account_key): + client = FormRecognizerClient(form_recognizer_account, AzureKeyCredential(form_recognizer_account_key), polling_interval=7) + self.assertEqual(client._client._config.polling_interval, 7) + + poller = client.begin_recognize_receipts_from_url(self.receipt_url_jpg, polling_interval=6) + poller.wait() + self.assertEqual(poller._polling_method._timeout, 6) + poller2 = client.begin_recognize_receipts_from_url(self.receipt_url_jpg) + poller2.wait() + self.assertEqual(poller2._polling_method._timeout, 7) # goes back to client default + @pytest.mark.live_test_only def test_active_directory_auth(self): token = self.generate_oauth_token() @@ -32,8 +49,8 @@ def test_receipt_url_bad_endpoint(self, resource_group, location, form_recognize poller = client.begin_recognize_receipts_from_url(self.receipt_url_jpg) @GlobalFormRecognizerAccountPreparer() - def test_receipt_url_auth_successful_key(self, resource_group, location, form_recognizer_account, form_recognizer_account_key): - client = FormRecognizerClient(form_recognizer_account, AzureKeyCredential(form_recognizer_account_key)) + @GlobalClientPreparer() + def test_receipt_url_auth_successful_key(self, client): poller = client.begin_recognize_receipts_from_url(self.receipt_url_jpg) result = poller.result() @@ -44,23 +61,21 @@ def test_receipt_url_auth_bad_key(self, resource_group, location, form_recognize poller = client.begin_recognize_receipts_from_url(self.receipt_url_jpg) @GlobalFormRecognizerAccountPreparer() - def test_receipt_bad_url(self, resource_group, location, form_recognizer_account, form_recognizer_account_key): - client = FormRecognizerClient(form_recognizer_account, AzureKeyCredential(form_recognizer_account_key)) - + @GlobalClientPreparer() + def test_receipt_bad_url(self, client): with self.assertRaises(HttpResponseError): poller = client.begin_recognize_receipts_from_url("https://badurl.jpg") @GlobalFormRecognizerAccountPreparer() - def test_receipt_url_pass_stream(self, resource_group, location, form_recognizer_account, form_recognizer_account_key): - client = FormRecognizerClient(form_recognizer_account, AzureKeyCredential(form_recognizer_account_key)) + @GlobalClientPreparer() + def test_receipt_url_pass_stream(self, client): with open(self.receipt_png, "rb") as receipt: with self.assertRaises(HttpResponseError): poller = client.begin_recognize_receipts_from_url(receipt) @GlobalFormRecognizerAccountPreparer() - def test_receipt_url_transform_jpg(self, resource_group, location, form_recognizer_account, form_recognizer_account_key): - client = FormRecognizerClient(form_recognizer_account, AzureKeyCredential(form_recognizer_account_key)) - + @GlobalClientPreparer() + def test_receipt_url_transform_jpg(self, client): responses = [] def callback(raw_response, _, headers): @@ -110,8 +125,8 @@ def callback(raw_response, _, headers): self.assertFormPagesTransformCorrect(receipt.pages, read_results) @GlobalFormRecognizerAccountPreparer() - def test_receipt_url_transform_png(self, resource_group, location, form_recognizer_account, form_recognizer_account_key): - client = FormRecognizerClient(form_recognizer_account, AzureKeyCredential(form_recognizer_account_key)) + @GlobalClientPreparer() + def test_receipt_url_transform_png(self, client): responses = [] @@ -162,8 +177,8 @@ def callback(raw_response, _, headers): self.assertFormPagesTransformCorrect(receipt.pages, read_results) @GlobalFormRecognizerAccountPreparer() - def test_receipt_url_include_text_content(self, resource_group, location, form_recognizer_account, form_recognizer_account_key): - client = FormRecognizerClient(form_recognizer_account, AzureKeyCredential(form_recognizer_account_key)) + @GlobalClientPreparer() + def test_receipt_url_include_text_content(self, client): poller = client.begin_recognize_receipts_from_url( self.receipt_url_jpg, @@ -184,8 +199,8 @@ def test_receipt_url_include_text_content(self, resource_group, location, form_r self.assertTextContentHasValues(value.value_data.text_content, receipt.page_range.first_page_number) @GlobalFormRecognizerAccountPreparer() - def test_receipt_url_jpg(self, resource_group, location, form_recognizer_account, form_recognizer_account_key): - client = FormRecognizerClient(form_recognizer_account, AzureKeyCredential(form_recognizer_account_key)) + @GlobalClientPreparer() + def test_receipt_url_jpg(self, client): poller = client.begin_recognize_receipts_from_url(self.receipt_url_jpg) @@ -210,8 +225,8 @@ def test_receipt_url_jpg(self, resource_group, location, form_recognizer_account self.assertReceiptItemsHasValues(receipt.fields["Items"].value, receipt.page_range.first_page_number, False) @GlobalFormRecognizerAccountPreparer() - def test_receipt_url_png(self, resource_group, location, form_recognizer_account, form_recognizer_account_key): - client = FormRecognizerClient(form_recognizer_account, AzureKeyCredential(form_recognizer_account_key)) + @GlobalClientPreparer() + def test_receipt_url_png(self, client): poller = client.begin_recognize_receipts_from_url(self.receipt_url_png) @@ -233,8 +248,8 @@ def test_receipt_url_png(self, resource_group, location, form_recognizer_account self.assertEqual(receipt_type.value, 'Itemized') @GlobalFormRecognizerAccountPreparer() - def test_receipt_multipage_url(self, resource_group, location, form_recognizer_account, form_recognizer_account_key): - client = FormRecognizerClient(form_recognizer_account, AzureKeyCredential(form_recognizer_account_key)) + @GlobalClientPreparer() + def test_receipt_multipage_url(self, client): poller = client.begin_recognize_receipts_from_url(self.multipage_url_pdf, include_text_content=True) result = poller.result() @@ -266,8 +281,8 @@ def test_receipt_multipage_url(self, resource_group, location, form_recognizer_a self.assertEqual(receipt_type.value, 'Itemized') @GlobalFormRecognizerAccountPreparer() - def test_receipt_multipage_transform_url(self, resource_group, location, form_recognizer_account, form_recognizer_account_key): - client = FormRecognizerClient(form_recognizer_account, AzureKeyCredential(form_recognizer_account_key)) + @GlobalClientPreparer() + def test_receipt_multipage_transform_url(self, client): responses = [] @@ -323,9 +338,9 @@ def callback(raw_response, _, headers): self.assertFormPagesTransformCorrect(returned_model, read_results) @GlobalFormRecognizerAccountPreparer() + @GlobalClientPreparer() @pytest.mark.live_test_only - def test_receipt_continuation_token(self, resource_group, location, form_recognizer_account, form_recognizer_account_key): - client = FormRecognizerClient(form_recognizer_account, AzureKeyCredential(form_recognizer_account_key)) + def test_receipt_continuation_token(self, client): initial_poller = client.begin_recognize_receipts_from_url(self.receipt_url_jpg) cont_token = initial_poller.continuation_token() diff --git a/sdk/formrecognizer/azure-ai-formrecognizer/tests/test_receipt_from_url_async.py b/sdk/formrecognizer/azure-ai-formrecognizer/tests/test_receipt_from_url_async.py index 21558906aed4..6770833364f7 100644 --- a/sdk/formrecognizer/azure-ai-formrecognizer/tests/test_receipt_from_url_async.py +++ b/sdk/formrecognizer/azure-ai-formrecognizer/tests/test_receipt_from_url_async.py @@ -5,6 +5,7 @@ # ------------------------------------ import pytest +import functools from datetime import date, time from azure.core.exceptions import HttpResponseError, ServiceRequestError, ClientAuthenticationError from azure.core.credentials import AzureKeyCredential @@ -13,10 +14,26 @@ from azure.ai.formrecognizer.aio import FormRecognizerClient from testcase import GlobalFormRecognizerAccountPreparer from asynctestcase import AsyncFormRecognizerTest +from testcase import GlobalClientPreparer as _GlobalClientPreparer + + +GlobalClientPreparer = functools.partial(_GlobalClientPreparer, FormRecognizerClient) class TestReceiptFromUrlAsync(AsyncFormRecognizerTest): + @GlobalFormRecognizerAccountPreparer() + async def test_polling_interval(self, resource_group, location, form_recognizer_account, form_recognizer_account_key): + client = FormRecognizerClient(form_recognizer_account, AzureKeyCredential(form_recognizer_account_key), polling_interval=7) + self.assertEqual(client._client._config.polling_interval, 7) + + poller = await client.begin_recognize_receipts_from_url(self.receipt_url_jpg, polling_interval=6) + await poller.wait() + self.assertEqual(poller._polling_method._timeout, 6) + poller2 = await client.begin_recognize_receipts_from_url(self.receipt_url_jpg) + await poller2.wait() + self.assertEqual(poller2._polling_method._timeout, 7) # goes back to client default + @pytest.mark.live_test_only @GlobalFormRecognizerAccountPreparer() async def test_active_directory_auth_async(self): @@ -39,8 +56,8 @@ async def test_receipt_url_bad_endpoint(self, resource_group, location, form_rec result = await poller.result() @GlobalFormRecognizerAccountPreparer() - async def test_receipt_url_auth_successful_key(self, resource_group, location, form_recognizer_account, form_recognizer_account_key): - client = FormRecognizerClient(form_recognizer_account, AzureKeyCredential(form_recognizer_account_key)) + @GlobalClientPreparer() + async def test_receipt_url_auth_successful_key(self, client): poller = await client.begin_recognize_receipts_from_url( self.receipt_url_jpg ) @@ -56,16 +73,16 @@ async def test_receipt_url_auth_bad_key(self, resource_group, location, form_rec result = await poller.result() @GlobalFormRecognizerAccountPreparer() - async def test_receipt_bad_url(self, resource_group, location, form_recognizer_account, form_recognizer_account_key): - client = FormRecognizerClient(form_recognizer_account, AzureKeyCredential(form_recognizer_account_key)) - + @GlobalClientPreparer() + async def test_receipt_bad_url(self, client): with self.assertRaises(HttpResponseError): poller = await client.begin_recognize_receipts_from_url("https://badurl.jpg") result = await poller.result() @GlobalFormRecognizerAccountPreparer() - async def test_receipt_url_pass_stream(self, resource_group, location, form_recognizer_account, form_recognizer_account_key): - client = FormRecognizerClient(form_recognizer_account, AzureKeyCredential(form_recognizer_account_key)) + @GlobalClientPreparer() + async def test_receipt_url_pass_stream(self, client): + with open(self.receipt_png, "rb") as fd: receipt = fd.read(4) # makes the recording smaller @@ -74,8 +91,8 @@ async def test_receipt_url_pass_stream(self, resource_group, location, form_reco result = await poller.result() @GlobalFormRecognizerAccountPreparer() - async def test_receipt_url_transform_jpg(self, resource_group, location, form_recognizer_account, form_recognizer_account_key): - client = FormRecognizerClient(form_recognizer_account, AzureKeyCredential(form_recognizer_account_key)) + @GlobalClientPreparer() + async def test_receipt_url_transform_jpg(self, client): responses = [] @@ -126,9 +143,8 @@ def callback(raw_response, _, headers): self.assertFormPagesTransformCorrect(receipt.pages, read_results) @GlobalFormRecognizerAccountPreparer() - async def test_receipt_url_transform_png(self, resource_group, location, form_recognizer_account, form_recognizer_account_key): - client = FormRecognizerClient(form_recognizer_account, AzureKeyCredential(form_recognizer_account_key)) - + @GlobalClientPreparer() + async def test_receipt_url_transform_png(self, client): responses = [] def callback(raw_response, _, headers): @@ -178,8 +194,8 @@ def callback(raw_response, _, headers): self.assertFormPagesTransformCorrect(receipt.pages, read_results) @GlobalFormRecognizerAccountPreparer() - async def test_receipt_url_include_text_content(self, resource_group, location, form_recognizer_account, form_recognizer_account_key): - client = FormRecognizerClient(form_recognizer_account, AzureKeyCredential(form_recognizer_account_key)) + @GlobalClientPreparer() + async def test_receipt_url_include_text_content(self, client): poller = await client.begin_recognize_receipts_from_url( self.receipt_url_jpg, @@ -200,8 +216,8 @@ async def test_receipt_url_include_text_content(self, resource_group, location, self.assertTextContentHasValues(value.value_data.text_content, receipt.page_range.first_page_number) @GlobalFormRecognizerAccountPreparer() - async def test_receipt_url_jpg(self, resource_group, location, form_recognizer_account, form_recognizer_account_key): - client = FormRecognizerClient(form_recognizer_account, AzureKeyCredential(form_recognizer_account_key)) + @GlobalClientPreparer() + async def test_receipt_url_jpg(self, client): poller = await client.begin_recognize_receipts_from_url( self.receipt_url_jpg @@ -228,8 +244,8 @@ async def test_receipt_url_jpg(self, resource_group, location, form_recognizer_a self.assertReceiptItemsHasValues(receipt.fields["Items"].value, receipt.page_range.first_page_number, False) @GlobalFormRecognizerAccountPreparer() - async def test_receipt_url_png(self, resource_group, location, form_recognizer_account, form_recognizer_account_key): - client = FormRecognizerClient(form_recognizer_account, AzureKeyCredential(form_recognizer_account_key)) + @GlobalClientPreparer() + async def test_receipt_url_png(self, client): poller = await client.begin_recognize_receipts_from_url(self.receipt_url_png) result = await poller.result() @@ -251,8 +267,8 @@ async def test_receipt_url_png(self, resource_group, location, form_recognizer_a self.assertEqual(receipt_type.value, 'Itemized') @GlobalFormRecognizerAccountPreparer() - async def test_receipt_multipage_url(self, resource_group, location, form_recognizer_account, form_recognizer_account_key): - client = FormRecognizerClient(form_recognizer_account, AzureKeyCredential(form_recognizer_account_key)) + @GlobalClientPreparer() + async def test_receipt_multipage_url(self, client): poller = await client.begin_recognize_receipts_from_url(self.multipage_url_pdf, include_text_content=True) result = await poller.result() @@ -284,8 +300,8 @@ async def test_receipt_multipage_url(self, resource_group, location, form_recogn self.assertEqual(receipt_type.value, 'Itemized') @GlobalFormRecognizerAccountPreparer() - async def test_receipt_multipage_transform_url(self, resource_group, location, form_recognizer_account, form_recognizer_account_key): - client = FormRecognizerClient(form_recognizer_account, AzureKeyCredential(form_recognizer_account_key)) + @GlobalClientPreparer() + async def test_receipt_multipage_transform_url(self, client): responses = [] @@ -341,9 +357,9 @@ def callback(raw_response, _, headers): self.assertFormPagesTransformCorrect(returned_model, read_results) @GlobalFormRecognizerAccountPreparer() + @GlobalClientPreparer() @pytest.mark.live_test_only - async def test_receipt_continuation_token(self, resource_group, location, form_recognizer_account, form_recognizer_account_key): - client = FormRecognizerClient(form_recognizer_account, AzureKeyCredential(form_recognizer_account_key)) + async def test_receipt_continuation_token(self, client): initial_poller = await client.begin_recognize_receipts_from_url(self.receipt_url_jpg) cont_token = initial_poller.continuation_token() diff --git a/sdk/formrecognizer/azure-ai-formrecognizer/tests/test_training.py b/sdk/formrecognizer/azure-ai-formrecognizer/tests/test_training.py index 92f73759df85..5810557853da 100644 --- a/sdk/formrecognizer/azure-ai-formrecognizer/tests/test_training.py +++ b/sdk/formrecognizer/azure-ai-formrecognizer/tests/test_training.py @@ -12,14 +12,30 @@ from azure.ai.formrecognizer._models import CustomFormModel from azure.ai.formrecognizer import FormTrainingClient from testcase import FormRecognizerTest, GlobalFormRecognizerAccountPreparer -from testcase import GlobalTrainingAccountPreparer as _GlobalTrainingAccountPreparer +from testcase import GlobalClientPreparer as _GlobalClientPreparer -GlobalTrainingAccountPreparer = functools.partial(_GlobalTrainingAccountPreparer, FormTrainingClient) +GlobalClientPreparer = functools.partial(_GlobalClientPreparer, FormTrainingClient) class TestTraining(FormRecognizerTest): + @GlobalFormRecognizerAccountPreparer() + @GlobalClientPreparer(training=True) + def test_polling_interval(self, client, container_sas_url): + def check_poll_value(poll): + if self.is_live: + self.assertEqual(poll, 5) + else: + self.assertEqual(poll, 0) + check_poll_value(client._client._config.polling_interval) + poller = client.begin_training(training_files_url=container_sas_url, use_training_labels=False, polling_interval=6) + poller.wait() + self.assertEqual(poller._polling_method._timeout, 6) + poller2 = client.begin_training(training_files_url=container_sas_url, use_training_labels=False) + poller2.wait() + check_poll_value(poller2._polling_method._timeout) # goes back to client default + @GlobalFormRecognizerAccountPreparer() def test_training_auth_bad_key(self, resource_group, location, form_recognizer_account, form_recognizer_account_key): client = FormTrainingClient(form_recognizer_account, AzureKeyCredential("xxxx")) @@ -27,7 +43,7 @@ def test_training_auth_bad_key(self, resource_group, location, form_recognizer_a poller = client.begin_training("xx", use_training_labels=False) @GlobalFormRecognizerAccountPreparer() - @GlobalTrainingAccountPreparer() + @GlobalClientPreparer(training=True) def test_training(self, client, container_sas_url): poller = client.begin_training(training_files_url=container_sas_url, use_training_labels=False) @@ -50,7 +66,7 @@ def test_training(self, client, container_sas_url): self.assertIsNotNone(field.name) @GlobalFormRecognizerAccountPreparer() - @GlobalTrainingAccountPreparer(multipage=True) + @GlobalClientPreparer(training=True, multipage=True) def test_training_multipage(self, client, container_sas_url): poller = client.begin_training(container_sas_url, use_training_labels=False) @@ -73,7 +89,7 @@ def test_training_multipage(self, client, container_sas_url): self.assertIsNotNone(field.name) @GlobalFormRecognizerAccountPreparer() - @GlobalTrainingAccountPreparer() + @GlobalClientPreparer(training=True) def test_training_transform(self, client, container_sas_url): raw_response = [] @@ -92,7 +108,7 @@ def callback(response): self.assertModelTransformCorrect(custom_model, raw_model, unlabeled=True) @GlobalFormRecognizerAccountPreparer() - @GlobalTrainingAccountPreparer(multipage=True) + @GlobalClientPreparer(training=True, multipage=True) def test_training_multipage_transform(self, client, container_sas_url): raw_response = [] @@ -111,7 +127,7 @@ def callback(response): self.assertModelTransformCorrect(custom_model, raw_model, unlabeled=True) @GlobalFormRecognizerAccountPreparer() - @GlobalTrainingAccountPreparer() + @GlobalClientPreparer(training=True) def test_training_with_labels(self, client, container_sas_url): poller = client.begin_training(training_files_url=container_sas_url, use_training_labels=True) @@ -135,7 +151,7 @@ def test_training_with_labels(self, client, container_sas_url): self.assertIsNotNone(field.name) @GlobalFormRecognizerAccountPreparer() - @GlobalTrainingAccountPreparer(multipage=True) + @GlobalClientPreparer(training=True, multipage=True) def test_training_multipage_with_labels(self, client, container_sas_url): poller = client.begin_training(container_sas_url, use_training_labels=True) @@ -159,7 +175,7 @@ def test_training_multipage_with_labels(self, client, container_sas_url): self.assertIsNotNone(field.name) @GlobalFormRecognizerAccountPreparer() - @GlobalTrainingAccountPreparer() + @GlobalClientPreparer(training=True) def test_training_with_labels_transform(self, client, container_sas_url): raw_response = [] @@ -178,7 +194,7 @@ def callback(response): self.assertModelTransformCorrect(custom_model, raw_model) @GlobalFormRecognizerAccountPreparer() - @GlobalTrainingAccountPreparer(multipage=True) + @GlobalClientPreparer(training=True, multipage=True) def test_train_multipage_w_labels_transform(self, client, container_sas_url): raw_response = [] @@ -197,7 +213,7 @@ def callback(response): self.assertModelTransformCorrect(custom_model, raw_model) @GlobalFormRecognizerAccountPreparer() - @GlobalTrainingAccountPreparer() + @GlobalClientPreparer(training=True) def test_training_with_files_filter(self, client, container_sas_url): poller = client.begin_training(training_files_url=container_sas_url, use_training_labels=False, include_sub_folders=True) @@ -215,7 +231,7 @@ def test_training_with_files_filter(self, client, container_sas_url): model = poller.result() @GlobalFormRecognizerAccountPreparer() - @GlobalTrainingAccountPreparer() + @GlobalClientPreparer(training=True) @pytest.mark.live_test_only def test_training_continuation_token(self, client, container_sas_url): diff --git a/sdk/formrecognizer/azure-ai-formrecognizer/tests/test_training_async.py b/sdk/formrecognizer/azure-ai-formrecognizer/tests/test_training_async.py index 67eb33d4398f..87ba872fd296 100644 --- a/sdk/formrecognizer/azure-ai-formrecognizer/tests/test_training_async.py +++ b/sdk/formrecognizer/azure-ai-formrecognizer/tests/test_training_async.py @@ -12,14 +12,31 @@ from azure.ai.formrecognizer._models import CustomFormModel from azure.ai.formrecognizer.aio import FormTrainingClient from testcase import FormRecognizerTest, GlobalFormRecognizerAccountPreparer -from testcase import GlobalTrainingAccountPreparer as _GlobalTrainingAccountPreparer from asynctestcase import AsyncFormRecognizerTest +from testcase import GlobalClientPreparer as _GlobalClientPreparer -GlobalTrainingAccountPreparer = functools.partial(_GlobalTrainingAccountPreparer, FormTrainingClient) + +GlobalClientPreparer = functools.partial(_GlobalClientPreparer, FormTrainingClient) class TestTrainingAsync(AsyncFormRecognizerTest): + @GlobalFormRecognizerAccountPreparer() + @GlobalClientPreparer(training=True) + async def test_polling_interval(self, client, container_sas_url): + def check_poll_value(poll): + if self.is_live: + self.assertEqual(poll, 5) + else: + self.assertEqual(poll, 0) + check_poll_value(client._client._config.polling_interval) + poller = await client.begin_training(training_files_url=container_sas_url, use_training_labels=False, polling_interval=6) + await poller.wait() + self.assertEqual(poller._polling_method._timeout, 6) + poller2 = await client.begin_training(training_files_url=container_sas_url, use_training_labels=False) + await poller2.wait() + check_poll_value(poller2._polling_method._timeout) # goes back to client default + @GlobalFormRecognizerAccountPreparer() async def test_training_auth_bad_key(self, resource_group, location, form_recognizer_account, form_recognizer_account_key): client = FormTrainingClient(form_recognizer_account, AzureKeyCredential("xxxx")) @@ -28,7 +45,7 @@ async def test_training_auth_bad_key(self, resource_group, location, form_recogn result = await poller.result() @GlobalFormRecognizerAccountPreparer() - @GlobalTrainingAccountPreparer() + @GlobalClientPreparer(training=True) async def test_training(self, client, container_sas_url): poller = await client.begin_training( @@ -53,7 +70,7 @@ async def test_training(self, client, container_sas_url): self.assertIsNotNone(field.name) @GlobalFormRecognizerAccountPreparer() - @GlobalTrainingAccountPreparer(multipage=True) + @GlobalClientPreparer(training=True, multipage=True) async def test_training_multipage(self, client, container_sas_url): poller = await client.begin_training(container_sas_url, use_training_labels=False) @@ -76,7 +93,7 @@ async def test_training_multipage(self, client, container_sas_url): self.assertIsNotNone(field.name) @GlobalFormRecognizerAccountPreparer() - @GlobalTrainingAccountPreparer() + @GlobalClientPreparer(training=True) async def test_training_transform(self, client, container_sas_url): raw_response = [] @@ -98,7 +115,7 @@ def callback(response): self.assertModelTransformCorrect(custom_model, raw_model, unlabeled=True) @GlobalFormRecognizerAccountPreparer() - @GlobalTrainingAccountPreparer(multipage=True) + @GlobalClientPreparer(training=True, multipage=True) async def test_training_multipage_transform(self, client, container_sas_url): raw_response = [] @@ -117,7 +134,7 @@ def callback(response): self.assertModelTransformCorrect(custom_model, raw_model, unlabeled=True) @GlobalFormRecognizerAccountPreparer() - @GlobalTrainingAccountPreparer() + @GlobalClientPreparer(training=True) async def test_training_with_labels(self, client, container_sas_url): poller = await client.begin_training(training_files_url=container_sas_url, use_training_labels=True) @@ -140,7 +157,7 @@ async def test_training_with_labels(self, client, container_sas_url): self.assertIsNotNone(field.name) @GlobalFormRecognizerAccountPreparer() - @GlobalTrainingAccountPreparer(multipage=True) + @GlobalClientPreparer(training=True, multipage=True) async def test_training_multipage_with_labels(self, client, container_sas_url): poller = await client.begin_training(container_sas_url, use_training_labels=True) @@ -164,7 +181,7 @@ async def test_training_multipage_with_labels(self, client, container_sas_url): self.assertIsNotNone(field.name) @GlobalFormRecognizerAccountPreparer() - @GlobalTrainingAccountPreparer() + @GlobalClientPreparer(training=True) async def test_training_with_labels_transform(self, client, container_sas_url): raw_response = [] @@ -183,7 +200,7 @@ def callback(response): self.assertModelTransformCorrect(custom_model, raw_model) @GlobalFormRecognizerAccountPreparer() - @GlobalTrainingAccountPreparer(multipage=True) + @GlobalClientPreparer(training=True, multipage=True) async def test_train_multipage_w_lbls_trnsfrm(self, client, container_sas_url): raw_response = [] @@ -202,7 +219,7 @@ def callback(response): self.assertModelTransformCorrect(custom_model, raw_model) @GlobalFormRecognizerAccountPreparer() - @GlobalTrainingAccountPreparer() + @GlobalClientPreparer(training=True) async def test_training_with_files_filter(self, client, container_sas_url): poller = await client.begin_training(training_files_url=container_sas_url, use_training_labels=False, include_sub_folders=True) @@ -220,7 +237,7 @@ async def test_training_with_files_filter(self, client, container_sas_url): model = await poller.result() @GlobalFormRecognizerAccountPreparer() - @GlobalTrainingAccountPreparer() + @GlobalClientPreparer(training=True) @pytest.mark.live_test_only async def test_training_continuation_token(self, client, container_sas_url): diff --git a/sdk/formrecognizer/azure-ai-formrecognizer/tests/testcase.py b/sdk/formrecognizer/azure-ai-formrecognizer/tests/testcase.py index c67bb0592dcf..c5bc014973fb 100644 --- a/sdk/formrecognizer/azure-ai-formrecognizer/tests/testcase.py +++ b/sdk/formrecognizer/azure-ai-formrecognizer/tests/testcase.py @@ -32,6 +32,7 @@ class AccessTokenReplacer(RecordingProcessor): """Replace the access token in a request/response body.""" def __init__(self, replacement='redacted'): + self._replacement = replacement def process_request(self, request): @@ -387,7 +388,7 @@ def create_resource(self, name, **kwargs): ) return { - 'location': 'centraluseuap', + 'location': REGION, 'resource_group': rg, } @@ -402,21 +403,22 @@ def __init__(self): def create_resource(self, name, **kwargs): form_recognizer_account = FormRecognizerTest._FORM_RECOGNIZER_ACCOUNT return { - 'location': 'centraluseuap', + 'location': REGION, 'resource_group': FormRecognizerTest._RESOURCE_GROUP, 'form_recognizer_account': form_recognizer_account, 'form_recognizer_account_key': FormRecognizerTest._FORM_RECOGNIZER_KEY } -class GlobalTrainingAccountPreparer(AzureMgmtPreparer): +class GlobalClientPreparer(AzureMgmtPreparer): def __init__(self, client_cls, client_kwargs={}, **kwargs): - super(GlobalTrainingAccountPreparer, self).__init__( + super(GlobalClientPreparer, self).__init__( name_prefix='', random_name_length=42 ) self.client_kwargs = client_kwargs self.client_cls = client_cls + self.training = kwargs.get("training", False) self.multipage_test = kwargs.get("multipage", False) self.multipage_test_2 = kwargs.get("multipage2", False) self.need_blob_sas_url = kwargs.get("blob_sas_url", False) @@ -446,50 +448,7 @@ def get_settings_value(self, key): raise return key_value - def create_resource(self, name, **kwargs): - client, container_sas_url, blob_sas_url = self.create_form_client_and_container_sas_url(**kwargs) - - if self.need_blob_sas_url: - return {"client": client, - "container_sas_url": container_sas_url, - "blob_sas_url": blob_sas_url} - if self.copy: - if self.is_live: - resource_group = kwargs.get("resource_group") - subscription_id = self.get_settings_value("SUBSCRIPTION_ID") - form_recognizer_name = FormRecognizerTest._FORM_RECOGNIZER_NAME - - resource_id = "/subscriptions/" + subscription_id + "/resourceGroups/" + resource_group.name + \ - "/providers/Microsoft.CognitiveServices/accounts/" + form_recognizer_name - resource_location = REGION - self.test_class_instance.scrubber.register_name_pair( - resource_id, - "resource_id" - ) - else: - resource_location = REGION - resource_id = "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/rgname/providers/Microsoft.CognitiveServices/accounts/frname" - - return { - "client": client, - "container_sas_url": container_sas_url, - "location": resource_location, - "resource_id": resource_id - } - - else: - return {"client": client, - "container_sas_url": container_sas_url} - - def create_form_client_and_container_sas_url(self, **kwargs): - form_recognizer_account = self.client_kwargs.pop("form_recognizer_account", None) - if form_recognizer_account is None: - form_recognizer_account = kwargs.pop("form_recognizer_account") - - form_recognizer_account_key = self.client_kwargs.pop("form_recognizer_account_key", None) - if form_recognizer_account_key is None: - form_recognizer_account_key = kwargs.pop("form_recognizer_account_key") - + def get_training_parameters(self, client): if self.is_live: if self.multipage_test: container_sas_url = self.get_settings_value("FORM_RECOGNIZER_MULTIPAGE_STORAGE_CONTAINER_SAS_URL") @@ -521,11 +480,71 @@ def create_form_client_and_container_sas_url(self, **kwargs): container_sas_url = "containersasurl" blob_sas_url = "blob_sas_url" + if self.need_blob_sas_url: + return {"client": client, + "container_sas_url": container_sas_url, + "blob_sas_url": blob_sas_url} + else: + return {"client": client, + "container_sas_url": container_sas_url} + + def get_copy_parameters(self, training_params, client, **kwargs): + if self.is_live: + resource_group = kwargs.get("resource_group") + subscription_id = self.get_settings_value("SUBSCRIPTION_ID") + form_recognizer_name = FormRecognizerTest._FORM_RECOGNIZER_NAME + + resource_id = "/subscriptions/" + subscription_id + "/resourceGroups/" + resource_group.name + \ + "/providers/Microsoft.CognitiveServices/accounts/" + form_recognizer_name + resource_location = REGION + self.test_class_instance.scrubber.register_name_pair( + resource_id, + "resource_id" + ) + else: + resource_location = REGION + resource_id = "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/rgname/providers/Microsoft.CognitiveServices/accounts/frname" + + return { + "client": client, + "container_sas_url": training_params["container_sas_url"], + "location": resource_location, + "resource_id": resource_id + } + + def create_resource(self, name, **kwargs): + client = self.create_form_client(**kwargs) + + if not self.training: + return {"client": client} + + training_params = self.get_training_parameters(client) + + if self.copy: + return self.get_copy_parameters(training_params, client, **kwargs) + + return training_params + + def create_form_client(self, **kwargs): + form_recognizer_account = self.client_kwargs.pop("form_recognizer_account", None) + if form_recognizer_account is None: + form_recognizer_account = kwargs.pop("form_recognizer_account") + + form_recognizer_account_key = self.client_kwargs.pop("form_recognizer_account_key", None) + if form_recognizer_account_key is None: + form_recognizer_account_key = kwargs.pop("form_recognizer_account_key") + + if self.is_live: + polling_interval = 5 + else: + polling_interval = 0 + return self.client_cls( form_recognizer_account, AzureKeyCredential(form_recognizer_account_key), + polling_interval=polling_interval, **self.client_kwargs - ), container_sas_url, blob_sas_url + ) @pytest.fixture(scope="session") From 3c46d67312a58e1b122095966d6dc0f72088f9ef Mon Sep 17 00:00:00 2001 From: Charles Lowell Date: Mon, 15 Jun 2020 12:37:50 -0700 Subject: [PATCH 06/21] AzureCliCredential correctly invokes /bin/sh (#12056) --- sdk/identity/azure-identity/CHANGELOG.md | 3 ++- .../azure/identity/aio/_credentials/azure_cli.py | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/sdk/identity/azure-identity/CHANGELOG.md b/sdk/identity/azure-identity/CHANGELOG.md index af85d5ae8eb1..3223ae7d8713 100644 --- a/sdk/identity/azure-identity/CHANGELOG.md +++ b/sdk/identity/azure-identity/CHANGELOG.md @@ -1,7 +1,8 @@ # Release History ## 1.4.0b6 (Unreleased) - +- The async `AzureCliCredential` correctly invokes `/bin/sh` + ([#12048](https://github.com/Azure/azure-sdk-for-python/issues/12048)) ## 1.4.0b5 (2020-06-12) - Prevent an error on importing `AzureCliCredential` on Windows caused by a bug diff --git a/sdk/identity/azure-identity/azure/identity/aio/_credentials/azure_cli.py b/sdk/identity/azure-identity/azure/identity/aio/_credentials/azure_cli.py index c049b1421cde..a562a7831b9f 100644 --- a/sdk/identity/azure-identity/azure/identity/aio/_credentials/azure_cli.py +++ b/sdk/identity/azure-identity/azure/identity/aio/_credentials/azure_cli.py @@ -62,7 +62,7 @@ async def _run_command(command): if sys.platform.startswith("win"): args = ("cmd", "/c " + command) else: - args = ("/bin/sh", "-c " + command) + args = ("/bin/sh", "-c", command) working_directory = get_safe_working_dir() From 287ac6b8d0330387057877f3133603d9e11b445d Mon Sep 17 00:00:00 2001 From: Azure SDK Bot <53356347+azure-sdk@users.noreply.github.com> Date: Mon, 15 Jun 2020 14:25:32 -0700 Subject: [PATCH 07/21] Increment version for storage releases (#12034) * Increment package version after release of azure_storage_file_share * Increment package version after release of azure_storage_blob * Increment package version after release of azure_storage_file_datalake * Increment package version after release of azure_storage_queue --- sdk/storage/azure-storage-blob/CHANGELOG.md | 3 +++ sdk/storage/azure-storage-blob/azure/storage/blob/_version.py | 2 +- sdk/storage/azure-storage-file-datalake/CHANGELOG.md | 3 +++ .../azure/storage/filedatalake/_version.py | 2 +- sdk/storage/azure-storage-file-share/CHANGELOG.md | 3 +++ .../azure/storage/fileshare/_version.py | 2 +- sdk/storage/azure-storage-queue/CHANGELOG.md | 3 +++ .../azure-storage-queue/azure/storage/queue/_version.py | 2 +- 8 files changed, 16 insertions(+), 4 deletions(-) diff --git a/sdk/storage/azure-storage-blob/CHANGELOG.md b/sdk/storage/azure-storage-blob/CHANGELOG.md index 39e71cfe66d3..fa406abfc27e 100644 --- a/sdk/storage/azure-storage-blob/CHANGELOG.md +++ b/sdk/storage/azure-storage-blob/CHANGELOG.md @@ -1,5 +1,8 @@ # Release History +## 12.3.3 (Unreleased) + + ## 12.3.2 (2020-6-12) **Fixes** - Fixed issue where batch requests could not be combined with SAS (#9534) diff --git a/sdk/storage/azure-storage-blob/azure/storage/blob/_version.py b/sdk/storage/azure-storage-blob/azure/storage/blob/_version.py index e9b99901cfb5..f7acb8dc3808 100644 --- a/sdk/storage/azure-storage-blob/azure/storage/blob/_version.py +++ b/sdk/storage/azure-storage-blob/azure/storage/blob/_version.py @@ -4,4 +4,4 @@ # license information. # -------------------------------------------------------------------------- -VERSION = "12.3.2" +VERSION = "12.3.3" diff --git a/sdk/storage/azure-storage-file-datalake/CHANGELOG.md b/sdk/storage/azure-storage-file-datalake/CHANGELOG.md index 6eea8a0222a9..c785b5388170 100644 --- a/sdk/storage/azure-storage-file-datalake/CHANGELOG.md +++ b/sdk/storage/azure-storage-file-datalake/CHANGELOG.md @@ -1,5 +1,8 @@ # Release History +## 12.0.3 (Unreleased) + + ## 12.0.2 (2020-6-12) **Fixes** - Improve the performance of upload when using max_concurrency diff --git a/sdk/storage/azure-storage-file-datalake/azure/storage/filedatalake/_version.py b/sdk/storage/azure-storage-file-datalake/azure/storage/filedatalake/_version.py index 8fd5a9134155..67d29d4fb6fb 100644 --- a/sdk/storage/azure-storage-file-datalake/azure/storage/filedatalake/_version.py +++ b/sdk/storage/azure-storage-file-datalake/azure/storage/filedatalake/_version.py @@ -4,4 +4,4 @@ # license information. # -------------------------------------------------------------------------- -VERSION = "12.0.2" +VERSION = "12.0.3" diff --git a/sdk/storage/azure-storage-file-share/CHANGELOG.md b/sdk/storage/azure-storage-file-share/CHANGELOG.md index 9ed6c15a5427..2012bec8afcd 100644 --- a/sdk/storage/azure-storage-file-share/CHANGELOG.md +++ b/sdk/storage/azure-storage-file-share/CHANGELOG.md @@ -1,5 +1,8 @@ # Release History +## 12.1.3 (Unreleased) + + ## 12.1.2 (2020-6-12) **Fixes** - Improve the performance of upload when using max_concurrency diff --git a/sdk/storage/azure-storage-file-share/azure/storage/fileshare/_version.py b/sdk/storage/azure-storage-file-share/azure/storage/fileshare/_version.py index 4cd76a18aff6..1788d7bc87a6 100644 --- a/sdk/storage/azure-storage-file-share/azure/storage/fileshare/_version.py +++ b/sdk/storage/azure-storage-file-share/azure/storage/fileshare/_version.py @@ -4,4 +4,4 @@ # license information. # -------------------------------------------------------------------------- -VERSION = "12.1.2" +VERSION = "12.1.3" diff --git a/sdk/storage/azure-storage-queue/CHANGELOG.md b/sdk/storage/azure-storage-queue/CHANGELOG.md index 0677d85e7a46..dbc94801dcf6 100644 --- a/sdk/storage/azure-storage-queue/CHANGELOG.md +++ b/sdk/storage/azure-storage-queue/CHANGELOG.md @@ -1,5 +1,8 @@ # Release History +## 12.1.3 (Unreleased) + + ## 12.1.2 (2020-6-12) **Notes** - Updated dependency from azure-core<2.0.0,>=1.2.2 to azure-core<2.0.0,>=1.6.0 diff --git a/sdk/storage/azure-storage-queue/azure/storage/queue/_version.py b/sdk/storage/azure-storage-queue/azure/storage/queue/_version.py index b8f9775400f4..100d81d4b1ec 100644 --- a/sdk/storage/azure-storage-queue/azure/storage/queue/_version.py +++ b/sdk/storage/azure-storage-queue/azure/storage/queue/_version.py @@ -9,4 +9,4 @@ # regenerated. # -------------------------------------------------------------------------- -VERSION = "12.1.2" +VERSION = "12.1.3" From 4930d99d8e19589f54be954c11fe39611e34bc20 Mon Sep 17 00:00:00 2001 From: Charles Lowell Date: Mon, 15 Jun 2020 16:08:51 -0700 Subject: [PATCH 08/21] Update KeyVaultPreparer with track 2 mgmt changes (#12060) --- tools/azure-sdk-tools/devtools_testutils/keyvault_preparer.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/azure-sdk-tools/devtools_testutils/keyvault_preparer.py b/tools/azure-sdk-tools/devtools_testutils/keyvault_preparer.py index 9128ebf8b255..7c3e76dae689 100644 --- a/tools/azure-sdk-tools/devtools_testutils/keyvault_preparer.py +++ b/tools/azure-sdk-tools/devtools_testutils/keyvault_preparer.py @@ -103,7 +103,7 @@ def create_resource(self, name, **kwargs): retries = 4 for i in range(retries): try: - vault = self.client.vaults.create_or_update(group, name, parameters).result() + vault = self.client.vaults.begin_create_or_update(group, name, parameters).result() break except Exception as ex: if "VaultAlreadyExists" in str(ex): From 485ee381ecec64fb5efb5c56bd0809feec774c8f Mon Sep 17 00:00:00 2001 From: Phoenix He Date: Tue, 16 Jun 2020 13:48:04 +0800 Subject: [PATCH 09/21] Enable track2 SDK Automation config on master branch (#11654) * Update swagger_to_sdk_config.json * Update swagger_to_sdk_config.json * Update swagger_to_sdk_config.json Co-authored-by: Zim Kalinowski --- swagger_to_sdk_config.json | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/swagger_to_sdk_config.json b/swagger_to_sdk_config.json index eeb46999ae52..70a8c6ec6749 100644 --- a/swagger_to_sdk_config.json +++ b/swagger_to_sdk_config.json @@ -2,14 +2,13 @@ "$schema": "https://openapistorageprod.blob.core.windows.net/sdkautomation/prod/schemas/swagger_to_sdk_config.schema.json", "meta": { "autorest_options": { - "version": "V2", - "use": "@microsoft.azure/autorest.python@~4.0.71", + "version": "3.0.6272", + "use": "@autorest/python@5.1.0-preview.1", "python": "", "python-mode": "update", "sdkrel:python-sdks-folder": "./sdk/.", "multiapi": "", - "keep-version-file" :"", - "no-async": "" + "track2" }, "advanced_options": { "create_sdk_pull_requests": true, From f9ffd8e54c0e00e6945fdfcb63d2bf82c39c6913 Mon Sep 17 00:00:00 2001 From: Laurent Mazuel Date: Tue, 16 Jun 2020 09:47:06 -0700 Subject: [PATCH 10/21] Regenerate LUIS (#12064) * Regenerate LUIS * Packaging update of azure-cognitiveservices-language-luis * ChangeLog * Update version Co-authored-by: Azure SDK Bot --- .../CHANGELOG.md | 25 ++ .../README.md | 23 +- .../luis/authoring/_luis_authoring_client.py | 5 - .../language/luis/authoring/models/_models.py | 24 ++ .../luis/authoring/models/_models_py3.py | 34 ++- .../luis/authoring/operations/__init__.py | 2 - .../operations/_examples_operations.py | 18 +- .../operations/_features_operations.py | 24 +- .../authoring/operations/_model_operations.py | 36 ++- .../operations/_permissions_operations.py | 277 ------------------ .../language/luis/version.py | 2 +- .../setup.py | 5 +- 12 files changed, 139 insertions(+), 336 deletions(-) delete mode 100644 sdk/cognitiveservices/azure-cognitiveservices-language-luis/azure/cognitiveservices/language/luis/authoring/operations/_permissions_operations.py diff --git a/sdk/cognitiveservices/azure-cognitiveservices-language-luis/CHANGELOG.md b/sdk/cognitiveservices/azure-cognitiveservices-language-luis/CHANGELOG.md index 4a3e700eb6fd..d5551708186f 100644 --- a/sdk/cognitiveservices/azure-cognitiveservices-language-luis/CHANGELOG.md +++ b/sdk/cognitiveservices/azure-cognitiveservices-language-luis/CHANGELOG.md @@ -1,5 +1,30 @@ # Release History +## 0.7.0 (2020-06-15) + +*Authoring* + +**Features** + + - Model JSONEntity has a new parameter children + - Model EntityLabel has a new parameter children + - Model EntityPrediction has a new parameter children + - Model EntityLabelObject has a new parameter children + - Model ModelFeatureInformation has a new parameter is_required + +**Breaking changes** + + - Operation ExamplesOperations.add has a new signature + - Operation ExamplesOperations.batch has a new signature + - Operation ExamplesOperations.list has a new signature + - Operation ModelOperations.list_entity_suggestions has a new signature + - Operation ModelOperations.list_intent_suggestions has a new signature + - Operation ModelOperations.delete_entity_feature has a new signature + - Operation ModelOperations.delete_intent_feature has a new signature + - Operation FeaturesOperations.add_entity_feature has a new signature + - Operation FeaturesOperations.add_intent_feature has a new signature + - Removed operation group PermissionsOperations + ## 0.6.0 (2020-05-11) *Authoring* diff --git a/sdk/cognitiveservices/azure-cognitiveservices-language-luis/README.md b/sdk/cognitiveservices/azure-cognitiveservices-language-luis/README.md index 96b85318803d..bf537d8e7c08 100644 --- a/sdk/cognitiveservices/azure-cognitiveservices-language-luis/README.md +++ b/sdk/cognitiveservices/azure-cognitiveservices-language-luis/README.md @@ -1,22 +1,21 @@ -## Microsoft Azure SDK for Python +# Microsoft Azure SDK for Python This is the Microsoft Azure Cognitive Services LUIS Client Library. +This package has been tested with Python 2.7, 3.5, 3.6, 3.7 and 3.8. +For a more complete view of Azure libraries, see the [Github repo](https://github.com/Azure/azure-sdk-for-python/) -This package has been tested with Python 2.7, 3.5, 3.6 and 3.7. -For a more complete set of Azure libraries, see the -[azure](https://pypi.python.org/pypi/azure) bundle package. +# Usage -## Usage - -For code examples, see [Cognitive Services -LUIS](https://docs.microsoft.com/python/api/overview/azure/cognitive-services) +For code examples, see [Cognitive Services LUIS](https://docs.microsoft.com/python/api/overview/azure/cognitive-services) on docs.microsoft.com. -## Provide Feedback -If you encounter any bugs or have suggestions, please file an issue in -the [Issues](https://github.com/Azure/azure-sdk-for-python/issues) +# Provide Feedback + +If you encounter any bugs or have suggestions, please file an issue in the +[Issues](https://github.com/Azure/azure-sdk-for-python/issues) section of the project. -![image](https://azure-sdk-impressions.azurewebsites.net/api/impressions/azure-sdk-for-python%2Fazure-cognitiveservices-language-luis%2FREADME.png) + +![Impressions](https://azure-sdk-impressions.azurewebsites.net/api/impressions/azure-sdk-for-python%2Fazure-cognitiveservices-language-luis%2FREADME.png) diff --git a/sdk/cognitiveservices/azure-cognitiveservices-language-luis/azure/cognitiveservices/language/luis/authoring/_luis_authoring_client.py b/sdk/cognitiveservices/azure-cognitiveservices-language-luis/azure/cognitiveservices/language/luis/authoring/_luis_authoring_client.py index 1a4854c8b590..185b9453df69 100644 --- a/sdk/cognitiveservices/azure-cognitiveservices-language-luis/azure/cognitiveservices/language/luis/authoring/_luis_authoring_client.py +++ b/sdk/cognitiveservices/azure-cognitiveservices-language-luis/azure/cognitiveservices/language/luis/authoring/_luis_authoring_client.py @@ -20,7 +20,6 @@ from .operations import AppsOperations from .operations import VersionsOperations from .operations import TrainOperations -from .operations import PermissionsOperations from .operations import PatternOperations from .operations import SettingsOperations from .operations import AzureAccountsOperations @@ -45,8 +44,6 @@ class LUISAuthoringClient(SDKClient): :vartype versions: azure.cognitiveservices.language.luis.authoring.operations.VersionsOperations :ivar train: Train operations :vartype train: azure.cognitiveservices.language.luis.authoring.operations.TrainOperations - :ivar permissions: Permissions operations - :vartype permissions: azure.cognitiveservices.language.luis.authoring.operations.PermissionsOperations :ivar pattern: Pattern operations :vartype pattern: azure.cognitiveservices.language.luis.authoring.operations.PatternOperations :ivar settings: Settings operations @@ -85,8 +82,6 @@ def __init__( self._client, self.config, self._serialize, self._deserialize) self.train = TrainOperations( self._client, self.config, self._serialize, self._deserialize) - self.permissions = PermissionsOperations( - self._client, self.config, self._serialize, self._deserialize) self.pattern = PatternOperations( self._client, self.config, self._serialize, self._deserialize) self.settings = SettingsOperations( diff --git a/sdk/cognitiveservices/azure-cognitiveservices-language-luis/azure/cognitiveservices/language/luis/authoring/models/_models.py b/sdk/cognitiveservices/azure-cognitiveservices-language-luis/azure/cognitiveservices/language/luis/authoring/models/_models.py index b1e37b4e3af3..6071ced8cd28 100644 --- a/sdk/cognitiveservices/azure-cognitiveservices-language-luis/azure/cognitiveservices/language/luis/authoring/models/_models.py +++ b/sdk/cognitiveservices/azure-cognitiveservices-language-luis/azure/cognitiveservices/language/luis/authoring/models/_models.py @@ -862,6 +862,9 @@ class EntityLabel(Model): :type role: str :param role_id: The role id for the predicted entity. :type role_id: str + :param children: + :type children: + list[~azure.cognitiveservices.language.luis.authoring.models.EntityLabel] """ _validation = { @@ -876,6 +879,7 @@ class EntityLabel(Model): 'end_token_index': {'key': 'endTokenIndex', 'type': 'int'}, 'role': {'key': 'role', 'type': 'str'}, 'role_id': {'key': 'roleId', 'type': 'str'}, + 'children': {'key': 'children', 'type': '[EntityLabel]'}, } def __init__(self, **kwargs): @@ -885,6 +889,7 @@ def __init__(self, **kwargs): self.end_token_index = kwargs.get('end_token_index', None) self.role = kwargs.get('role', None) self.role_id = kwargs.get('role_id', None) + self.children = kwargs.get('children', None) class EntityLabelObject(Model): @@ -903,6 +908,9 @@ class EntityLabelObject(Model): :type end_char_index: int :param role: The role the entity plays in the utterance. :type role: str + :param children: The identified entities within the example utterance. + :type children: + list[~azure.cognitiveservices.language.luis.authoring.models.EntityLabelObject] """ _validation = { @@ -916,6 +924,7 @@ class EntityLabelObject(Model): 'start_char_index': {'key': 'startCharIndex', 'type': 'int'}, 'end_char_index': {'key': 'endCharIndex', 'type': 'int'}, 'role': {'key': 'role', 'type': 'str'}, + 'children': {'key': 'children', 'type': '[EntityLabelObject]'}, } def __init__(self, **kwargs): @@ -924,6 +933,7 @@ def __init__(self, **kwargs): self.start_char_index = kwargs.get('start_char_index', None) self.end_char_index = kwargs.get('end_char_index', None) self.role = kwargs.get('role', None) + self.children = kwargs.get('children', None) class EntityModelCreateObject(Model): @@ -1065,6 +1075,9 @@ class EntityPrediction(Model): :type end_token_index: int :param phrase: Required. The actual token(s) that comprise the entity. :type phrase: str + :param children: + :type children: + list[~azure.cognitiveservices.language.luis.authoring.models.EntityPrediction] """ _validation = { @@ -1079,6 +1092,7 @@ class EntityPrediction(Model): 'start_token_index': {'key': 'startTokenIndex', 'type': 'int'}, 'end_token_index': {'key': 'endTokenIndex', 'type': 'int'}, 'phrase': {'key': 'phrase', 'type': 'str'}, + 'children': {'key': 'children', 'type': '[EntityPrediction]'}, } def __init__(self, **kwargs): @@ -1087,6 +1101,7 @@ def __init__(self, **kwargs): self.start_token_index = kwargs.get('start_token_index', None) self.end_token_index = kwargs.get('end_token_index', None) self.phrase = kwargs.get('phrase', None) + self.children = kwargs.get('children', None) class EntityRole(Model): @@ -1618,6 +1633,9 @@ class JSONEntity(Model): :type entity: str :param role: The role the entity plays in the utterance. :type role: str + :param children: + :type children: + list[~azure.cognitiveservices.language.luis.authoring.models.JSONEntity] """ _validation = { @@ -1631,6 +1649,7 @@ class JSONEntity(Model): 'end_pos': {'key': 'endPos', 'type': 'int'}, 'entity': {'key': 'entity', 'type': 'str'}, 'role': {'key': 'role', 'type': 'str'}, + 'children': {'key': 'children', 'type': '[JSONEntity]'}, } def __init__(self, **kwargs): @@ -1639,6 +1658,7 @@ def __init__(self, **kwargs): self.end_pos = kwargs.get('end_pos', None) self.entity = kwargs.get('entity', None) self.role = kwargs.get('role', None) + self.children = kwargs.get('children', None) class JSONModelFeature(Model): @@ -2045,17 +2065,21 @@ class ModelFeatureInformation(Model): :type model_name: str :param feature_name: The name of the feature used. :type feature_name: str + :param is_required: + :type is_required: bool """ _attribute_map = { 'model_name': {'key': 'modelName', 'type': 'str'}, 'feature_name': {'key': 'featureName', 'type': 'str'}, + 'is_required': {'key': 'isRequired', 'type': 'bool'}, } def __init__(self, **kwargs): super(ModelFeatureInformation, self).__init__(**kwargs) self.model_name = kwargs.get('model_name', None) self.feature_name = kwargs.get('feature_name', None) + self.is_required = kwargs.get('is_required', None) class ModelInfoResponse(Model): diff --git a/sdk/cognitiveservices/azure-cognitiveservices-language-luis/azure/cognitiveservices/language/luis/authoring/models/_models_py3.py b/sdk/cognitiveservices/azure-cognitiveservices-language-luis/azure/cognitiveservices/language/luis/authoring/models/_models_py3.py index 55e8311dd71d..a462332757f6 100644 --- a/sdk/cognitiveservices/azure-cognitiveservices-language-luis/azure/cognitiveservices/language/luis/authoring/models/_models_py3.py +++ b/sdk/cognitiveservices/azure-cognitiveservices-language-luis/azure/cognitiveservices/language/luis/authoring/models/_models_py3.py @@ -862,6 +862,9 @@ class EntityLabel(Model): :type role: str :param role_id: The role id for the predicted entity. :type role_id: str + :param children: + :type children: + list[~azure.cognitiveservices.language.luis.authoring.models.EntityLabel] """ _validation = { @@ -876,15 +879,17 @@ class EntityLabel(Model): 'end_token_index': {'key': 'endTokenIndex', 'type': 'int'}, 'role': {'key': 'role', 'type': 'str'}, 'role_id': {'key': 'roleId', 'type': 'str'}, + 'children': {'key': 'children', 'type': '[EntityLabel]'}, } - def __init__(self, *, entity_name: str, start_token_index: int, end_token_index: int, role: str=None, role_id: str=None, **kwargs) -> None: + def __init__(self, *, entity_name: str, start_token_index: int, end_token_index: int, role: str=None, role_id: str=None, children=None, **kwargs) -> None: super(EntityLabel, self).__init__(**kwargs) self.entity_name = entity_name self.start_token_index = start_token_index self.end_token_index = end_token_index self.role = role self.role_id = role_id + self.children = children class EntityLabelObject(Model): @@ -903,6 +908,9 @@ class EntityLabelObject(Model): :type end_char_index: int :param role: The role the entity plays in the utterance. :type role: str + :param children: The identified entities within the example utterance. + :type children: + list[~azure.cognitiveservices.language.luis.authoring.models.EntityLabelObject] """ _validation = { @@ -916,14 +924,16 @@ class EntityLabelObject(Model): 'start_char_index': {'key': 'startCharIndex', 'type': 'int'}, 'end_char_index': {'key': 'endCharIndex', 'type': 'int'}, 'role': {'key': 'role', 'type': 'str'}, + 'children': {'key': 'children', 'type': '[EntityLabelObject]'}, } - def __init__(self, *, entity_name: str, start_char_index: int, end_char_index: int, role: str=None, **kwargs) -> None: + def __init__(self, *, entity_name: str, start_char_index: int, end_char_index: int, role: str=None, children=None, **kwargs) -> None: super(EntityLabelObject, self).__init__(**kwargs) self.entity_name = entity_name self.start_char_index = start_char_index self.end_char_index = end_char_index self.role = role + self.children = children class EntityModelCreateObject(Model): @@ -1065,6 +1075,9 @@ class EntityPrediction(Model): :type end_token_index: int :param phrase: Required. The actual token(s) that comprise the entity. :type phrase: str + :param children: + :type children: + list[~azure.cognitiveservices.language.luis.authoring.models.EntityPrediction] """ _validation = { @@ -1079,14 +1092,16 @@ class EntityPrediction(Model): 'start_token_index': {'key': 'startTokenIndex', 'type': 'int'}, 'end_token_index': {'key': 'endTokenIndex', 'type': 'int'}, 'phrase': {'key': 'phrase', 'type': 'str'}, + 'children': {'key': 'children', 'type': '[EntityPrediction]'}, } - def __init__(self, *, entity_name: str, start_token_index: int, end_token_index: int, phrase: str, **kwargs) -> None: + def __init__(self, *, entity_name: str, start_token_index: int, end_token_index: int, phrase: str, children=None, **kwargs) -> None: super(EntityPrediction, self).__init__(**kwargs) self.entity_name = entity_name self.start_token_index = start_token_index self.end_token_index = end_token_index self.phrase = phrase + self.children = children class EntityRole(Model): @@ -1618,6 +1633,9 @@ class JSONEntity(Model): :type entity: str :param role: The role the entity plays in the utterance. :type role: str + :param children: + :type children: + list[~azure.cognitiveservices.language.luis.authoring.models.JSONEntity] """ _validation = { @@ -1631,14 +1649,16 @@ class JSONEntity(Model): 'end_pos': {'key': 'endPos', 'type': 'int'}, 'entity': {'key': 'entity', 'type': 'str'}, 'role': {'key': 'role', 'type': 'str'}, + 'children': {'key': 'children', 'type': '[JSONEntity]'}, } - def __init__(self, *, start_pos: int, end_pos: int, entity: str, role: str=None, **kwargs) -> None: + def __init__(self, *, start_pos: int, end_pos: int, entity: str, role: str=None, children=None, **kwargs) -> None: super(JSONEntity, self).__init__(**kwargs) self.start_pos = start_pos self.end_pos = end_pos self.entity = entity self.role = role + self.children = children class JSONModelFeature(Model): @@ -2045,17 +2065,21 @@ class ModelFeatureInformation(Model): :type model_name: str :param feature_name: The name of the feature used. :type feature_name: str + :param is_required: + :type is_required: bool """ _attribute_map = { 'model_name': {'key': 'modelName', 'type': 'str'}, 'feature_name': {'key': 'featureName', 'type': 'str'}, + 'is_required': {'key': 'isRequired', 'type': 'bool'}, } - def __init__(self, *, model_name: str=None, feature_name: str=None, **kwargs) -> None: + def __init__(self, *, model_name: str=None, feature_name: str=None, is_required: bool=None, **kwargs) -> None: super(ModelFeatureInformation, self).__init__(**kwargs) self.model_name = model_name self.feature_name = feature_name + self.is_required = is_required class ModelInfoResponse(Model): diff --git a/sdk/cognitiveservices/azure-cognitiveservices-language-luis/azure/cognitiveservices/language/luis/authoring/operations/__init__.py b/sdk/cognitiveservices/azure-cognitiveservices-language-luis/azure/cognitiveservices/language/luis/authoring/operations/__init__.py index afe29f0d3f6a..0d53c240e247 100644 --- a/sdk/cognitiveservices/azure-cognitiveservices-language-luis/azure/cognitiveservices/language/luis/authoring/operations/__init__.py +++ b/sdk/cognitiveservices/azure-cognitiveservices-language-luis/azure/cognitiveservices/language/luis/authoring/operations/__init__.py @@ -15,7 +15,6 @@ from ._apps_operations import AppsOperations from ._versions_operations import VersionsOperations from ._train_operations import TrainOperations -from ._permissions_operations import PermissionsOperations from ._pattern_operations import PatternOperations from ._settings_operations import SettingsOperations from ._azure_accounts_operations import AzureAccountsOperations @@ -27,7 +26,6 @@ 'AppsOperations', 'VersionsOperations', 'TrainOperations', - 'PermissionsOperations', 'PatternOperations', 'SettingsOperations', 'AzureAccountsOperations', diff --git a/sdk/cognitiveservices/azure-cognitiveservices-language-luis/azure/cognitiveservices/language/luis/authoring/operations/_examples_operations.py b/sdk/cognitiveservices/azure-cognitiveservices-language-luis/azure/cognitiveservices/language/luis/authoring/operations/_examples_operations.py index 444b3a037d5c..a25af3a63958 100644 --- a/sdk/cognitiveservices/azure-cognitiveservices-language-luis/azure/cognitiveservices/language/luis/authoring/operations/_examples_operations.py +++ b/sdk/cognitiveservices/azure-cognitiveservices-language-luis/azure/cognitiveservices/language/luis/authoring/operations/_examples_operations.py @@ -36,7 +36,7 @@ def __init__(self, client, config, serializer, deserializer): self.config = config def add( - self, app_id, version_id, example_label_object, custom_headers=None, raw=False, **operation_config): + self, app_id, version_id, example_label_object, enable_nested_children=False, custom_headers=None, raw=False, **operation_config): """Adds a labeled example utterance in a version of the application. :param app_id: The application ID. @@ -47,6 +47,8 @@ def add( expected intent and entities. :type example_label_object: ~azure.cognitiveservices.language.luis.authoring.models.ExampleLabelObject + :param enable_nested_children: Toggles nested/flat format + :type enable_nested_children: bool :param dict custom_headers: headers that will be added to the request :param bool raw: returns the direct response alongside the deserialized response @@ -70,6 +72,8 @@ def add( # Construct parameters query_parameters = {} + if enable_nested_children is not None: + query_parameters['enableNestedChildren'] = self._serialize.query("enable_nested_children", enable_nested_children, 'bool') # Construct headers header_parameters = {} @@ -100,7 +104,7 @@ def add( add.metadata = {'url': '/apps/{appId}/versions/{versionId}/example'} def batch( - self, app_id, version_id, example_label_object_array, custom_headers=None, raw=False, **operation_config): + self, app_id, version_id, example_label_object_array, enable_nested_children=False, custom_headers=None, raw=False, **operation_config): """Adds a batch of labeled example utterances to a version of the application. @@ -111,6 +115,8 @@ def batch( :param example_label_object_array: Array of example utterances. :type example_label_object_array: list[~azure.cognitiveservices.language.luis.authoring.models.ExampleLabelObject] + :param enable_nested_children: Toggles nested/flat format + :type enable_nested_children: bool :param dict custom_headers: headers that will be added to the request :param bool raw: returns the direct response alongside the deserialized response @@ -134,6 +140,8 @@ def batch( # Construct parameters query_parameters = {} + if enable_nested_children is not None: + query_parameters['enableNestedChildren'] = self._serialize.query("enable_nested_children", enable_nested_children, 'bool') # Construct headers header_parameters = {} @@ -166,7 +174,7 @@ def batch( batch.metadata = {'url': '/apps/{appId}/versions/{versionId}/examples'} def list( - self, app_id, version_id, skip=0, take=100, custom_headers=None, raw=False, **operation_config): + self, app_id, version_id, skip=0, take=100, enable_nested_children=False, custom_headers=None, raw=False, **operation_config): """Returns example utterances to be reviewed from a version of the application. @@ -179,6 +187,8 @@ def list( :param take: The number of entries to return. Maximum page size is 500. Default is 100. :type take: int + :param enable_nested_children: Toggles nested/flat format + :type enable_nested_children: bool :param dict custom_headers: headers that will be added to the request :param bool raw: returns the direct response alongside the deserialized response @@ -206,6 +216,8 @@ def list( query_parameters['skip'] = self._serialize.query("skip", skip, 'int', minimum=0) if take is not None: query_parameters['take'] = self._serialize.query("take", take, 'int', maximum=500, minimum=0) + if enable_nested_children is not None: + query_parameters['enableNestedChildren'] = self._serialize.query("enable_nested_children", enable_nested_children, 'bool') # Construct headers header_parameters = {} diff --git a/sdk/cognitiveservices/azure-cognitiveservices-language-luis/azure/cognitiveservices/language/luis/authoring/operations/_features_operations.py b/sdk/cognitiveservices/azure-cognitiveservices-language-luis/azure/cognitiveservices/language/luis/authoring/operations/_features_operations.py index 1b7b477b3311..1843ed6e9d01 100644 --- a/sdk/cognitiveservices/azure-cognitiveservices-language-luis/azure/cognitiveservices/language/luis/authoring/operations/_features_operations.py +++ b/sdk/cognitiveservices/azure-cognitiveservices-language-luis/azure/cognitiveservices/language/luis/authoring/operations/_features_operations.py @@ -421,7 +421,7 @@ def delete_phrase_list( delete_phrase_list.metadata = {'url': '/apps/{appId}/versions/{versionId}/phraselists/{phraselistId}'} def add_intent_feature( - self, app_id, version_id, intent_id, model_name=None, feature_name=None, custom_headers=None, raw=False, **operation_config): + self, app_id, version_id, intent_id, feature_relation_create_object, custom_headers=None, raw=False, **operation_config): """Adds a new feature relation to be used by the intent in a version of the application. @@ -431,10 +431,10 @@ def add_intent_feature( :type version_id: str :param intent_id: The intent classifier ID. :type intent_id: str - :param model_name: The name of the model used. - :type model_name: str - :param feature_name: The name of the feature used. - :type feature_name: str + :param feature_relation_create_object: A Feature relation information + object. + :type feature_relation_create_object: + ~azure.cognitiveservices.language.luis.authoring.models.ModelFeatureInformation :param dict custom_headers: headers that will be added to the request :param bool raw: returns the direct response alongside the deserialized response @@ -447,8 +447,6 @@ def add_intent_feature( :raises: :class:`ErrorResponseException` """ - feature_relation_create_object = models.ModelFeatureInformation(model_name=model_name, feature_name=feature_name) - # Construct URL url = self.add_intent_feature.metadata['url'] path_format_arguments = { @@ -491,7 +489,7 @@ def add_intent_feature( add_intent_feature.metadata = {'url': '/apps/{appId}/versions/{versionId}/intents/{intentId}/features'} def add_entity_feature( - self, app_id, version_id, entity_id, model_name=None, feature_name=None, custom_headers=None, raw=False, **operation_config): + self, app_id, version_id, entity_id, feature_relation_create_object, custom_headers=None, raw=False, **operation_config): """Adds a new feature relation to be used by the entity in a version of the application. @@ -501,10 +499,10 @@ def add_entity_feature( :type version_id: str :param entity_id: The entity extractor ID. :type entity_id: str - :param model_name: The name of the model used. - :type model_name: str - :param feature_name: The name of the feature used. - :type feature_name: str + :param feature_relation_create_object: A Feature relation information + object. + :type feature_relation_create_object: + ~azure.cognitiveservices.language.luis.authoring.models.ModelFeatureInformation :param dict custom_headers: headers that will be added to the request :param bool raw: returns the direct response alongside the deserialized response @@ -517,8 +515,6 @@ def add_entity_feature( :raises: :class:`ErrorResponseException` """ - feature_relation_create_object = models.ModelFeatureInformation(model_name=model_name, feature_name=feature_name) - # Construct URL url = self.add_entity_feature.metadata['url'] path_format_arguments = { diff --git a/sdk/cognitiveservices/azure-cognitiveservices-language-luis/azure/cognitiveservices/language/luis/authoring/operations/_model_operations.py b/sdk/cognitiveservices/azure-cognitiveservices-language-luis/azure/cognitiveservices/language/luis/authoring/operations/_model_operations.py index d3ced6fec0c8..3cb9ff0f4208 100644 --- a/sdk/cognitiveservices/azure-cognitiveservices-language-luis/azure/cognitiveservices/language/luis/authoring/operations/_model_operations.py +++ b/sdk/cognitiveservices/azure-cognitiveservices-language-luis/azure/cognitiveservices/language/luis/authoring/operations/_model_operations.py @@ -1389,7 +1389,7 @@ def replace_intent_features( replace_intent_features.metadata = {'url': '/apps/{appId}/versions/{versionId}/intents/{intentId}/features'} def delete_intent_feature( - self, app_id, version_id, intent_id, model_name=None, feature_name=None, custom_headers=None, raw=False, **operation_config): + self, app_id, version_id, intent_id, feature_relation_delete_object, custom_headers=None, raw=False, **operation_config): """Deletes a relation from the feature relations used by the intent in a version of the application. @@ -1399,10 +1399,10 @@ def delete_intent_feature( :type version_id: str :param intent_id: The intent classifier ID. :type intent_id: str - :param model_name: The name of the model used. - :type model_name: str - :param feature_name: The name of the feature used. - :type feature_name: str + :param feature_relation_delete_object: A feature information object + containing the feature relation to delete. + :type feature_relation_delete_object: + ~azure.cognitiveservices.language.luis.authoring.models.ModelFeatureInformation :param dict custom_headers: headers that will be added to the request :param bool raw: returns the direct response alongside the deserialized response @@ -1415,8 +1415,6 @@ def delete_intent_feature( :raises: :class:`ErrorResponseException` """ - feature_relation_delete_object = models.ModelFeatureInformation(model_name=model_name, feature_name=feature_name) - # Construct URL url = self.delete_intent_feature.metadata['url'] path_format_arguments = { @@ -1587,7 +1585,7 @@ def replace_entity_features( replace_entity_features.metadata = {'url': '/apps/{appId}/versions/{versionId}/entities/{entityId}/features'} def delete_entity_feature( - self, app_id, version_id, entity_id, model_name=None, feature_name=None, custom_headers=None, raw=False, **operation_config): + self, app_id, version_id, entity_id, feature_relation_delete_object, custom_headers=None, raw=False, **operation_config): """Deletes a relation from the feature relations used by the entity in a version of the application. @@ -1597,10 +1595,10 @@ def delete_entity_feature( :type version_id: str :param entity_id: The entity extractor ID. :type entity_id: str - :param model_name: The name of the model used. - :type model_name: str - :param feature_name: The name of the feature used. - :type feature_name: str + :param feature_relation_delete_object: A feature information object + containing the feature relation to delete. + :type feature_relation_delete_object: + ~azure.cognitiveservices.language.luis.authoring.models.ModelFeatureInformation :param dict custom_headers: headers that will be added to the request :param bool raw: returns the direct response alongside the deserialized response @@ -1613,8 +1611,6 @@ def delete_entity_feature( :raises: :class:`ErrorResponseException` """ - feature_relation_delete_object = models.ModelFeatureInformation(model_name=model_name, feature_name=feature_name) - # Construct URL url = self.delete_entity_feature.metadata['url'] path_format_arguments = { @@ -2544,7 +2540,7 @@ def update_sub_list( update_sub_list.metadata = {'url': '/apps/{appId}/versions/{versionId}/closedlists/{clEntityId}/sublists/{subListId}'} def list_intent_suggestions( - self, app_id, version_id, intent_id, take=100, custom_headers=None, raw=False, **operation_config): + self, app_id, version_id, intent_id, take=100, enable_nested_children=False, custom_headers=None, raw=False, **operation_config): """Suggests example utterances that would improve the accuracy of the intent model in a version of the application. @@ -2557,6 +2553,8 @@ def list_intent_suggestions( :param take: The number of entries to return. Maximum page size is 500. Default is 100. :type take: int + :param enable_nested_children: Toggles nested/flat format + :type enable_nested_children: bool :param dict custom_headers: headers that will be added to the request :param bool raw: returns the direct response alongside the deserialized response @@ -2583,6 +2581,8 @@ def list_intent_suggestions( query_parameters = {} if take is not None: query_parameters['take'] = self._serialize.query("take", take, 'int', maximum=500, minimum=0) + if enable_nested_children is not None: + query_parameters['enableNestedChildren'] = self._serialize.query("enable_nested_children", enable_nested_children, 'bool') # Construct headers header_parameters = {} @@ -2609,7 +2609,7 @@ def list_intent_suggestions( list_intent_suggestions.metadata = {'url': '/apps/{appId}/versions/{versionId}/intents/{intentId}/suggest'} def list_entity_suggestions( - self, app_id, version_id, entity_id, take=100, custom_headers=None, raw=False, **operation_config): + self, app_id, version_id, entity_id, take=100, enable_nested_children=False, custom_headers=None, raw=False, **operation_config): """Get suggested example utterances that would improve the accuracy of the entity model in a version of the application. @@ -2622,6 +2622,8 @@ def list_entity_suggestions( :param take: The number of entries to return. Maximum page size is 500. Default is 100. :type take: int + :param enable_nested_children: Toggles nested/flat format + :type enable_nested_children: bool :param dict custom_headers: headers that will be added to the request :param bool raw: returns the direct response alongside the deserialized response @@ -2648,6 +2650,8 @@ def list_entity_suggestions( query_parameters = {} if take is not None: query_parameters['take'] = self._serialize.query("take", take, 'int', maximum=500, minimum=0) + if enable_nested_children is not None: + query_parameters['enableNestedChildren'] = self._serialize.query("enable_nested_children", enable_nested_children, 'bool') # Construct headers header_parameters = {} diff --git a/sdk/cognitiveservices/azure-cognitiveservices-language-luis/azure/cognitiveservices/language/luis/authoring/operations/_permissions_operations.py b/sdk/cognitiveservices/azure-cognitiveservices-language-luis/azure/cognitiveservices/language/luis/authoring/operations/_permissions_operations.py deleted file mode 100644 index 2050f431a60d..000000000000 --- a/sdk/cognitiveservices/azure-cognitiveservices-language-luis/azure/cognitiveservices/language/luis/authoring/operations/_permissions_operations.py +++ /dev/null @@ -1,277 +0,0 @@ -# coding=utf-8 -# -------------------------------------------------------------------------- -# Copyright (c) Microsoft Corporation. All rights reserved. -# Licensed under the MIT License. See License.txt in the project root for -# license information. -# -# Code generated by Microsoft (R) AutoRest Code Generator. -# Changes may cause incorrect behavior and will be lost if the code is -# regenerated. -# -------------------------------------------------------------------------- - -from msrest.pipeline import ClientRawResponse - -from .. import models - - -class PermissionsOperations(object): - """PermissionsOperations operations. - - You should not instantiate directly this class, but create a Client instance that will create it for you and attach it as attribute. - - :param client: Client for service requests. - :param config: Configuration of service client. - :param serializer: An object model serializer. - :param deserializer: An object model deserializer. - """ - - models = models - - def __init__(self, client, config, serializer, deserializer): - - self._client = client - self._serialize = serializer - self._deserialize = deserializer - - self.config = config - - def list( - self, app_id, custom_headers=None, raw=False, **operation_config): - """Gets the list of user emails that have permissions to access your - application. - - :param app_id: The application ID. - :type app_id: str - :param dict custom_headers: headers that will be added to the request - :param bool raw: returns the direct response alongside the - deserialized response - :param operation_config: :ref:`Operation configuration - overrides`. - :return: UserAccessList or ClientRawResponse if raw=true - :rtype: - ~azure.cognitiveservices.language.luis.authoring.models.UserAccessList - or ~msrest.pipeline.ClientRawResponse - :raises: - :class:`ErrorResponseException` - """ - # Construct URL - url = self.list.metadata['url'] - path_format_arguments = { - 'Endpoint': self._serialize.url("self.config.endpoint", self.config.endpoint, 'str', skip_quote=True), - 'appId': self._serialize.url("app_id", app_id, 'str') - } - url = self._client.format_url(url, **path_format_arguments) - - # Construct parameters - query_parameters = {} - - # Construct headers - header_parameters = {} - header_parameters['Accept'] = 'application/json' - if custom_headers: - header_parameters.update(custom_headers) - - # Construct and send request - request = self._client.get(url, query_parameters, header_parameters) - response = self._client.send(request, stream=False, **operation_config) - - if response.status_code not in [200]: - raise models.ErrorResponseException(self._deserialize, response) - - deserialized = None - if response.status_code == 200: - deserialized = self._deserialize('UserAccessList', response) - - if raw: - client_raw_response = ClientRawResponse(deserialized, response) - return client_raw_response - - return deserialized - list.metadata = {'url': '/apps/{appId}/permissions'} - - def add( - self, app_id, email=None, custom_headers=None, raw=False, **operation_config): - """Adds a user to the allowed list of users to access this LUIS - application. Users are added using their email address. - - :param app_id: The application ID. - :type app_id: str - :param email: The email address of the user. - :type email: str - :param dict custom_headers: headers that will be added to the request - :param bool raw: returns the direct response alongside the - deserialized response - :param operation_config: :ref:`Operation configuration - overrides`. - :return: OperationStatus or ClientRawResponse if raw=true - :rtype: - ~azure.cognitiveservices.language.luis.authoring.models.OperationStatus - or ~msrest.pipeline.ClientRawResponse - :raises: - :class:`ErrorResponseException` - """ - user_to_add = models.UserCollaborator(email=email) - - # Construct URL - url = self.add.metadata['url'] - path_format_arguments = { - 'Endpoint': self._serialize.url("self.config.endpoint", self.config.endpoint, 'str', skip_quote=True), - 'appId': self._serialize.url("app_id", app_id, 'str') - } - url = self._client.format_url(url, **path_format_arguments) - - # Construct parameters - query_parameters = {} - - # Construct headers - header_parameters = {} - header_parameters['Accept'] = 'application/json' - header_parameters['Content-Type'] = 'application/json; charset=utf-8' - if custom_headers: - header_parameters.update(custom_headers) - - # Construct body - body_content = self._serialize.body(user_to_add, 'UserCollaborator') - - # Construct and send request - request = self._client.post(url, query_parameters, header_parameters, body_content) - response = self._client.send(request, stream=False, **operation_config) - - if response.status_code not in [200]: - raise models.ErrorResponseException(self._deserialize, response) - - deserialized = None - if response.status_code == 200: - deserialized = self._deserialize('OperationStatus', response) - - if raw: - client_raw_response = ClientRawResponse(deserialized, response) - return client_raw_response - - return deserialized - add.metadata = {'url': '/apps/{appId}/permissions'} - - def delete( - self, app_id, email=None, custom_headers=None, raw=False, **operation_config): - """Removes a user from the allowed list of users to access this LUIS - application. Users are removed using their email address. - - :param app_id: The application ID. - :type app_id: str - :param email: The email address of the user. - :type email: str - :param dict custom_headers: headers that will be added to the request - :param bool raw: returns the direct response alongside the - deserialized response - :param operation_config: :ref:`Operation configuration - overrides`. - :return: OperationStatus or ClientRawResponse if raw=true - :rtype: - ~azure.cognitiveservices.language.luis.authoring.models.OperationStatus - or ~msrest.pipeline.ClientRawResponse - :raises: - :class:`ErrorResponseException` - """ - user_to_delete = models.UserCollaborator(email=email) - - # Construct URL - url = self.delete.metadata['url'] - path_format_arguments = { - 'Endpoint': self._serialize.url("self.config.endpoint", self.config.endpoint, 'str', skip_quote=True), - 'appId': self._serialize.url("app_id", app_id, 'str') - } - url = self._client.format_url(url, **path_format_arguments) - - # Construct parameters - query_parameters = {} - - # Construct headers - header_parameters = {} - header_parameters['Accept'] = 'application/json' - header_parameters['Content-Type'] = 'application/json; charset=utf-8' - if custom_headers: - header_parameters.update(custom_headers) - - # Construct body - body_content = self._serialize.body(user_to_delete, 'UserCollaborator') - - # Construct and send request - request = self._client.delete(url, query_parameters, header_parameters, body_content) - response = self._client.send(request, stream=False, **operation_config) - - if response.status_code not in [200]: - raise models.ErrorResponseException(self._deserialize, response) - - deserialized = None - if response.status_code == 200: - deserialized = self._deserialize('OperationStatus', response) - - if raw: - client_raw_response = ClientRawResponse(deserialized, response) - return client_raw_response - - return deserialized - delete.metadata = {'url': '/apps/{appId}/permissions'} - - def update( - self, app_id, emails=None, custom_headers=None, raw=False, **operation_config): - """Replaces the current user access list with the new list sent in the - body. If an empty list is sent, all access to other users will be - removed. - - :param app_id: The application ID. - :type app_id: str - :param emails: The email address of the users. - :type emails: list[str] - :param dict custom_headers: headers that will be added to the request - :param bool raw: returns the direct response alongside the - deserialized response - :param operation_config: :ref:`Operation configuration - overrides`. - :return: OperationStatus or ClientRawResponse if raw=true - :rtype: - ~azure.cognitiveservices.language.luis.authoring.models.OperationStatus - or ~msrest.pipeline.ClientRawResponse - :raises: - :class:`ErrorResponseException` - """ - collaborators = models.CollaboratorsArray(emails=emails) - - # Construct URL - url = self.update.metadata['url'] - path_format_arguments = { - 'Endpoint': self._serialize.url("self.config.endpoint", self.config.endpoint, 'str', skip_quote=True), - 'appId': self._serialize.url("app_id", app_id, 'str') - } - url = self._client.format_url(url, **path_format_arguments) - - # Construct parameters - query_parameters = {} - - # Construct headers - header_parameters = {} - header_parameters['Accept'] = 'application/json' - header_parameters['Content-Type'] = 'application/json; charset=utf-8' - if custom_headers: - header_parameters.update(custom_headers) - - # Construct body - body_content = self._serialize.body(collaborators, 'CollaboratorsArray') - - # Construct and send request - request = self._client.put(url, query_parameters, header_parameters, body_content) - response = self._client.send(request, stream=False, **operation_config) - - if response.status_code not in [200]: - raise models.ErrorResponseException(self._deserialize, response) - - deserialized = None - if response.status_code == 200: - deserialized = self._deserialize('OperationStatus', response) - - if raw: - client_raw_response = ClientRawResponse(deserialized, response) - return client_raw_response - - return deserialized - update.metadata = {'url': '/apps/{appId}/permissions'} diff --git a/sdk/cognitiveservices/azure-cognitiveservices-language-luis/azure/cognitiveservices/language/luis/version.py b/sdk/cognitiveservices/azure-cognitiveservices-language-luis/azure/cognitiveservices/language/luis/version.py index 901c89378794..21801edec7d8 100644 --- a/sdk/cognitiveservices/azure-cognitiveservices-language-luis/azure/cognitiveservices/language/luis/version.py +++ b/sdk/cognitiveservices/azure-cognitiveservices-language-luis/azure/cognitiveservices/language/luis/version.py @@ -9,4 +9,4 @@ # regenerated. # -------------------------------------------------------------------------- -VERSION = "0.6.0" +VERSION = "0.7.0" diff --git a/sdk/cognitiveservices/azure-cognitiveservices-language-luis/setup.py b/sdk/cognitiveservices/azure-cognitiveservices-language-luis/setup.py index fe8b6a11ec3c..5a52f11c8810 100644 --- a/sdk/cognitiveservices/azure-cognitiveservices-language-luis/setup.py +++ b/sdk/cognitiveservices/azure-cognitiveservices-language-luis/setup.py @@ -36,7 +36,9 @@ pass # Version extraction inspired from 'requests' -with open(os.path.join(package_folder_path, 'version.py'), 'r') as fd: +with open(os.path.join(package_folder_path, 'version.py') + if os.path.exists(os.path.join(package_folder_path, 'version.py')) + else os.path.join(package_folder_path, '_version.py'), 'r') as fd: version = re.search(r'^VERSION\s*=\s*[\'"]([^\'"]*)[\'"]', fd.read(), re.MULTILINE).group(1) @@ -67,6 +69,7 @@ 'Programming Language :: Python :: 3.5', 'Programming Language :: Python :: 3.6', 'Programming Language :: Python :: 3.7', + 'Programming Language :: Python :: 3.8', 'License :: OSI Approved :: MIT License', ], zip_safe=False, From 9920af1b122e8d9c0c4794b8dfc096819e95942d Mon Sep 17 00:00:00 2001 From: Steve Faulkner Date: Tue, 16 Jun 2020 17:41:33 -0500 Subject: [PATCH 11/21] Update Cosmos CODEOWNERS (#11500) Co-authored-by: Steve Faulkner --- .github/CODEOWNERS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index f14d45781737..ae8a8d5863e3 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -25,7 +25,7 @@ /sdk/containerinstance/ @samkreter @xizhamsft /sdk/containerregistry/ @djyou /sdk/containerservice/ @samkreter @zqingqing1 @GaneshaThirumurthi -/sdk/cosmos/ @shurd +/sdk/cosmos/ @southpolesteve @zfoster /sdk/datafactory/ @hvermis /sdk/datalake/ @ro-joowan /sdk/datadatamigration/ @vchske From 37f2988c7d7ea681414b547e790ff15ec7d39b25 Mon Sep 17 00:00:00 2001 From: changlong-liu <59815250+changlong-liu@users.noreply.github.com> Date: Wed, 17 Jun 2020 09:33:29 +0800 Subject: [PATCH 12/21] modify changelog (#12071) * modify changelog * modify changelog --- sdk/appconfiguration/azure-mgmt-appconfiguration/CHANGELOG.md | 1 - sdk/compute/azure-mgmt-compute/CHANGELOG.md | 1 - sdk/eventhub/azure-mgmt-eventhub/CHANGELOG.md | 1 - sdk/monitor/azure-mgmt-monitor/CHANGELOG.md | 1 - sdk/network/azure-mgmt-network/CHANGELOG.md | 1 - sdk/resources/azure-mgmt-resource/CHANGELOG.md | 1 - sdk/storage/azure-mgmt-storage/CHANGELOG.md | 1 - 7 files changed, 7 deletions(-) diff --git a/sdk/appconfiguration/azure-mgmt-appconfiguration/CHANGELOG.md b/sdk/appconfiguration/azure-mgmt-appconfiguration/CHANGELOG.md index 16bc518b8a18..b7d8c55cc3d9 100644 --- a/sdk/appconfiguration/azure-mgmt-appconfiguration/CHANGELOG.md +++ b/sdk/appconfiguration/azure-mgmt-appconfiguration/CHANGELOG.md @@ -3,7 +3,6 @@ ## 1.0.0b1 (2020-06-17) This is beta preview version. -For detailed changelog please refer to equivalent stable version 0.4.0 (https://pypi.org/project/azure-mgmt-appconfiguration/0.4.0/) This version uses a next-generation code generator that introduces important breaking changes, but also important new features (like unified authentication and async programming). diff --git a/sdk/compute/azure-mgmt-compute/CHANGELOG.md b/sdk/compute/azure-mgmt-compute/CHANGELOG.md index 7a76dd60c61f..197a7c9e874c 100644 --- a/sdk/compute/azure-mgmt-compute/CHANGELOG.md +++ b/sdk/compute/azure-mgmt-compute/CHANGELOG.md @@ -3,7 +3,6 @@ ## 17.0.0b1 (2020-06-17) This is beta preview version. -For detailed changelog please refer to equivalent stable version 12.0.0 (https://pypi.org/project/azure-mgmt-compute/12.0.0/) This version uses a next-generation code generator that introduces important breaking changes, but also important new features (like unified authentication and async programming). diff --git a/sdk/eventhub/azure-mgmt-eventhub/CHANGELOG.md b/sdk/eventhub/azure-mgmt-eventhub/CHANGELOG.md index 7101b3d4f0fe..8df9772a1dc1 100644 --- a/sdk/eventhub/azure-mgmt-eventhub/CHANGELOG.md +++ b/sdk/eventhub/azure-mgmt-eventhub/CHANGELOG.md @@ -3,7 +3,6 @@ ## 8.0.0b1 (2020-06-17) This is beta preview version. -For detailed changelog please refer to equivalent stable version 3.1.0 (https://pypi.org/project/azure-mgmt-eventhub/3.1.0/) This version uses a next-generation code generator that introduces important breaking changes, but also important new features (like unified authentication and async programming). diff --git a/sdk/monitor/azure-mgmt-monitor/CHANGELOG.md b/sdk/monitor/azure-mgmt-monitor/CHANGELOG.md index 7b30666e9178..249938eb2da3 100644 --- a/sdk/monitor/azure-mgmt-monitor/CHANGELOG.md +++ b/sdk/monitor/azure-mgmt-monitor/CHANGELOG.md @@ -3,7 +3,6 @@ ## 1.0.0b1 (2020-06-17) This is beta preview version. -For detailed changelog please refer to equivalent stable version 0.9.0 (https://pypi.org/project/azure-mgmt-monitor/0.9.0/) This version uses a next-generation code generator that introduces important breaking changes, but also important new features (like unified authentication and async programming). diff --git a/sdk/network/azure-mgmt-network/CHANGELOG.md b/sdk/network/azure-mgmt-network/CHANGELOG.md index e239d4001969..669764092fbe 100644 --- a/sdk/network/azure-mgmt-network/CHANGELOG.md +++ b/sdk/network/azure-mgmt-network/CHANGELOG.md @@ -3,7 +3,6 @@ ## 15.0.0b1 (2020-06-17) This is beta preview version. -For detailed changelog please refer to equivalent stable version 10.2.0 (https://pypi.org/project/azure-mgmt-network/10.2.0/) This version uses a next-generation code generator that introduces important breaking changes, but also important new features (like unified authentication and async programming). diff --git a/sdk/resources/azure-mgmt-resource/CHANGELOG.md b/sdk/resources/azure-mgmt-resource/CHANGELOG.md index 28aac56f6868..5a5146a0fbd1 100644 --- a/sdk/resources/azure-mgmt-resource/CHANGELOG.md +++ b/sdk/resources/azure-mgmt-resource/CHANGELOG.md @@ -3,7 +3,6 @@ ## 15.0.0b1 (2020-06-17) This is beta preview version. -For detailed changelog please refer to equivalent stable version 10.0.0 (https://pypi.org/project/azure-mgmt-resource/10.0.0/) This version uses a next-generation code generator that introduces important breaking changes, but also important new features (like unified authentication and async programming). diff --git a/sdk/storage/azure-mgmt-storage/CHANGELOG.md b/sdk/storage/azure-mgmt-storage/CHANGELOG.md index 27042fd1de99..6a1a429fc42c 100644 --- a/sdk/storage/azure-mgmt-storage/CHANGELOG.md +++ b/sdk/storage/azure-mgmt-storage/CHANGELOG.md @@ -3,7 +3,6 @@ ## 15.0.0b1 (2020-06-17) This is beta preview version. -For detailed changelog please refer to equivalent stable version 10.0.0 (https://pypi.org/project/azure-mgmt-storage/10.0.0/) This version uses a next-generation code generator that introduces important breaking changes, but also important new features (like unified authentication and async programming). From 450873531f19a39d9de6d1cf795758b07392d87f Mon Sep 17 00:00:00 2001 From: Phoenix He Date: Wed, 17 Jun 2020 10:19:53 +0800 Subject: [PATCH 13/21] Fix format in swagger_to_sdk_config.json (#12083) --- swagger_to_sdk_config.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/swagger_to_sdk_config.json b/swagger_to_sdk_config.json index 70a8c6ec6749..ffccb4bfcb5a 100644 --- a/swagger_to_sdk_config.json +++ b/swagger_to_sdk_config.json @@ -8,7 +8,7 @@ "python-mode": "update", "sdkrel:python-sdks-folder": "./sdk/.", "multiapi": "", - "track2" + "track2": "" }, "advanced_options": { "create_sdk_pull_requests": true, From 005aaecb9b6a9b134a7b9a002406c4cd2073f256 Mon Sep 17 00:00:00 2001 From: changlong-liu <59815250+changlong-liu@users.noreply.github.com> Date: Wed, 17 Jun 2020 12:10:03 +0800 Subject: [PATCH 14/21] reserve 1 more version for storage and network (#12082) --- sdk/network/azure-mgmt-network/CHANGELOG.md | 3 ++- sdk/network/azure-mgmt-network/azure/mgmt/network/_version.py | 2 +- sdk/storage/azure-mgmt-storage/CHANGELOG.md | 2 +- sdk/storage/azure-mgmt-storage/azure/mgmt/storage/_version.py | 2 +- 4 files changed, 5 insertions(+), 4 deletions(-) diff --git a/sdk/network/azure-mgmt-network/CHANGELOG.md b/sdk/network/azure-mgmt-network/CHANGELOG.md index 669764092fbe..613ef86f2b35 100644 --- a/sdk/network/azure-mgmt-network/CHANGELOG.md +++ b/sdk/network/azure-mgmt-network/CHANGELOG.md @@ -1,8 +1,9 @@ # Release History -## 15.0.0b1 (2020-06-17) +## 16.0.0b1 (2020-06-17) This is beta preview version. +For detailed changelog please refer to equivalent stable version 10.2.0 (https://pypi.org/project/azure-mgmt-network/10.2.0/) This version uses a next-generation code generator that introduces important breaking changes, but also important new features (like unified authentication and async programming). diff --git a/sdk/network/azure-mgmt-network/azure/mgmt/network/_version.py b/sdk/network/azure-mgmt-network/azure/mgmt/network/_version.py index 944a54ca9dbd..91f529ff9b7f 100644 --- a/sdk/network/azure-mgmt-network/azure/mgmt/network/_version.py +++ b/sdk/network/azure-mgmt-network/azure/mgmt/network/_version.py @@ -5,4 +5,4 @@ # license information. # -------------------------------------------------------------------------- -VERSION = "15.0.0b1" \ No newline at end of file +VERSION = "16.0.0b1" \ No newline at end of file diff --git a/sdk/storage/azure-mgmt-storage/CHANGELOG.md b/sdk/storage/azure-mgmt-storage/CHANGELOG.md index 6a1a429fc42c..c15d7b806ced 100644 --- a/sdk/storage/azure-mgmt-storage/CHANGELOG.md +++ b/sdk/storage/azure-mgmt-storage/CHANGELOG.md @@ -1,6 +1,6 @@ # Release History -## 15.0.0b1 (2020-06-17) +## 16.0.0b1 (2020-06-17) This is beta preview version. diff --git a/sdk/storage/azure-mgmt-storage/azure/mgmt/storage/_version.py b/sdk/storage/azure-mgmt-storage/azure/mgmt/storage/_version.py index 944a54ca9dbd..91f529ff9b7f 100644 --- a/sdk/storage/azure-mgmt-storage/azure/mgmt/storage/_version.py +++ b/sdk/storage/azure-mgmt-storage/azure/mgmt/storage/_version.py @@ -5,4 +5,4 @@ # license information. # -------------------------------------------------------------------------- -VERSION = "15.0.0b1" \ No newline at end of file +VERSION = "16.0.0b1" \ No newline at end of file From af6657465243825385f7112baf6addfdb120a78e Mon Sep 17 00:00:00 2001 From: Zim Kalinowski Date: Wed, 17 Jun 2020 09:25:37 +0200 Subject: [PATCH 15/21] updating doc references (#12086) * updating doc references * Packaging update of azure-mgmt-eventhub * Packaging update of azure-mgmt-keyvault * Packaging update of azure-mgmt-monitor * Packaging update of azure-mgmt-resource * Packaging update of azure-mgmt-appconfiguration * Packaging update of azure-mgmt-compute * Packaging update of azure-mgmt-storage * Packaging update of azure-mgmt-network * udpated * Packaging update of azure-mgmt-resource * Packaging update of azure-mgmt-monitor * Packaging update of azure-mgmt-storage * Packaging update of azure-mgmt-eventhub * Packaging update of azure-mgmt-network * Packaging update of azure-mgmt-compute Co-authored-by: Azure SDK Bot --- sdk/appconfiguration/azure-mgmt-appconfiguration/README.md | 2 +- .../azure-mgmt-appconfiguration/sdk_packaging.toml | 2 +- sdk/compute/azure-mgmt-compute/README.md | 2 +- sdk/compute/azure-mgmt-compute/sdk_packaging.toml | 2 +- sdk/eventhub/azure-mgmt-eventhub/README.md | 2 +- sdk/eventhub/azure-mgmt-eventhub/sdk_packaging.toml | 2 +- sdk/keyvault/azure-mgmt-keyvault/README.md | 2 +- sdk/keyvault/azure-mgmt-keyvault/sdk_packaging.toml | 2 +- sdk/monitor/azure-mgmt-monitor/README.md | 2 +- sdk/monitor/azure-mgmt-monitor/sdk_packaging.toml | 2 +- sdk/network/azure-mgmt-network/README.md | 2 +- sdk/network/azure-mgmt-network/sdk_packaging.toml | 2 +- sdk/resources/azure-mgmt-resource/README.md | 2 +- sdk/resources/azure-mgmt-resource/sdk_packaging.toml | 2 +- sdk/storage/azure-mgmt-storage/README.md | 2 +- sdk/storage/azure-mgmt-storage/sdk_packaging.toml | 2 +- 16 files changed, 16 insertions(+), 16 deletions(-) diff --git a/sdk/appconfiguration/azure-mgmt-appconfiguration/README.md b/sdk/appconfiguration/azure-mgmt-appconfiguration/README.md index 2e608ec1ca33..46c9a3737991 100644 --- a/sdk/appconfiguration/azure-mgmt-appconfiguration/README.md +++ b/sdk/appconfiguration/azure-mgmt-appconfiguration/README.md @@ -7,7 +7,7 @@ For a more complete view of Azure libraries, see the [Github repo](https://githu # Usage -For code examples, see [App Configuration Management](https://docs.microsoft.com/python/api/overview/azure/) +For code examples, see [App Configuration Management](https://docs.microsoft.com/python/api/overview/azure/?view=azure-python-preview) on docs.microsoft.com. diff --git a/sdk/appconfiguration/azure-mgmt-appconfiguration/sdk_packaging.toml b/sdk/appconfiguration/azure-mgmt-appconfiguration/sdk_packaging.toml index 475e3052001d..33c1518fe5d9 100644 --- a/sdk/appconfiguration/azure-mgmt-appconfiguration/sdk_packaging.toml +++ b/sdk/appconfiguration/azure-mgmt-appconfiguration/sdk_packaging.toml @@ -2,7 +2,7 @@ package_name = "azure-mgmt-appconfiguration" package_nspkg = "azure-mgmt-nspkg" package_pprint_name = "App Configuration Management" -package_doc_id = "" +package_doc_id = "?view=azure-python-preview" is_stable = false is_arm = true need_msrestazure = false diff --git a/sdk/compute/azure-mgmt-compute/README.md b/sdk/compute/azure-mgmt-compute/README.md index 7eb4e5bd9798..69b7ce951cdb 100644 --- a/sdk/compute/azure-mgmt-compute/README.md +++ b/sdk/compute/azure-mgmt-compute/README.md @@ -7,7 +7,7 @@ For a more complete view of Azure libraries, see the [Github repo](https://githu # Usage -For code examples, see [Compute Management](https://docs.microsoft.com/python/api/overview/azure/virtualmachines) +For code examples, see [Compute Management](https://docs.microsoft.com/python/api/overview/azure/virtualmachines?view=azure-python-preview) on docs.microsoft.com. diff --git a/sdk/compute/azure-mgmt-compute/sdk_packaging.toml b/sdk/compute/azure-mgmt-compute/sdk_packaging.toml index c9ad325c9f00..51f4eaa4954f 100644 --- a/sdk/compute/azure-mgmt-compute/sdk_packaging.toml +++ b/sdk/compute/azure-mgmt-compute/sdk_packaging.toml @@ -1,7 +1,7 @@ [packaging] package_name = "azure-mgmt-compute" package_pprint_name = "Compute Management" -package_doc_id = "virtualmachines" +package_doc_id = "virtualmachines?view=azure-python-preview" is_stable = true need_msrestazure = false need_azurecore = true diff --git a/sdk/eventhub/azure-mgmt-eventhub/README.md b/sdk/eventhub/azure-mgmt-eventhub/README.md index 4e106d7738e8..379b41e66b9f 100644 --- a/sdk/eventhub/azure-mgmt-eventhub/README.md +++ b/sdk/eventhub/azure-mgmt-eventhub/README.md @@ -7,7 +7,7 @@ For a more complete view of Azure libraries, see the [Github repo](https://githu # Usage -For code examples, see [EventHub Management](https://docs.microsoft.com/python/api/overview/azure/event-hub) +For code examples, see [EventHub Management](https://docs.microsoft.com/python/api/overview/azure/event-hub?view=azure-python-preview) on docs.microsoft.com. diff --git a/sdk/eventhub/azure-mgmt-eventhub/sdk_packaging.toml b/sdk/eventhub/azure-mgmt-eventhub/sdk_packaging.toml index c6caa55ed6fe..06e47cc95671 100644 --- a/sdk/eventhub/azure-mgmt-eventhub/sdk_packaging.toml +++ b/sdk/eventhub/azure-mgmt-eventhub/sdk_packaging.toml @@ -1,7 +1,7 @@ [packaging] package_name = "azure-mgmt-eventhub" package_pprint_name = "EventHub Management" -package_doc_id = "event-hub" +package_doc_id = "event-hub?view=azure-python-preview" is_stable = true need_msrestazure = false need_azurecore = true diff --git a/sdk/keyvault/azure-mgmt-keyvault/README.md b/sdk/keyvault/azure-mgmt-keyvault/README.md index 364ed03365cb..29b62886db32 100644 --- a/sdk/keyvault/azure-mgmt-keyvault/README.md +++ b/sdk/keyvault/azure-mgmt-keyvault/README.md @@ -7,7 +7,7 @@ For a more complete view of Azure libraries, see the [Github repo](https://githu # Usage -For code examples, see [MyService Management](https://docs.microsoft.com/python/api/overview/azure/) +For code examples, see [MyService Management](https://docs.microsoft.com/python/api/overview/azure/?view=azure-python-preview) on docs.microsoft.com. diff --git a/sdk/keyvault/azure-mgmt-keyvault/sdk_packaging.toml b/sdk/keyvault/azure-mgmt-keyvault/sdk_packaging.toml index 1fe6fdf19239..bd7e5d1e5519 100644 --- a/sdk/keyvault/azure-mgmt-keyvault/sdk_packaging.toml +++ b/sdk/keyvault/azure-mgmt-keyvault/sdk_packaging.toml @@ -2,7 +2,7 @@ package_name = "azure-mgmt-keyvault" package_nspkg = "azure-mgmt-nspkg" package_pprint_name = "MyService Management" -package_doc_id = "" +package_doc_id = "?view=azure-python-preview" is_stable = false is_arm = true need_msrestazure = false diff --git a/sdk/monitor/azure-mgmt-monitor/README.md b/sdk/monitor/azure-mgmt-monitor/README.md index 63160ba1e003..343d88de5c06 100644 --- a/sdk/monitor/azure-mgmt-monitor/README.md +++ b/sdk/monitor/azure-mgmt-monitor/README.md @@ -7,7 +7,7 @@ For a more complete view of Azure libraries, see the [Github repo](https://githu # Usage -For code examples, see [Monitor](https://docs.microsoft.com/python/api/overview/azure/monitoring) +For code examples, see [Monitor](https://docs.microsoft.com/python/api/overview/azure/monitoring?view=azure-python-preview) on docs.microsoft.com. diff --git a/sdk/monitor/azure-mgmt-monitor/sdk_packaging.toml b/sdk/monitor/azure-mgmt-monitor/sdk_packaging.toml index a5ddcc7d735b..063a4ad7e2d9 100644 --- a/sdk/monitor/azure-mgmt-monitor/sdk_packaging.toml +++ b/sdk/monitor/azure-mgmt-monitor/sdk_packaging.toml @@ -1,7 +1,7 @@ [packaging] package_name = "azure-mgmt-monitor" package_pprint_name = "Monitor" -package_doc_id = "monitoring" +package_doc_id = "monitoring?view=azure-python-preview" is_stable = false need_msrestazure = false need_azurecore = true diff --git a/sdk/network/azure-mgmt-network/README.md b/sdk/network/azure-mgmt-network/README.md index 9137078a48f9..e9eb0ebf0244 100644 --- a/sdk/network/azure-mgmt-network/README.md +++ b/sdk/network/azure-mgmt-network/README.md @@ -7,7 +7,7 @@ For a more complete view of Azure libraries, see the [Github repo](https://githu # Usage -For code examples, see [Network Management](https://docs.microsoft.com/python/api/overview/azure/network) +For code examples, see [Network Management](https://docs.microsoft.com/python/api/overview/azure/network?view=azure-python-preview) on docs.microsoft.com. diff --git a/sdk/network/azure-mgmt-network/sdk_packaging.toml b/sdk/network/azure-mgmt-network/sdk_packaging.toml index c53dc599b647..8c46553dc54a 100644 --- a/sdk/network/azure-mgmt-network/sdk_packaging.toml +++ b/sdk/network/azure-mgmt-network/sdk_packaging.toml @@ -1,7 +1,7 @@ [packaging] package_name = "azure-mgmt-network" package_pprint_name = "Network Management" -package_doc_id = "network" +package_doc_id = "network?view=azure-python-preview" is_stable = true is_arm = true need_msrestazure = false diff --git a/sdk/resources/azure-mgmt-resource/README.md b/sdk/resources/azure-mgmt-resource/README.md index bc1a2096c766..eb217cef93ad 100644 --- a/sdk/resources/azure-mgmt-resource/README.md +++ b/sdk/resources/azure-mgmt-resource/README.md @@ -7,7 +7,7 @@ For a more complete view of Azure libraries, see the [Github repo](https://githu # Usage -For code examples, see [Resource Management](https://docs.microsoft.com/python/api/overview/azure/resources) +For code examples, see [Resource Management](https://docs.microsoft.com/python/api/overview/azure/resources?view=azure-python-preview) on docs.microsoft.com. diff --git a/sdk/resources/azure-mgmt-resource/sdk_packaging.toml b/sdk/resources/azure-mgmt-resource/sdk_packaging.toml index c3869ba706c5..8568ba9b05ea 100644 --- a/sdk/resources/azure-mgmt-resource/sdk_packaging.toml +++ b/sdk/resources/azure-mgmt-resource/sdk_packaging.toml @@ -1,7 +1,7 @@ [packaging] package_name = "azure-mgmt-resource" package_pprint_name = "Resource Management" -package_doc_id = "resources" +package_doc_id = "resources?view=azure-python-preview" is_stable = true need_msrestazure = false need_azurecore = true diff --git a/sdk/storage/azure-mgmt-storage/README.md b/sdk/storage/azure-mgmt-storage/README.md index 5416db7f2b40..3d433d30c1e3 100644 --- a/sdk/storage/azure-mgmt-storage/README.md +++ b/sdk/storage/azure-mgmt-storage/README.md @@ -7,7 +7,7 @@ For a more complete view of Azure libraries, see the [Github repo](https://githu # Usage -For code examples, see [Storage Management](https://docs.microsoft.com/python/api/overview/azure/storage) +For code examples, see [Storage Management](https://docs.microsoft.com/python/api/overview/azure/storage?view=azure-python-preview) on docs.microsoft.com. diff --git a/sdk/storage/azure-mgmt-storage/sdk_packaging.toml b/sdk/storage/azure-mgmt-storage/sdk_packaging.toml index ce3d679ed573..66ebe77c72a6 100644 --- a/sdk/storage/azure-mgmt-storage/sdk_packaging.toml +++ b/sdk/storage/azure-mgmt-storage/sdk_packaging.toml @@ -1,7 +1,7 @@ [packaging] package_name = "azure-mgmt-storage" package_pprint_name = "Storage Management" -package_doc_id = "storage" +package_doc_id = "storage?view=azure-python-preview" is_stable = true need_msrestazure = false need_azurecore = true From 83bf9a5581d6a14a00afdcf2aec11e4daf54146b Mon Sep 17 00:00:00 2001 From: Zim Kalinowski Date: Wed, 17 Jun 2020 11:07:15 +0200 Subject: [PATCH 16/21] syncing missing changelog items (#12089) --- sdk/eventhub/azure-mgmt-eventhub/CHANGELOG.md | 18 ++++++++++++++++++ sdk/monitor/azure-mgmt-monitor/CHANGELOG.md | 11 +++++++++++ sdk/network/azure-mgmt-network/CHANGELOG.md | 18 ++++++++++++++++++ 3 files changed, 47 insertions(+) diff --git a/sdk/eventhub/azure-mgmt-eventhub/CHANGELOG.md b/sdk/eventhub/azure-mgmt-eventhub/CHANGELOG.md index 8df9772a1dc1..7fd58b7c11a4 100644 --- a/sdk/eventhub/azure-mgmt-eventhub/CHANGELOG.md +++ b/sdk/eventhub/azure-mgmt-eventhub/CHANGELOG.md @@ -30,6 +30,24 @@ This version uses a next-generation code generator that introduces important bre - This client has now stable and official support for async. Check the `aio` namespace of your package to find the async client. - This client now support natively tracing library like OpenCensus or OpenTelemetry. See this [tracing quickstart](https://github.com/Azure/azure-sdk-for-python/tree/master/sdk/core/azure-core-tracing-opentelemetry) for an overview. +## 4.0.0 (2020-06-12) + +**Features** + + - Model Cluster has a new parameter updated_at + - Model Cluster has a new parameter created_at + - Added operation ClustersOperations.update + - Added operation ClustersOperations.create_or_update + - Added operation ClustersOperations.list_available_cluster_region + +** Breaking changes** + + - Model Cluster no longer has parameter created + - Model Cluster no longer has parameter updated + - Removed operation ClustersOperations.put + - Removed operation ClustersOperations.list_available_clusters + - Removed operation ClustersOperations.patch + ## 3.1.0 (2020-05-13) **Features** diff --git a/sdk/monitor/azure-mgmt-monitor/CHANGELOG.md b/sdk/monitor/azure-mgmt-monitor/CHANGELOG.md index 249938eb2da3..8390be1cba7e 100644 --- a/sdk/monitor/azure-mgmt-monitor/CHANGELOG.md +++ b/sdk/monitor/azure-mgmt-monitor/CHANGELOG.md @@ -31,6 +31,17 @@ This version uses a next-generation code generator that introduces important bre - This client now support natively tracing library like OpenCensus or OpenTelemetry. See this [tracing quickstart](https://github.com/Azure/azure-sdk-for-python/tree/master/sdk/core/azure-core-tracing-opentelemetry) for an overview. +## 0.10.0 (2020-06-08) + +**Features** + + - Model WebtestLocationAvailabilityCriteria has a new parameter additional_properties + - Added operation group SubscriptionDiagnosticSettingsOperations + +**Breaking changes** + + - Model WebtestLocationAvailabilityCriteria has a new required parameter odatatype + ## 0.9.0 (2020-04-09) **Features** diff --git a/sdk/network/azure-mgmt-network/CHANGELOG.md b/sdk/network/azure-mgmt-network/CHANGELOG.md index 613ef86f2b35..66a0b3536c6a 100644 --- a/sdk/network/azure-mgmt-network/CHANGELOG.md +++ b/sdk/network/azure-mgmt-network/CHANGELOG.md @@ -32,6 +32,24 @@ This version uses a next-generation code generator that introduces important bre - This client now support natively tracing library like OpenCensus or OpenTelemetry. See this [tracing quickstart](https://github.com/Azure/azure-sdk-for-python/tree/master/sdk/core/azure-core-tracing-opentelemetry) for an overview. +## 10.2.0 (2020-04-10) + +**Features** + + - Model VpnConnection has a new parameter routing_configuration + - Model NatRuleCondition has a new parameter terminate_tls + - Model HubVirtualNetworkConnection has a new parameter routing_configuration + - Model ExpressRouteConnection has a new parameter routing_configuration + - Model FirewallPolicy has a new parameter transport_security + - Model FirewallPolicy has a new parameter identity + - Model FirewallPolicy has a new parameter threat_intel_whitelist + - Model ApplicationRuleCondition has a new parameter target_urls + - Model P2SConnectionConfiguration has a new parameter routing_configuration + - Model BackendAddressPool has a new parameter load_balancer_backend_addresses + - Added operation LoadBalancerBackendAddressPoolsOperations.create_or_update + - Added operation LoadBalancerBackendAddressPoolsOperations.delete + - Added operation group HubRouteTablesOperations + ## 10.1.0 (2020-04-10) **Features** From 99799d510538035e89d6525a6c10aa526263ba0d Mon Sep 17 00:00:00 2001 From: Krista Pratico Date: Wed, 17 Jun 2020 08:43:19 -0700 Subject: [PATCH 17/21] [formrecognizer] test parity with other languages (#12059) * add encoding check test * add copy test bad model id * test big endian tiff * add missing blank page test for custom forms * update tests to use GlobalClientPreparer --- ...ent_from_url.test_content_encoded_url.yaml | 42 ++ ...om_url_async.test_content_encoded_url.yaml | 30 + ...del.test_copy_model_fail_bad_model_id.yaml | 334 +++++++++ ...ync.test_copy_model_fail_bad_model_id.yaml | 237 ++++++ ...s.test_custom_form_labeled_blank_page.yaml | 668 +++++++++++++++++ ...test_custom_form_unlabeled_blank_page.yaml | 696 ++++++++++++++++++ ...c.test_custom_form_labeled_blank_page.yaml | 577 +++++++++++++++ ...test_custom_form_unlabeled_blank_page.yaml | 618 ++++++++++++++++ ...rom_url.test_custom_forms_encoded_url.yaml | 42 ++ ...l_async.test_custom_forms_encoded_url.yaml | 30 + ...pt_from_url.test_receipts_encoded_url.yaml | 43 ++ ...m_url_async.test_receipts_encoded_url.yaml | 31 + ...st_training.test_training_encoded_url.yaml | 638 ++++++++++++++++ ...ining_async.test_training_encoded_url.yaml | 433 +++++++++++ .../tests/test_content_from_url.py | 8 + .../tests/test_content_from_url_async.py | 8 + .../tests/test_content_type.py | 8 +- .../tests/test_copy_model.py | 15 + .../tests/test_copy_model_async.py | 15 + .../tests/test_custom_forms.py | 42 ++ .../tests/test_custom_forms_async.py | 42 ++ .../tests/test_custom_forms_from_url.py | 11 + .../tests/test_custom_forms_from_url_async.py | 11 + .../tests/test_receipt_from_url.py | 8 + .../tests/test_receipt_from_url_async.py | 8 + .../tests/test_training.py | 11 + .../tests/test_training_async.py | 11 + 27 files changed, 4615 insertions(+), 2 deletions(-) create mode 100644 sdk/formrecognizer/azure-ai-formrecognizer/tests/recordings/test_content_from_url.test_content_encoded_url.yaml create mode 100644 sdk/formrecognizer/azure-ai-formrecognizer/tests/recordings/test_content_from_url_async.test_content_encoded_url.yaml create mode 100644 sdk/formrecognizer/azure-ai-formrecognizer/tests/recordings/test_copy_model.test_copy_model_fail_bad_model_id.yaml create mode 100644 sdk/formrecognizer/azure-ai-formrecognizer/tests/recordings/test_copy_model_async.test_copy_model_fail_bad_model_id.yaml create mode 100644 sdk/formrecognizer/azure-ai-formrecognizer/tests/recordings/test_custom_forms.test_custom_form_labeled_blank_page.yaml create mode 100644 sdk/formrecognizer/azure-ai-formrecognizer/tests/recordings/test_custom_forms.test_custom_form_unlabeled_blank_page.yaml create mode 100644 sdk/formrecognizer/azure-ai-formrecognizer/tests/recordings/test_custom_forms_async.test_custom_form_labeled_blank_page.yaml create mode 100644 sdk/formrecognizer/azure-ai-formrecognizer/tests/recordings/test_custom_forms_async.test_custom_form_unlabeled_blank_page.yaml create mode 100644 sdk/formrecognizer/azure-ai-formrecognizer/tests/recordings/test_custom_forms_from_url.test_custom_forms_encoded_url.yaml create mode 100644 sdk/formrecognizer/azure-ai-formrecognizer/tests/recordings/test_custom_forms_from_url_async.test_custom_forms_encoded_url.yaml create mode 100644 sdk/formrecognizer/azure-ai-formrecognizer/tests/recordings/test_receipt_from_url.test_receipts_encoded_url.yaml create mode 100644 sdk/formrecognizer/azure-ai-formrecognizer/tests/recordings/test_receipt_from_url_async.test_receipts_encoded_url.yaml create mode 100644 sdk/formrecognizer/azure-ai-formrecognizer/tests/recordings/test_training.test_training_encoded_url.yaml create mode 100644 sdk/formrecognizer/azure-ai-formrecognizer/tests/recordings/test_training_async.test_training_encoded_url.yaml diff --git a/sdk/formrecognizer/azure-ai-formrecognizer/tests/recordings/test_content_from_url.test_content_encoded_url.yaml b/sdk/formrecognizer/azure-ai-formrecognizer/tests/recordings/test_content_from_url.test_content_encoded_url.yaml new file mode 100644 index 000000000000..fe1fbe51a0f6 --- /dev/null +++ b/sdk/formrecognizer/azure-ai-formrecognizer/tests/recordings/test_content_from_url.test_content_encoded_url.yaml @@ -0,0 +1,42 @@ +interactions: +- request: + body: 'b''{"source": "https://fakeuri.com/blank%20space"}''' + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + Content-Length: + - '47' + Content-Type: + - application/json + User-Agent: + - azsdk-python-ai-formrecognizer/1.0.0b4 Python/3.7.3 (Windows-10-10.0.18362-SP0) + Python/3.7.3 (Windows-10-10.0.18362-SP0) + method: POST + uri: https://centraluseuap.api.cognitive.microsoft.com/formrecognizer/v2.0-preview/layout/analyze + response: + body: + string: '{"error": {"code": "FailedToDownloadImage", "message": "Failed to download + image from input URL."}}' + headers: + apim-request-id: + - 0801fae5-b315-4d33-9df7-c6fb8333d4db + content-type: + - application/json; charset=utf-8 + date: + - Mon, 15 Jun 2020 18:08:28 GMT + strict-transport-security: + - max-age=31536000; includeSubDomains; preload + transfer-encoding: + - chunked + x-content-type-options: + - nosniff + x-envoy-upstream-service-time: + - '2496' + status: + code: 400 + message: Bad Request +version: 1 diff --git a/sdk/formrecognizer/azure-ai-formrecognizer/tests/recordings/test_content_from_url_async.test_content_encoded_url.yaml b/sdk/formrecognizer/azure-ai-formrecognizer/tests/recordings/test_content_from_url_async.test_content_encoded_url.yaml new file mode 100644 index 000000000000..52dbdba891f1 --- /dev/null +++ b/sdk/formrecognizer/azure-ai-formrecognizer/tests/recordings/test_content_from_url_async.test_content_encoded_url.yaml @@ -0,0 +1,30 @@ +interactions: +- request: + body: 'b''{"source": "https://fakeuri.com/blank%20space"}''' + headers: + Content-Length: + - '47' + Content-Type: + - application/json + User-Agent: + - azsdk-python-ai-formrecognizer/1.0.0b4 Python/3.7.3 (Windows-10-10.0.18362-SP0) + Python/3.7.3 (Windows-10-10.0.18362-SP0) + method: POST + uri: https://centraluseuap.api.cognitive.microsoft.com/formrecognizer/v2.0-preview/layout/analyze + response: + body: + string: '{"error": {"code": "FailedToDownloadImage", "message": "Failed to download + image from input URL."}}' + headers: + apim-request-id: ba66c94f-af17-4785-a6d6-7c04ca117d87 + content-type: application/json; charset=utf-8 + date: Mon, 15 Jun 2020 18:18:10 GMT + strict-transport-security: max-age=31536000; includeSubDomains; preload + transfer-encoding: chunked + x-content-type-options: nosniff + x-envoy-upstream-service-time: '490' + status: + code: 400 + message: Bad Request + url: https://centraluseuap.api.cognitive.microsoft.com//formrecognizer/v2.0-preview/layout/analyze +version: 1 diff --git a/sdk/formrecognizer/azure-ai-formrecognizer/tests/recordings/test_copy_model.test_copy_model_fail_bad_model_id.yaml b/sdk/formrecognizer/azure-ai-formrecognizer/tests/recordings/test_copy_model.test_copy_model_fail_bad_model_id.yaml new file mode 100644 index 000000000000..17ea3b612580 --- /dev/null +++ b/sdk/formrecognizer/azure-ai-formrecognizer/tests/recordings/test_copy_model.test_copy_model_fail_bad_model_id.yaml @@ -0,0 +1,334 @@ +interactions: +- request: + body: 'b''b\''{"source": "containersasurl", "sourceFilter": {"prefix": "", "includeSubFolders": + false}, "useLabelFile": false}\''''' + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + Content-Length: + - '288' + Content-Type: + - application/json + User-Agent: + - azsdk-python-ai-formrecognizer/1.0.0b4 Python/3.7.3 (Windows-10-10.0.18362-SP0) + Python/3.7.3 (Windows-10-10.0.18362-SP0) + method: POST + uri: https://centraluseuap.api.cognitive.microsoft.com/formrecognizer/v2.0-preview/custom/models + response: + body: + string: '' + headers: + apim-request-id: + - f21540e1-b85e-4278-bb42-5de47553a8b1 + content-length: + - '0' + date: + - Mon, 15 Jun 2020 19:26:39 GMT + location: + - https://centraluseuap.api.cognitive.microsoft.com/formrecognizer/v2.0-preview/custom/models/6b520a60-a98f-45b1-b67d-0f954dc951ef + strict-transport-security: + - max-age=31536000; includeSubDomains; preload + x-content-type-options: + - nosniff + x-envoy-upstream-service-time: + - '355' + status: + code: 201 + message: Created +- request: + body: null + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + User-Agent: + - azsdk-python-ai-formrecognizer/1.0.0b4 Python/3.7.3 (Windows-10-10.0.18362-SP0) + Python/3.7.3 (Windows-10-10.0.18362-SP0) + method: GET + uri: https://centraluseuap.api.cognitive.microsoft.com/formrecognizer/v2.0-preview/custom/models/6b520a60-a98f-45b1-b67d-0f954dc951ef?includeKeys=true + response: + body: + string: '{"modelInfo": {"modelId": "6b520a60-a98f-45b1-b67d-0f954dc951ef", "status": + "creating", "createdDateTime": "2020-06-15T19:26:39Z", "lastUpdatedDateTime": + "2020-06-15T19:26:39Z"}}' + headers: + apim-request-id: + - 63c03928-e2a3-4a34-8807-b7d151834dbf + content-type: + - application/json; charset=utf-8 + date: + - Mon, 15 Jun 2020 19:26:49 GMT + strict-transport-security: + - max-age=31536000; includeSubDomains; preload + transfer-encoding: + - chunked + x-content-type-options: + - nosniff + x-envoy-upstream-service-time: + - '5202' + status: + code: 200 + message: OK +- request: + body: null + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + User-Agent: + - azsdk-python-ai-formrecognizer/1.0.0b4 Python/3.7.3 (Windows-10-10.0.18362-SP0) + Python/3.7.3 (Windows-10-10.0.18362-SP0) + method: GET + uri: https://centraluseuap.api.cognitive.microsoft.com/formrecognizer/v2.0-preview/custom/models/6b520a60-a98f-45b1-b67d-0f954dc951ef?includeKeys=true + response: + body: + string: '{"modelInfo": {"modelId": "6b520a60-a98f-45b1-b67d-0f954dc951ef", "status": + "ready", "createdDateTime": "2020-06-15T19:26:39Z", "lastUpdatedDateTime": + "2020-06-15T19:26:50Z"}, "keys": {"clusters": {"0": ["Additional Notes:", + "Address:", "Company Name:", "Company Phone:", "Dated As:", "Details", "Email:", + "Hero Limited", "Name:", "Phone:", "Purchase Order", "Purchase Order #:", + "Quantity", "SUBTOTAL", "Seattle, WA 93849 Phone:", "Shipped From", "Shipped + To", "TAX", "TOTAL", "Total", "Unit Price", "Vendor Name:", "Website:"]}}, + "trainResult": {"trainingDocuments": [{"documentName": "Form_1.jpg", "pages": + 1, "errors": [], "status": "succeeded"}, {"documentName": "Form_2.jpg", "pages": + 1, "errors": [], "status": "succeeded"}, {"documentName": "Form_3.jpg", "pages": + 1, "errors": [], "status": "succeeded"}, {"documentName": "Form_4.jpg", "pages": + 1, "errors": [], "status": "succeeded"}, {"documentName": "Form_5.jpg", "pages": + 1, "errors": [], "status": "succeeded"}], "errors": []}}' + headers: + apim-request-id: + - a6825139-aaca-42fd-aa6e-71c9ce2b7cc3 + content-type: + - application/json; charset=utf-8 + date: + - Mon, 15 Jun 2020 19:26:55 GMT + strict-transport-security: + - max-age=31536000; includeSubDomains; preload + transfer-encoding: + - chunked + x-content-type-options: + - nosniff + x-envoy-upstream-service-time: + - '207' + status: + code: 200 + message: OK +- request: + body: null + headers: + Accept: + - application/json + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + Content-Length: + - '0' + User-Agent: + - azsdk-python-ai-formrecognizer/1.0.0b4 Python/3.7.3 (Windows-10-10.0.18362-SP0) + Python/3.7.3 (Windows-10-10.0.18362-SP0) + method: POST + uri: https://centraluseuap.api.cognitive.microsoft.com/formrecognizer/v2.0-preview/custom/models/copyAuthorization + response: + body: + string: '{"modelId": "21c912d3-898a-41bc-8dd4-adf96f6c5e89", "accessToken": + "redacted", "expirationDateTimeTicks": 1592335615}' + headers: + apim-request-id: + - 429dcd52-6812-4a60-85f7-e0ab17b0c832 + content-type: + - application/json; charset=utf-8 + date: + - Mon, 15 Jun 2020 19:26:55 GMT + location: + - https://centraluseuap.api.cognitive.microsoft.com/formrecognizer/v2.0-preview/custom/models/21c912d3-898a-41bc-8dd4-adf96f6c5e89 + strict-transport-security: + - max-age=31536000; includeSubDomains; preload + transfer-encoding: + - chunked + x-content-type-options: + - nosniff + x-envoy-upstream-service-time: + - '191' + status: + code: 201 + message: Created +- request: + body: 'b''b\''{"targetResourceId": "resource_id", "targetResourceRegion": "centraluseuap", + "copyAuthorization": {"modelId": "21c912d3-898a-41bc-8dd4-adf96f6c5e89", "accessToken": + 00000000-0000-0000-0000-000000000000, "expirationDateTimeTicks": 1592335615}}\''''' + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + Content-Length: + - '447' + Content-Type: + - application/json + User-Agent: + - azsdk-python-ai-formrecognizer/1.0.0b4 Python/3.7.3 (Windows-10-10.0.18362-SP0) + Python/3.7.3 (Windows-10-10.0.18362-SP0) + method: POST + uri: https://centraluseuap.api.cognitive.microsoft.com/formrecognizer/v2.0-preview/custom/models/00000000-0000-0000-0000-000000000000/copy + response: + body: + string: '{"error": {"code": "1999", "message": "An error occurred during model + copy. Retry the operation or contact service administrator if the issue persists."}}' + headers: + apim-request-id: + - a150c173-e10a-402a-9c35-1abb75a46d56 + content-type: + - application/json; charset=utf-8 + date: + - Mon, 15 Jun 2020 19:26:55 GMT + strict-transport-security: + - max-age=31536000; includeSubDomains; preload + transfer-encoding: + - chunked + x-content-type-options: + - nosniff + x-envoy-upstream-service-time: + - '37' + status: + code: 500 + message: Internal Server Error +- request: + body: 'b''b\''{"targetResourceId": "resource_id", "targetResourceRegion": "centraluseuap", + "copyAuthorization": {"modelId": "21c912d3-898a-41bc-8dd4-adf96f6c5e89", "accessToken": + 00000000-0000-0000-0000-000000000000, "expirationDateTimeTicks": 1592335615}}\''''' + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + Content-Length: + - '447' + Content-Type: + - application/json + User-Agent: + - azsdk-python-ai-formrecognizer/1.0.0b4 Python/3.7.3 (Windows-10-10.0.18362-SP0) + Python/3.7.3 (Windows-10-10.0.18362-SP0) + method: POST + uri: https://centraluseuap.api.cognitive.microsoft.com/formrecognizer/v2.0-preview/custom/models/00000000-0000-0000-0000-000000000000/copy + response: + body: + string: '{"error": {"code": "1999", "message": "An error occurred during model + copy. Retry the operation or contact service administrator if the issue persists."}}' + headers: + apim-request-id: + - 8a77f21c-fbd1-4798-a991-b22c8bfc3b5f + content-type: + - application/json; charset=utf-8 + date: + - Mon, 15 Jun 2020 19:26:56 GMT + strict-transport-security: + - max-age=31536000; includeSubDomains; preload + transfer-encoding: + - chunked + x-content-type-options: + - nosniff + x-envoy-upstream-service-time: + - '47' + status: + code: 500 + message: Internal Server Error +- request: + body: 'b''b\''{"targetResourceId": "resource_id", "targetResourceRegion": "centraluseuap", + "copyAuthorization": {"modelId": "21c912d3-898a-41bc-8dd4-adf96f6c5e89", "accessToken": + 00000000-0000-0000-0000-000000000000, "expirationDateTimeTicks": 1592335615}}\''''' + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + Content-Length: + - '447' + Content-Type: + - application/json + User-Agent: + - azsdk-python-ai-formrecognizer/1.0.0b4 Python/3.7.3 (Windows-10-10.0.18362-SP0) + Python/3.7.3 (Windows-10-10.0.18362-SP0) + method: POST + uri: https://centraluseuap.api.cognitive.microsoft.com/formrecognizer/v2.0-preview/custom/models/00000000-0000-0000-0000-000000000000/copy + response: + body: + string: '{"error": {"code": "1999", "message": "An error occurred during model + copy. Retry the operation or contact service administrator if the issue persists."}}' + headers: + apim-request-id: + - 8af7df01-9ce5-45d3-9846-d29ccfe17c77 + content-type: + - application/json; charset=utf-8 + date: + - Mon, 15 Jun 2020 19:26:57 GMT + strict-transport-security: + - max-age=31536000; includeSubDomains; preload + transfer-encoding: + - chunked + x-content-type-options: + - nosniff + x-envoy-upstream-service-time: + - '26' + status: + code: 500 + message: Internal Server Error +- request: + body: 'b''b\''{"targetResourceId": "resource_id", "targetResourceRegion": "centraluseuap", + "copyAuthorization": {"modelId": "21c912d3-898a-41bc-8dd4-adf96f6c5e89", "accessToken": + 00000000-0000-0000-0000-000000000000, "expirationDateTimeTicks": 1592335615}}\''''' + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + Content-Length: + - '447' + Content-Type: + - application/json + User-Agent: + - azsdk-python-ai-formrecognizer/1.0.0b4 Python/3.7.3 (Windows-10-10.0.18362-SP0) + Python/3.7.3 (Windows-10-10.0.18362-SP0) + method: POST + uri: https://centraluseuap.api.cognitive.microsoft.com/formrecognizer/v2.0-preview/custom/models/00000000-0000-0000-0000-000000000000/copy + response: + body: + string: '{"error": {"code": "1999", "message": "An error occurred during model + copy. Retry the operation or contact service administrator if the issue persists."}}' + headers: + apim-request-id: + - b1dd14ec-68df-43fd-a680-7d08cca4e9eb + content-type: + - application/json; charset=utf-8 + date: + - Mon, 15 Jun 2020 19:27:00 GMT + strict-transport-security: + - max-age=31536000; includeSubDomains; preload + transfer-encoding: + - chunked + x-content-type-options: + - nosniff + x-envoy-upstream-service-time: + - '73' + status: + code: 500 + message: Internal Server Error +version: 1 diff --git a/sdk/formrecognizer/azure-ai-formrecognizer/tests/recordings/test_copy_model_async.test_copy_model_fail_bad_model_id.yaml b/sdk/formrecognizer/azure-ai-formrecognizer/tests/recordings/test_copy_model_async.test_copy_model_fail_bad_model_id.yaml new file mode 100644 index 000000000000..36e6271f026a --- /dev/null +++ b/sdk/formrecognizer/azure-ai-formrecognizer/tests/recordings/test_copy_model_async.test_copy_model_fail_bad_model_id.yaml @@ -0,0 +1,237 @@ +interactions: +- request: + body: 'b''b\''{"source": "containersasurl", "sourceFilter": {"prefix": "", "includeSubFolders": + false}, "useLabelFile": false}\''''' + headers: + Content-Length: + - '288' + Content-Type: + - application/json + User-Agent: + - azsdk-python-ai-formrecognizer/1.0.0b4 Python/3.7.3 (Windows-10-10.0.18362-SP0) + Python/3.7.3 (Windows-10-10.0.18362-SP0) + method: POST + uri: https://centraluseuap.api.cognitive.microsoft.com/formrecognizer/v2.0-preview/custom/models + response: + body: + string: '' + headers: + apim-request-id: 70d9593f-6e1b-402e-bd28-88364a662e78 + content-length: '0' + date: Mon, 15 Jun 2020 19:33:57 GMT + location: https://centraluseuap.api.cognitive.microsoft.com/formrecognizer/v2.0-preview/custom/models/656b1437-a715-4e3b-a9f1-1b0f3e6998c9 + strict-transport-security: max-age=31536000; includeSubDomains; preload + x-content-type-options: nosniff + x-envoy-upstream-service-time: '577' + status: + code: 201 + message: Created + url: https://centraluseuap.api.cognitive.microsoft.com//formrecognizer/v2.0-preview/custom/models +- request: + body: null + headers: + User-Agent: + - azsdk-python-ai-formrecognizer/1.0.0b4 Python/3.7.3 (Windows-10-10.0.18362-SP0) + Python/3.7.3 (Windows-10-10.0.18362-SP0) + method: GET + uri: https://centraluseuap.api.cognitive.microsoft.com/formrecognizer/v2.0-preview/custom/models/656b1437-a715-4e3b-a9f1-1b0f3e6998c9?includeKeys=true + response: + body: + string: '{"modelInfo": {"modelId": "656b1437-a715-4e3b-a9f1-1b0f3e6998c9", "status": + "creating", "createdDateTime": "2020-06-15T19:33:58Z", "lastUpdatedDateTime": + "2020-06-15T19:33:58Z"}}' + headers: + apim-request-id: 2a5e274d-9336-4019-9dd4-e3831027ef75 + content-type: application/json; charset=utf-8 + date: Mon, 15 Jun 2020 19:34:03 GMT + strict-transport-security: max-age=31536000; includeSubDomains; preload + transfer-encoding: chunked + x-content-type-options: nosniff + x-envoy-upstream-service-time: '203' + status: + code: 200 + message: OK + url: https://centraluseuap.api.cognitive.microsoft.com/formrecognizer/v2.0-preview/custom/models/656b1437-a715-4e3b-a9f1-1b0f3e6998c9?includeKeys=true +- request: + body: null + headers: + User-Agent: + - azsdk-python-ai-formrecognizer/1.0.0b4 Python/3.7.3 (Windows-10-10.0.18362-SP0) + Python/3.7.3 (Windows-10-10.0.18362-SP0) + method: GET + uri: https://centraluseuap.api.cognitive.microsoft.com/formrecognizer/v2.0-preview/custom/models/656b1437-a715-4e3b-a9f1-1b0f3e6998c9?includeKeys=true + response: + body: + string: '{"modelInfo": {"modelId": "656b1437-a715-4e3b-a9f1-1b0f3e6998c9", "status": + "ready", "createdDateTime": "2020-06-15T19:33:58Z", "lastUpdatedDateTime": + "2020-06-15T19:34:08Z"}, "keys": {"clusters": {"0": ["Additional Notes:", + "Address:", "Company Name:", "Company Phone:", "Dated As:", "Details", "Email:", + "Hero Limited", "Name:", "Phone:", "Purchase Order", "Purchase Order #:", + "Quantity", "SUBTOTAL", "Seattle, WA 93849 Phone:", "Shipped From", "Shipped + To", "TAX", "TOTAL", "Total", "Unit Price", "Vendor Name:", "Website:"]}}, + "trainResult": {"trainingDocuments": [{"documentName": "Form_1.jpg", "pages": + 1, "errors": [], "status": "succeeded"}, {"documentName": "Form_2.jpg", "pages": + 1, "errors": [], "status": "succeeded"}, {"documentName": "Form_3.jpg", "pages": + 1, "errors": [], "status": "succeeded"}, {"documentName": "Form_4.jpg", "pages": + 1, "errors": [], "status": "succeeded"}, {"documentName": "Form_5.jpg", "pages": + 1, "errors": [], "status": "succeeded"}], "errors": []}}' + headers: + apim-request-id: 83db96a0-56c7-41f2-b015-35f496dcb128 + content-type: application/json; charset=utf-8 + date: Mon, 15 Jun 2020 19:34:08 GMT + strict-transport-security: max-age=31536000; includeSubDomains; preload + transfer-encoding: chunked + x-content-type-options: nosniff + x-envoy-upstream-service-time: '154' + status: + code: 200 + message: OK + url: https://centraluseuap.api.cognitive.microsoft.com/formrecognizer/v2.0-preview/custom/models/656b1437-a715-4e3b-a9f1-1b0f3e6998c9?includeKeys=true +- request: + body: null + headers: + Accept: + - application/json + User-Agent: + - azsdk-python-ai-formrecognizer/1.0.0b4 Python/3.7.3 (Windows-10-10.0.18362-SP0) + Python/3.7.3 (Windows-10-10.0.18362-SP0) + method: POST + uri: https://centraluseuap.api.cognitive.microsoft.com/formrecognizer/v2.0-preview/custom/models/copyAuthorization + response: + body: + string: '{"modelId": "eb496d1a-6e6e-418e-b013-26445fdd50fd", "accessToken": + "redacted", "expirationDateTimeTicks": 1592336050}' + headers: + apim-request-id: b8a2880b-5002-40b1-b5f4-01df565bb593 + content-type: application/json; charset=utf-8 + date: Mon, 15 Jun 2020 19:34:10 GMT + location: https://centraluseuap.api.cognitive.microsoft.com/formrecognizer/v2.0-preview/custom/models/eb496d1a-6e6e-418e-b013-26445fdd50fd + strict-transport-security: max-age=31536000; includeSubDomains; preload + transfer-encoding: chunked + x-content-type-options: nosniff + x-envoy-upstream-service-time: '1771' + status: + code: 201 + message: Created + url: https://centraluseuap.api.cognitive.microsoft.com//formrecognizer/v2.0-preview/custom/models/copyAuthorization +- request: + body: 'b''b\''{"targetResourceId": "resource_id", "targetResourceRegion": "centraluseuap", + "copyAuthorization": {"modelId": "eb496d1a-6e6e-418e-b013-26445fdd50fd", "accessToken": + 00000000-0000-0000-0000-000000000000, "expirationDateTimeTicks": 1592336050}}\''''' + headers: + Content-Length: + - '447' + Content-Type: + - application/json + User-Agent: + - azsdk-python-ai-formrecognizer/1.0.0b4 Python/3.7.3 (Windows-10-10.0.18362-SP0) + Python/3.7.3 (Windows-10-10.0.18362-SP0) + method: POST + uri: https://centraluseuap.api.cognitive.microsoft.com/formrecognizer/v2.0-preview/custom/models/00000000-0000-0000-0000-000000000000/copy + response: + body: + string: '{"error": {"code": "1999", "message": "An error occurred during model + copy. Retry the operation or contact service administrator if the issue persists."}}' + headers: + apim-request-id: 6e45bd33-370d-4634-9c8b-0d17fd44ee69 + content-type: application/json; charset=utf-8 + date: Mon, 15 Jun 2020 19:34:10 GMT + strict-transport-security: max-age=31536000; includeSubDomains; preload + transfer-encoding: chunked + x-content-type-options: nosniff + x-envoy-upstream-service-time: '23' + status: + code: 500 + message: Internal Server Error + url: https://centraluseuap.api.cognitive.microsoft.com//formrecognizer/v2.0-preview/custom/models/00000000-0000-0000-0000-000000000000/copy +- request: + body: 'b''b\''{"targetResourceId": "resource_id", "targetResourceRegion": "centraluseuap", + "copyAuthorization": {"modelId": "eb496d1a-6e6e-418e-b013-26445fdd50fd", "accessToken": + 00000000-0000-0000-0000-000000000000, "expirationDateTimeTicks": 1592336050}}\''''' + headers: + Content-Length: + - '447' + Content-Type: + - application/json + User-Agent: + - azsdk-python-ai-formrecognizer/1.0.0b4 Python/3.7.3 (Windows-10-10.0.18362-SP0) + Python/3.7.3 (Windows-10-10.0.18362-SP0) + method: POST + uri: https://centraluseuap.api.cognitive.microsoft.com/formrecognizer/v2.0-preview/custom/models/00000000-0000-0000-0000-000000000000/copy + response: + body: + string: '{"error": {"code": "1999", "message": "An error occurred during model + copy. Retry the operation or contact service administrator if the issue persists."}}' + headers: + apim-request-id: 185ae74c-84c7-43fc-a8f1-ccf17f93b385 + content-type: application/json; charset=utf-8 + date: Mon, 15 Jun 2020 19:34:11 GMT + strict-transport-security: max-age=31536000; includeSubDomains; preload + transfer-encoding: chunked + x-content-type-options: nosniff + x-envoy-upstream-service-time: '24' + status: + code: 500 + message: Internal Server Error + url: https://centraluseuap.api.cognitive.microsoft.com//formrecognizer/v2.0-preview/custom/models/00000000-0000-0000-0000-000000000000/copy +- request: + body: 'b''b\''{"targetResourceId": "resource_id", "targetResourceRegion": "centraluseuap", + "copyAuthorization": {"modelId": "eb496d1a-6e6e-418e-b013-26445fdd50fd", "accessToken": + 00000000-0000-0000-0000-000000000000, "expirationDateTimeTicks": 1592336050}}\''''' + headers: + Content-Length: + - '447' + Content-Type: + - application/json + User-Agent: + - azsdk-python-ai-formrecognizer/1.0.0b4 Python/3.7.3 (Windows-10-10.0.18362-SP0) + Python/3.7.3 (Windows-10-10.0.18362-SP0) + method: POST + uri: https://centraluseuap.api.cognitive.microsoft.com/formrecognizer/v2.0-preview/custom/models/00000000-0000-0000-0000-000000000000/copy + response: + body: + string: '{"error": {"code": "1999", "message": "An error occurred during model + copy. Retry the operation or contact service administrator if the issue persists."}}' + headers: + apim-request-id: 95e0f833-d7b8-4ebb-b744-44a2290508e0 + content-type: application/json; charset=utf-8 + date: Mon, 15 Jun 2020 19:34:12 GMT + strict-transport-security: max-age=31536000; includeSubDomains; preload + transfer-encoding: chunked + x-content-type-options: nosniff + x-envoy-upstream-service-time: '33' + status: + code: 500 + message: Internal Server Error + url: https://centraluseuap.api.cognitive.microsoft.com//formrecognizer/v2.0-preview/custom/models/00000000-0000-0000-0000-000000000000/copy +- request: + body: 'b''b\''{"targetResourceId": "resource_id", "targetResourceRegion": "centraluseuap", + "copyAuthorization": {"modelId": "eb496d1a-6e6e-418e-b013-26445fdd50fd", "accessToken": + 00000000-0000-0000-0000-000000000000, "expirationDateTimeTicks": 1592336050}}\''''' + headers: + Content-Length: + - '447' + Content-Type: + - application/json + User-Agent: + - azsdk-python-ai-formrecognizer/1.0.0b4 Python/3.7.3 (Windows-10-10.0.18362-SP0) + Python/3.7.3 (Windows-10-10.0.18362-SP0) + method: POST + uri: https://centraluseuap.api.cognitive.microsoft.com/formrecognizer/v2.0-preview/custom/models/00000000-0000-0000-0000-000000000000/copy + response: + body: + string: '{"error": {"code": "1999", "message": "An error occurred during model + copy. Retry the operation or contact service administrator if the issue persists."}}' + headers: + apim-request-id: fbe856d6-98d6-441b-a68d-d2558abc8a3b + content-type: application/json; charset=utf-8 + date: Mon, 15 Jun 2020 19:34:15 GMT + strict-transport-security: max-age=31536000; includeSubDomains; preload + transfer-encoding: chunked + x-content-type-options: nosniff + x-envoy-upstream-service-time: '23' + status: + code: 500 + message: Internal Server Error + url: https://centraluseuap.api.cognitive.microsoft.com//formrecognizer/v2.0-preview/custom/models/00000000-0000-0000-0000-000000000000/copy +version: 1 diff --git a/sdk/formrecognizer/azure-ai-formrecognizer/tests/recordings/test_custom_forms.test_custom_form_labeled_blank_page.yaml b/sdk/formrecognizer/azure-ai-formrecognizer/tests/recordings/test_custom_forms.test_custom_form_labeled_blank_page.yaml new file mode 100644 index 000000000000..05f20aa46418 --- /dev/null +++ b/sdk/formrecognizer/azure-ai-formrecognizer/tests/recordings/test_custom_forms.test_custom_form_labeled_blank_page.yaml @@ -0,0 +1,668 @@ +interactions: +- request: + body: 'b''b\''{"source": "containersasurl", "sourceFilter": {"prefix": "", "includeSubFolders": + false}, "useLabelFile": true}\''''' + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + Content-Length: + - '287' + Content-Type: + - application/json + User-Agent: + - azsdk-python-ai-formrecognizer/1.0.0b4 Python/3.7.3 (Windows-10-10.0.18362-SP0) + Python/3.7.3 (Windows-10-10.0.18362-SP0) + method: POST + uri: https://centraluseuap.api.cognitive.microsoft.com/formrecognizer/v2.0-preview/custom/models + response: + body: + string: '' + headers: + apim-request-id: + - 8d2d7ac5-68e1-44af-aad1-ce5ea6bdec0e + content-length: + - '0' + date: + - Mon, 15 Jun 2020 19:53:06 GMT + location: + - https://centraluseuap.api.cognitive.microsoft.com/formrecognizer/v2.0-preview/custom/models/9a939b7b-6f9a-4413-8745-69bcaafed0d7 + strict-transport-security: + - max-age=31536000; includeSubDomains; preload + x-content-type-options: + - nosniff + x-envoy-upstream-service-time: + - '415' + status: + code: 201 + message: Created +- request: + body: null + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + User-Agent: + - azsdk-python-ai-formrecognizer/1.0.0b4 Python/3.7.3 (Windows-10-10.0.18362-SP0) + Python/3.7.3 (Windows-10-10.0.18362-SP0) + method: GET + uri: https://centraluseuap.api.cognitive.microsoft.com/formrecognizer/v2.0-preview/custom/models/9a939b7b-6f9a-4413-8745-69bcaafed0d7?includeKeys=true + response: + body: + string: '{"modelInfo": {"modelId": "9a939b7b-6f9a-4413-8745-69bcaafed0d7", "status": + "creating", "createdDateTime": "2020-06-15T19:53:07Z", "lastUpdatedDateTime": + "2020-06-15T19:53:07Z"}}' + headers: + apim-request-id: + - f01f0630-df69-4192-b522-052a44fa7142 + content-type: + - application/json; charset=utf-8 + date: + - Mon, 15 Jun 2020 19:53:12 GMT + strict-transport-security: + - max-age=31536000; includeSubDomains; preload + transfer-encoding: + - chunked + x-content-type-options: + - nosniff + x-envoy-upstream-service-time: + - '167' + status: + code: 200 + message: OK +- request: + body: null + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + User-Agent: + - azsdk-python-ai-formrecognizer/1.0.0b4 Python/3.7.3 (Windows-10-10.0.18362-SP0) + Python/3.7.3 (Windows-10-10.0.18362-SP0) + method: GET + uri: https://centraluseuap.api.cognitive.microsoft.com/formrecognizer/v2.0-preview/custom/models/9a939b7b-6f9a-4413-8745-69bcaafed0d7?includeKeys=true + response: + body: + string: '{"modelInfo": {"modelId": "9a939b7b-6f9a-4413-8745-69bcaafed0d7", "status": + "ready", "createdDateTime": "2020-06-15T19:53:07Z", "lastUpdatedDateTime": + "2020-06-15T19:53:12Z"}, "trainResult": {"averageModelAccuracy": 0.973, "trainingDocuments": + [{"documentName": "Form_1.jpg", "pages": 1, "status": "succeeded"}, {"documentName": + "Form_2.jpg", "pages": 1, "status": "succeeded"}, {"documentName": "Form_3.jpg", + "pages": 1, "status": "succeeded"}, {"documentName": "Form_4.jpg", "pages": + 1, "status": "succeeded"}, {"documentName": "Form_5.jpg", "pages": 1, "status": + "succeeded"}], "fields": [{"fieldName": "CompanyAddress", "accuracy": 0.8}, + {"fieldName": "CompanyName", "accuracy": 1.0}, {"fieldName": "CompanyPhoneNumber", + "accuracy": 1.0}, {"fieldName": "DatedAs", "accuracy": 1.0}, {"fieldName": + "Email", "accuracy": 0.8}, {"fieldName": "Merchant", "accuracy": 1.0}, {"fieldName": + "PhoneNumber", "accuracy": 1.0}, {"fieldName": "PurchaseOrderNumber", "accuracy": + 1.0}, {"fieldName": "Quantity", "accuracy": 1.0}, {"fieldName": "Signature", + "accuracy": 1.0}, {"fieldName": "Subtotal", "accuracy": 1.0}, {"fieldName": + "Tax", "accuracy": 1.0}, {"fieldName": "Total", "accuracy": 1.0}, {"fieldName": + "VendorName", "accuracy": 1.0}, {"fieldName": "Website", "accuracy": 1.0}], + "errors": []}}' + headers: + apim-request-id: + - dd0b1d09-7a4d-4691-8b78-08f9fa8914e4 + content-type: + - application/json; charset=utf-8 + date: + - Mon, 15 Jun 2020 19:53:17 GMT + strict-transport-security: + - max-age=31536000; includeSubDomains; preload + transfer-encoding: + - chunked + x-content-type-options: + - nosniff + x-envoy-upstream-service-time: + - '60' + status: + code: 200 + message: OK +- request: + body: !!python/object/new:_io.BytesIO + state: !!python/tuple + - !!binary | + JVBERi0xLjcNCiW1tbW1DQoxIDAgb2JqDQo8PC9UeXBlL0NhdGFsb2cvUGFnZXMgMiAwIFIvTGFu + Zyhlbi1VUykgL1N0cnVjdFRyZWVSb290IDEwIDAgUi9NYXJrSW5mbzw8L01hcmtlZCB0cnVlPj4v + TWV0YWRhdGEgMjAgMCBSL1ZpZXdlclByZWZlcmVuY2VzIDIxIDAgUj4+DQplbmRvYmoNCjIgMCBv + YmoNCjw8L1R5cGUvUGFnZXMvQ291bnQgMS9LaWRzWyAzIDAgUl0gPj4NCmVuZG9iag0KMyAwIG9i + ag0KPDwvVHlwZS9QYWdlL1BhcmVudCAyIDAgUi9SZXNvdXJjZXM8PC9Gb250PDwvRjEgNSAwIFI+ + Pi9FeHRHU3RhdGU8PC9HUzcgNyAwIFIvR1M4IDggMCBSPj4vUHJvY1NldFsvUERGL1RleHQvSW1h + Z2VCL0ltYWdlQy9JbWFnZUldID4+L01lZGlhQm94WyAwIDAgNjEyIDc5Ml0gL0NvbnRlbnRzIDQg + MCBSL0dyb3VwPDwvVHlwZS9Hcm91cC9TL1RyYW5zcGFyZW5jeS9DUy9EZXZpY2VSR0I+Pi9UYWJz + L1MvU3RydWN0UGFyZW50cyAwPj4NCmVuZG9iag0KNCAwIG9iag0KPDwvRmlsdGVyL0ZsYXRlRGVj + b2RlL0xlbmd0aCAxMzI+Pg0Kc3RyZWFtDQp4nC2MsQrCQBBE+4X9hynV4m73iJ4HIUUuMSgEFA8s + xFJTKaj/D67iFAPDPB78HnXtx7ztIE2Dtst4MomTb5IGCFbWMQW8rkynBR5MbWHyG4WqkwrlxqTG + CRQxOAkVoiS3tOdu3HCMmN7mxPRb6/8amM4zzC8oO6bejAcm9GPGB3fjHKoNCmVuZHN0cmVhbQ0K + ZW5kb2JqDQo1IDAgb2JqDQo8PC9UeXBlL0ZvbnQvU3VidHlwZS9UcnVlVHlwZS9OYW1lL0YxL0Jh + c2VGb250L0JDREVFRStDYWxpYnJpL0VuY29kaW5nL1dpbkFuc2lFbmNvZGluZy9Gb250RGVzY3Jp + cHRvciA2IDAgUi9GaXJzdENoYXIgMzIvTGFzdENoYXIgMzIvV2lkdGhzIDE4IDAgUj4+DQplbmRv + YmoNCjYgMCBvYmoNCjw8L1R5cGUvRm9udERlc2NyaXB0b3IvRm9udE5hbWUvQkNERUVFK0NhbGli + cmkvRmxhZ3MgMzIvSXRhbGljQW5nbGUgMC9Bc2NlbnQgNzUwL0Rlc2NlbnQgLTI1MC9DYXBIZWln + aHQgNzUwL0F2Z1dpZHRoIDUyMS9NYXhXaWR0aCAxNzQzL0ZvbnRXZWlnaHQgNDAwL1hIZWlnaHQg + MjUwL1N0ZW1WIDUyL0ZvbnRCQm94WyAtNTAzIC0yNTAgMTI0MCA3NTBdIC9Gb250RmlsZTIgMTkg + MCBSPj4NCmVuZG9iag0KNyAwIG9iag0KPDwvVHlwZS9FeHRHU3RhdGUvQk0vTm9ybWFsL2NhIDE+ + Pg0KZW5kb2JqDQo4IDAgb2JqDQo8PC9UeXBlL0V4dEdTdGF0ZS9CTS9Ob3JtYWwvQ0EgMT4+DQpl + bmRvYmoNCjkgMCBvYmoNCjw8L0F1dGhvcihLcmlzdGEgUHJhdGljbykgL0NyZWF0b3Io/v8ATQBp + AGMAcgBvAHMAbwBmAHQArgAgAFcAbwByAGQAIABmAG8AcgAgAE8AZgBmAGkAYwBlACAAMwA2ADUp + IC9DcmVhdGlvbkRhdGUoRDoyMDIwMDMyMDEwNDQ0Ni0wNycwMCcpIC9Nb2REYXRlKEQ6MjAyMDAz + MjAxMDQ0NDYtMDcnMDAnKSAvUHJvZHVjZXIo/v8ATQBpAGMAcgBvAHMAbwBmAHQArgAgAFcAbwBy + AGQAIABmAG8AcgAgAE8AZgBmAGkAYwBlACAAMwA2ADUpID4+DQplbmRvYmoNCjE3IDAgb2JqDQo8 + PC9UeXBlL09ialN0bS9OIDcvRmlyc3QgNDYvRmlsdGVyL0ZsYXRlRGVjb2RlL0xlbmd0aCAyOTY+ + Pg0Kc3RyZWFtDQp4nG1R0WrCMBR9F/yH+we3sa1jIMKYyoZYSivsofgQ610NtomkKejfL3ftsANf + wjk355ycJCKGAEQEsQDhQRCD8Oh1DmIGUTgDEUIU++EcopcAFgtMWR1AhjmmuL9fCXNnu9Kta2pw + W0BwAEwrCFmzXE4nvSUYLCtTdg1p98wpuEp2gME1UuwtUWaMw8zUtJNX7sh5qbQ+i3e5Lk84Jupj + RrsJ3dyW7iCG6I3P0sYRJrys9elB9l56NDfMqXT4QfJEtsfs+cOfulaa8rPkhjx40z5BOmX0wK1T + 39KDX/Zl7OVozOVxe560ZyLHJR3uZGnNiL+f/TriKyVrU40Gea1ONNL253hZZWWDG1V1loa7Jl3T + FvzH83+vm8iG2qKnj6efTn4AVAqiuw0KZW5kc3RyZWFtDQplbmRvYmoNCjE4IDAgb2JqDQpbIDIy + Nl0gDQplbmRvYmoNCjE5IDAgb2JqDQo8PC9GaWx0ZXIvRmxhdGVEZWNvZGUvTGVuZ3RoIDE5Mzg5 + L0xlbmd0aDEgODE3NDA+Pg0Kc3RyZWFtDQp4nOx9B3xUVdr+OfdOy8wkM5NkkkkmYWaYJASGFCCB + BJAMpNA7gwk1IYWAAQKEIgJGUdAo9l7Rta1YJgNqwO5iWQv2vhZ2XVdXse3qKgL5nnPfORDY1f+3 + 1fX7z5s88zznPeWe+t6TH8kPxhljdnzoWG3lqIoZBf1stzPumcAYf6Jy1ITyq5qr4hnPzGBMKZw8 + vWDgtY/W3YO8s1Crtn5JXetF716EsiddgvwP6le3eXe1vlHM2LYLGNM/0NS6cMnGd9UhjC1dy1h8 + YGHLyU2vVu4oYuwW1LF90NxY1/DtxJPDaM+K9gY3wxF/Z8Z+pCuQzmpe0rZ2xDjjAaQ/YmzRHS3L + 6uvyGvrezNi9hSg+c0nd2tZ8c/abyG9Gee+Sxra6q07ftppxXzLSZyytW9J43YGv5zP2KfpbuLJ1 + 2cq2bjfbzHjGQVG+dUVja9LC3mmMnXITHvcJE3NhGLpv9uI1H8+3Df+apZmYsPs/Wf+s4NfHrpn8 + /YFD7XGfmgYjGccURoZ6BnaY8T3mbd8fOLAt7lOtpR6WdofwuPuxdmZnw6EVcAHbwljiYO25nKm6 + AL+A6ZlJf6V+EJrsRay+wDYrzMQUm15RFJ2q6D5g+d2PsKxTtB7AJk73elmQsexnqQ/G65QcL+Pd + Ik+9T58gRsqSdQlHe8OfZ//fm+F1dsdP3Yf/K6ZrZDf81H34e8xg+Pf0V93/85qHf4fpiljtT92H + mP3zpjzNrvyp+/BzMOX3bMw/Uo9/w1r+1X2JWcxiFrOY/eOmXM3NP5hXy/b/J/vyczG1mJ3zU/ch + ZjGLWcxi9o+b7lHW9B9/5hJ23n/6mTGLWcxiFrOYxSxmMYtZzGIWs/+7Fvs5M2Yxi1nMYhazmMUs + ZjGLWcxiFrOYxey/23jst9FjFrOYxSxmMYtZzGIWs5jFLGYxi1nMYhazmMUsZjGLWcxiFrOYxSxm + MYtZzGIWs5jFLGYxi1nMYhazmMUsZjGLWcz+S6x790/dg5jF7Cc2NYqM6P8k1YEUlLKa6dhSpFOY + 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 + +xG3mmhL96QrfW1pvHxyGl+Wdmra+WmqzfW8Swm6+vavsqU+n/pe6uepuqRgat/8KpZiT/GmqE4x + tpSJM6o0LqsgHlCsjdWT4s+psjm5zelxKpWfO/lmpnIv54zbQaoJZXZyp6dKfZCLX6LTM84vYDMC + 47tMbNr4sGnK7DA/K5w9XXwGp84KG84Ks9Cs2dWdnJ9Xo/1OQjhZ/FKJlj5z61aWOWp8OHN6dUTd + ti1zVM34cLvQwaCmu4VmKFITmLdy1cpAdfAE5njf8YVDdT5sf96u2GzcZuu2KUEbOm9L8CQo4qM7 + QQ0mDBhSZYv3xCviozteTQnGwyPG18c6ZUaVzeKxKKEyy2SLErSUlVcFLXmFVX81zh1inPTkQNs8 + fMxb2RbQvpGq4atEMiC84ntlG9Lia5WWZoEfNSoGmr8S1iadbT9e67/d+E/dgZ+/0W/yjOxWzmAN + yibgdOA0oB04FdgIbADWA6cA64CTgbXAGmA1sApoA1YCy4FWYBmwFFgCtAAnAYuBRUAzsBBoAhqB + BqAeWADUAbXAfGAeMBeYA8wGZgE1QDVwIjATCAEzgOnANGAqMAWYDEwCJgITgPHAOGAsMAYYDVQB + lUAFUA6MAkYCQaAMGAGcAAwHhgFDgVKgBBgCDAaKgSJgEDAQGAAUAgVAPpAH9AcCQD+gL5AL9AFy + gGwgC/ADvQEf4AU8QC8gE8gA3EA6kAa4gFQgBXACyUASkAg4ADtgAxKAeMAKWAAzEAeYACNgAPSA + bmQ3PlVAATjAWAOHjx8GDgEHge+BA8B3wLfAX4BvgK+BPwN/Ar4CvgS+AD4HPgP2A58CnwB/BD4G + PgL+AHwI/B74APgd8FtgH/A+8B7wLvAO8BvgbeAt4E3gDeB14DXgVeAV4GXgJeBF4AXgeWAv8Bzw + LPAM8DTwa+Ap4EngCeBxYA/wK+Ax4FHgEeBh4CHgQeAB4H5gN7AL6ALuA+4F7gF2AjuACNAJhIG7 + gbuAO4E7gO3A7cAvgduAW4FbgJuBm4BfADcCNwDbgOuB64BrgWuAq4GrgCuBK4DLgcuAS4FLgIuB + i4ALgQuA84HzgK3AucA5QAdwNnAWsAXYDJzJGka2c5x/jvPPcf45zj/H+ec4/xznn+P8c5x/jvPP + cf45zj/H+ec4/xznn+P8c5x/jvPPVwCIARwxgCMGcMQAjhjAEQM4YgBHDOCIARwxgCMGcMQAjhjA + EQM4YgBHDOCIARwxgCMGcMQAjhjAEQM4YgBHDOCIARwxgCMGcMQAjhjAEQM4YgBHDOA4/xznn+P8 + c5x9jrPPcfY5zj7H2ec4+xxnn+Psc5x9jrP/U8fhn7nV/NQd+JkbW7myx8VMmGv+PMaY8TrGDl98 + zF+MTGGL2UrWjq/NbCu7mD3M3mYL2CaoK9k2dgv7JQuzR9mv2ev/7J/A9LTDJ+uXMKt6HzOwJMa6 + D3TvP3wL0KVP6OG5GKkknfeop9ve/dlxvs8OX9xtP9xlSGRmrW688hK8f+KHug/glYt092CRVrZA + 27QaXxqvO3z34VuPm4OpbBabzeawuayW1WH8DayZLcLMnMRa2BK2VEstRd5CfDYhNR+lEF40fbTU + MtYKrGBtbBVbja9W6JXRlMhbrqVXsTX4WstOZuvYKWw92xD9XKN51iNnnZZeC2xkp2JlTmOna0oy + eTaxM9iZWLUt7Cx29o+mzj6iOtg57Fys83ns/B/UW49JXYCvC9lF2A+XsEvZZewK7Iur2TXHeS/X + /Fex69j12DMi71J4rteUyH2APcHuYXexu9m92lzWY9ZoRuS8NGlz2Io5WI8RburRY5q/NUdmayPG + LsbWER3pWvhP71FjdXQeRclNKEmt0DqIVjYcNxMXYAykj46IUpdq4z/q7TkrP+aV83FNj5m5WksJ + dbz3h/Rl7FqcwBvwKWZVqBuhSV2v6Z7+646U3aalf8FuYjdjLW7VlGTy3AJ9K7sNZ/t2tp3dga+j + uqcivovdqa1cmHWyCNvBdmIl72X3sS7N/2N5f8u/I+qPHPHsYrvZ/dghD7FHEGkew5f0PAjfw1Hv + Hs1H6cfYr5AWpSj1BHsSEepp9gx7lj3PHkdqr/b5FFIvsJfYy+x1Hg/1IvsYn4fYC/oPWAIbiR// + d2Oer2Hz2Lx/ZXQ73vTpzMm2dX/bvab7W3UMa+IzcIG8A6u0k52Ln9iXHi3JPcys+y1LZju7v1Hn + gHMPvaVvPnxj9+dMj6i5Un0JUU5lRlbKJrJJ7PLwmYHqB1g8bikpbCi/5x5nRYUpz/gQbiAK8+IO + Y2KclwdtOiX+vvT0Mv99xYatqmNsF8/bWWbcitt52aF3D+0tOPTu/sTSgv284J197+6zf7nXUVow + aN8r+wYUuoPJ6fH3taBqsf++lmLVsLVFdZSJ+sG4lrKgYtzagkZcZYH0vYG9BYG9ATQTKBxQwx0+ + h4bkBMVoTDb4e+crxX1yBg8aNHCEUlyU4++doGi+osFDRqiDBvZS1GTpGaGINFdfOjhLnXzIoGz0 + l80cpO+VbkuON+iVDFdi3vBs+/TZ2cPzM42q0aDqTcbcIaN6j2+p7P2W0ZHpTMlMNJkSM1OcmQ7j + obf1CQe+0id8X65r+f4S1TBsTlmWeoXZpOgMhq5errR+w3xjZ9qS7DpLkt2RYjImOqy5FXMObXZm + iDYynE5q69BExtkd3QcMAcz+cPaamPWgvXZE6wglvrAwtaDAnO9ypXd1f7TDzieCv9hhi3K8xt/s + sGr80Q6LYMUR7JU1wGo1u1DcbLeJDxQ0m1HK7EIR82782MW6HwmmIcGyBk+1uFLjC1wD8g2e3Kme + UGJIH2JlsMTUUsegMl7wSmCf9o4f6BhkP6IcpScUDBrkGDSgcC6W8W+24TraCBYtWy6Bw88TVKH6 + cL/jiLNIrF4vJZUP4lgyIZ2GgCnZk5bqSzIphwepFmdmsrNXskU5PJqbkr1pLm+Ssb+72VuY5Yrj + a/R8syXdk5O2xOZOsqabrEa93mg16RZ+f4nRbFR1RrMBS3TlEf8t/bKs6bnugyeqt/Tql2aJS8p0 + YkvfwJh6EG//ROZhI2jvJ+EnaMbSleRgXJzru4QG93f6haxsfxl2c3QLWxNc37UkNOjd37UgC5u1 + TNuiYmD+3jnawHwYjbEoHw6H2KHqwbEdT239PjkrK5k7Oh7dVBHODW1pufCCps01/RXPuc9uHpnp + U2/yZVae8fDGaecuHHrwswGNl4u/xb6h+4C+Ef0rYYtF73b2d+b1cXXx7mBc7/gCc15e7yKzSDlY + 7+KGvBSLmpnTkNlsb9Y3y+UUi7lvYCKWLrG01L5voKO0VAzBdnxxuXLHr5vB8P9ctxSnvtGY5E1N + 8yYalcPn6Py52O1x6uErFWOiNy3Nk2jMcbV4+vuwaH11fKA1zdc3oyktK9VoMep0+FDXHDzDalUN + cQZ1/cGzj3if7O0VC3aoSHmqV790i7e3+Nt1zId6DeZjEAuyBjEju5hZce4cYA84isSvaOQMc3Rh + 5WwZAceHw4alln7jbUiNzoYWkUqxiANf2Ye5eE1bysTAMMeHLSjpLf2mJVpWTIUWd0p7zEWfPvmq + /9hJEGvsFPGol5qampKi9ljua0zO7Ay3z2lWZ9qyCkcWLdS2ry/ZhPVPrz1zdmFm8YQB7rxsn73G + bPzUWTg+eOl5IyYNTEsyYhLUuATLV/0qCtIPTz4yGc/4MnOqFo4smlk50G7xFQZzP05PU971Dw+k + Hb4rrUD81Vlt9371GtyBcxDJH9DiiadsGLe4S0UkKBWRoNRuFx+IDqUiJpTez7/DRi/ofl8ElYJo + sCmIBhuNrVG/RbBiDpqTfFWW0j5uXUI/8c9RrnFFXVy3I2GifgImGCeE9hsFjFeicaNUCxdmWdEl + au5scY1LEHV3tmiVMeM4Q8fvvmLaexTAU1Id0UDuVHO0cO9M7qWIyR6iXmN0ZCSLCDv6ytn1556Y + O3DBhfMnbwoakz0u7Mm4W8o3VJRVD0lzFs0c6TshWNUnDUEB02o1rZk4c+KmzgVt958xurJcsRjj + RayINx6qnH7i8AXrgxWnN56Q2K98AM7hlbj936o+jX23WTuHrcU8xxaNw7boFIG/2Gmz8wm2aKC2 + dfFvg4ksmISYG3TgwwsnS8eJzQ7GBcbl2JzesU4xddiOIrzswXxps6bNWWdAK2huOVrSRUWPRBvM + jpgJY49tGZ0jp/YSNCi3KoY4kyk1M8uZVlg81G9KpChqSMxITcm0G7NHDi3NjPdlZVp1KlcXpPRy + xMXFmZLzJww5FDZZTDodPtQzTJY4bEqLadPgij421WQ2xyW4sePGKI8r6wwOlsWK2SwxK5G4tOL7 + eTU2VR4/O2h3eJakxam54ZTlA6+2tqkro3ukVNsjCEpaIErSCqXkhltSllsHXt2iFYzuh1JtP/Do + 2/5/tR0GD1HWpfkcKTZDQd3wUbNL070j55cNmJZrtKUnJ6fbDWfljs7NKvLYrL0G5mSNzVc+sMbr + EHhGFgwomLxoeNXKyYGcHJ6vN+lUVWfSH56en+8tKvdnVRX7AsUiHrcoz/AX9W6Wx6rEiHf0TmdY + 5ROD1nTznj7Le9ucvVqdK4+u6Jd7ErVRxvcx72k5mv+/WMfBIq7SKur4i4rOqDdZbE6HLcPrT9Hb + aTBpfn+qq1+OPynBl2LUcd1LDleCUW/QW1y5mYdvw7B0YmyKywob7clNNelMhoRUpnBz9zf8N/p5 + uEP2ZdliHPfos90T7VXo+Dt70d979dlBLY2Opr+zt0c3i9Wc6LQnHX+/etAo7jcZiUYHNzn9GW6/ + 05QQl5br8fR14UXa1+PJTYvjq0xWsausJnW3NdGqN1gd1u9LfQG3xeIO+Hx5aRZLWp6I8/u79/O7 + dfO1HpbQezlFaWBe5lRK77XY+6G/ixg6a98j38r3CmcQXpfosn1Pj073UYt+qNOXGm1uZ4rbbuAO + Q1JWhrs3InBcSlZmRk5qXFxqTkZmVkocLxYXChUfSrfVbtbrLTbrQW9mH5fF4uqTmZmbZjan5aLP + 56hNylX6VT1n1Z0z2j4as/rcQG1W3UEtLWb1uYHHzGq0P8bjPClOZZPBnpqY6LIZUs3JvlS8Q+L4 + 4S3H+Apz1M1yWvnzUh0ecKzPbmfMjp+JZ+lm6ybhvm9jqbjz9GEFbAgrY6PZZHYim88W4qfnNexU + PkF7gyyd0twyo6Vk7frh63Nb2/q3eWsbshpMYyZYJ7Bgha7CXliUXNSyvq1hQkVRUcWEhrb1LcaM + 6jmujHErVk9aPWrdxqqNAxcvHbw0fda8XvMSp81MmakMHWEYYe6Xn5C/euPSeTNH5OePmDlv6cbV + xpymBb1zWMFzBc85UksLyHD3fG7gj39wUSPx76khTmPJP9a/YA5zFaT/vV3Ultnfu7ho0MA+UU6K + cmqUZb7xuPTxfHy+MeXYdPZx7cvnqa8UFhUVXiI+/jJowKABWf/T3nfANXW1D9+bhD0VUUSQi6iA + hnATQFDqiBAgyjIMxR2SAJEsk7BstYAL9yiKoyq4rROp1daFonW2WletVnFvnHWv/znn3oSA2Ne+ + v1+/vu/7JY8kZzzn2ed5zuWGK2y9C+WB1/ogLjeIIYLvb93hAGOsEfftBjKYx2uPc4ODufgBOPlu + MHx/DrFLYYs5F7yRoPfu16Agbi3o4GWgkQqpfQ7e8J28wJC3MaA1hySDGQSN9M4KNG7BZb8Fk8Ec + 0Hj/HpvOOMastbjFsLSuhpethj4Wjg2HsVgZ0Bp+jcqHtIUfmE/INsb4LZxWdsy2frDVVtdMZ6Ez + PfTW8ZzroLe/x0KawjQ97xo3XTMmfZnC9HH54LjrEuRiuExh1lo5t3Z1aeNodRu3cWrp5NzS0Qb/ + HcetnN3AqJNVW5eoVkRrZ8tDzJNWzV1bN+9j62Jvw7hqAU4d4Nxhwej1dgcTXHoyWZYs0K4xjp9x + dwUkmr19zHBo7u5kaWHfzKHBk5zsoSXaoLe0NBJY6f0PVjMYpNVTcAVvXQlSUGAQyWV6u3pHMXLf + TrZ6mgHW7P7PALzovwJ++XuAkfYX4MY/Dcw5//vA8voPhPl/Am/MYIb/DbCIawCr/oPgpRnM8L8N + VlH/NsSbwQxmMIMZzGCGT4KTZjCDGcxgBjOYwQz/Y3DZDGYwgxnMYAYzmMEMZjCDGcxgBjOYwQxm + MIMZzGAGM5jhfwAem8EM//8C+lu0AEY7jP4/7RnOaISJ/m7PEfVgm4E5sjbRbSbWnrWLbrNMcCww + N9YVum1pMm6F5bJe0W1rrJPFGLptgxFWxXTbllFuxLfDUq2W0m17rJPVC7rt4GhpbZDTEesDcOi/ + p8OtW/rRbRyzakXSbQZm5VZIt5mYm9tEus0ywbHA7N2W0G1Lk3ErLNxtLd22xlxbBtJtG8zZ7Qbd + tsUTjfh2WGe3Z3TbHnNt7U23HayYrbvQbUesA8BhYjjLBgjX3EJDtyk7U23KzlSbsjPVZpngUHam + 2pYm45SdqTZlZ6pN2ZlqU3am2pSdqTZlZ6rt4OhGdKXblJ3XYATGw0iMi4WBVhx6QpcWU2M68JOB + 6cFYBHqyGfV8MzEYkYOWCuOAGT6mAEBgIjCWiWWBOR3qycCnDGDngncpwHTAYkArHYzIsDyAkQCo + yQCNZKwAtQgsFlAuAHRzEEcFaGUiSQjwo0bPBtMaeRBGmUksCLQ6GnuhGBvxFwMKGoBLAL5iwAfS + kGDZNG4f0MsCo3A2B8inM+qTjJ5QpkMSfEyeDGQHAusN+ulgBo6KkRUa6kjRUdOaEohLDpiVIH0N + 1s0Da7VoJAdgSZHVCDCehcbiMCGQCVpHjtapkF3D0XoZwpBhSsATWlmK3glaIgMugcZ1yKdyIIvB + e/V6wHk9kEIOVuqAFSKQNnKkidyohxj8KMEKSkJKHzHiQdC+lgOKkKoY4EFaBaCXB1p65Af47Lt0 + 0FYgmbTIFlBf+Gy9TNpSFFU90oniqUIaSZCkKsRFh/wkRF7JACNi9Gw3LdKRQJ+UL+RIJ8oWOhQV + OkBVTMcr9JiGHjdwUQI6CmQfDS2lCowoEVeKpg5Zql4CyFGDdDE8+4+yLSW7AkUNjIQsOnKhVPA5 + d/D5gXrUUyFfG+KashnFhfKjitZLjWybjjDrJTbVCFotH62jtM4GfQ7au6be9EXUlIhCAbJDDr1L + Te1tiD4VHclQf8ovWhQNhhiVIV/DyNUYtaFkzKRxdKA3iqauB1pQHso1ekmMYgTuAGUDvQyZRwIk + ESP+Epo/B2WXTOQrOPNhvur2gdapdOQYIr8LoMIDmePjka5HPKUoEiGXbKMP6nfmh3kyk45rjREb + Ri7lcRXAl6HY+X+Tb23NGfe/JuPGAkkkmB/aZf70PIFFo6hQI8n0AGC+6oYFApAi28KVyg+ih0PH + XCBoF6AYykRRBH1TAEbhE04pGxuoUjQVSAYoQQaSlspzFK2mYlSH4lyDdKesYFgHvZqGeFCZpgBZ + mrKM3uhtA7YhL0jo3A13ORvZAOJp6KgwzdMaZFcVnR8oKjK6L6ZzsgxlFDnSkJIuHclh8HJjj+np + FVT8aD8YyTDqwP6kTEBVBSmyqZ6uPtT+pPiyjXwaa0Bl0Tz6SalZH7FZHq2pHO00BdpT1M7/0PZw + DVVZ/AC+f4MIbpo6JcO/a1vT/UFVd4Kuz3rkOUmDOtlYg/qq2FiucJMYgJpQulCnBUOu1BpPHlJU + e1Uoj4g/qikVe+IGUUXlAzX9TmlFtXPQfqHykxTVMTmdWyg6EFOBsv/HY5TK4iraM/XUDTtEbnKq + yEL5Tk7bGWZ1B5QvZbQOhhOGwcoNo5qNPCNGbSlmOF81znONd4Jfo7wgQ3k6D50o5Mj70KtiMAYt + lAkwDHOBNM1hjXKnP71767NF/WnAIM1fqU6fWA0Ij0Y0Yg00CE9jNMMnEVN+MkQNdTpR0FWkPrr/ + rMIZovLjVQ56LtG4c3QmZxHK31QUyGheVMZW0X5nI521dPUxnCuoc1Em7WdDHFNxpaHPOxQHNTp3 + i5GehkgRY/VVvnE++xt8YbSQGOkO7Sanc72U3qsS+qytQrKa1kw5Oo3rUGzSMn7ct6Cd1LDOA2/7 + m9hIanKFYLofPpkeVn9VY8BuOruxG2U3g+0br1agqwJ5I70NctWfwep3TX0lMviQjRmuzuBVmKEv + M4kQDbr+UqB4yzKpsJTU6UgWGV2pcoy+NM0llA8DaY/r0C5RGGUw7OuGsfTpVjWt8JSWppWmYUzX + WyIP2VH5b/rRUA1y0NUlZRmZiQRS9A551ttlBMCQmNQO/Z/kYyrzS5EGhorXrUEWp05juajd1Klb + hWqEocqYXp8Z6kRTOaXhKh3KFZSv0mm9m6654o94VGvUXoeiVIWoU7vowyvffzcCDPUtBhOg2QQs + CvT6g2opQiNCMEaALCoCM6mgFwlGI8GIL8BIoud9kaf6ozoUA/BSUI2jaIjAezzop6EcF4URqA97 + fQF+PKAF1wqwAYiHAFBLQpgiRDsOjMaCTwGNB1dEgJEU0IftaJQFKX7xYBV1DSGkayIlaTIYJ4wa + NpRKiDgaJIsDPRGgH0PP8gFtIaIH5Yf8o1A73ihnFC0pH9kIUoY0I4BEsagHR1PAZyLAS0L8+Uhn + Stp4pEMUmKd0ESAJIGcOrSuFB+2TSs9AH0H5YgHUa8VHNohB0tTbLwJ8JgLJIf1oMJuMKkQCWBmJ + NE1C1hPQNoPaxqJevVaUpyKQNtCq0AaRoB0HfqKNthOhd0oWkQm1hrbrj+brsSj9+PR7BLJcAupR + 3ohAvWTkKzjLpn0pQno05tofRaIAYfGRxknGCIlC0UtJb4hOikeCiSQUP+hbU1kMUU38yR6hqBjm + U2hPf2gXaHU+sgmUK8nI+WOUwd5cQ/BIbhgRJ5do1Tp1hp6IUGs1aq1YL1erOARfoSBE8swsvY4Q + yXQyba5MynGIkaVrZXlEgkamSi7QyIhYcYE6R08o1JlyCSFRawq0cAUBKZNBREf4EcomRGKFJouI + Easkakk2GO2jzlIRMTlSHeSTnCXXEQpTOhlqLdFbnq6QS8QKguYIcNSAKaFT52glMgKKmyfWyogc + lVSmJfRZMiJOmEzEyiUylU4WTuhkMkKmTJdJpTIpoaBGCalMJ9HKNVA9xEMq04vlCh0nQqyQp2vl + kIeYUKoBQcBHrNIBKlp5BpEhVsoVBUSeXJ9F6HLS9QoZoVUDvnJVJhAKoOplSrBSJQUG0KpkWh2H + EOqJDJlYn6OV6QitDGgh1wMeEh2b0CnFwK4SsQa04RJljkIv1wCSqhylTAswdTI9IqAjNFo18AaU + FlBXKNR5RBYwLiFXasQSPSFXEXpoayAZWAJ0VAFe6gwiXZ6JCFOM9LJ8PVgsz5ZxCFpNXx2hFKsK + CEkOcCklNzSfChhZKwa6aOU6aFGZWEnkaCAbQDETjOjkowC6Xg0UyoUqiQngACXFCwaPJEusBYLJ + tByRLDNHIdYa46qbgXU3GA8hqcBE0AVdOLygBqbXa8VSmVKszYZ6IJcaIzMTWFwDhyVqoL5KLtNx + YnMkfmKdP/AiEa1Vq/VZer1G1y0wUKqW6DhKw0oOWBCoL9CoM7ViTVZBoDgdxBlEBZiKHIlYl6FW + AYMDrHpmuhyNRiEHgQPnOESaOgdYrIDIASGkh8EKh6EhJMC1ehmbkMp1GhDAlEM1WjmYlQAUGfgU + AzfKtEq5Xg/IpRcgrQzhCEwF4katNTQyIAf2h7qDOJDmSPRsGI65YC0brjEwAP7Jy5JLskwkywNM + 5SqJIgfEfr30ahWIFD+5P7UtTNABhT+TltpFINaB33V6rVxCBaSBAYpDA61wZAE/OeAC9gRMJVq4 + c6TqPJVCLZY2tJ6YMhWILKAOcB9s5Og1IAtIZVBNiJMlU2gaWhTkJRC7FDp0iBztkyx5ulwP85ND + MhA5Qw13CxSZNjWbSBfrgKxqlTFTGJzgR8eCTMXJk2fLNTKpXMxRazMDYS8QYA6jc4o/cC8KC7QH + IJmmk2BTyesEjRELMU5CM49QA52gacBeUoDEhszdME1CUzZIlA4OidA5OrR5gN7ABDKwCgQ2sIyU + TWRoQdKDWwRsxEygM7QxsBXwKFhOqNNBslNBo4hRojbE2adrAQUS63RqiVwM4wPsM5CyVHoxlU/l + CmAZP0ixgbZEEp2pT/ojiaQoG1J+aBIP5Vk4bBJubDrcoPSGaYUcxCnFG9LSUpUKcECbCGrIhrlc + ngE/ZcggmhygkC4LbVhAOj0Hbl4dHKSjBGgYCBTXyWCKVmvkVEb9qKjUhgcsqU1DWxoJkZelVv6J + jnAb5GhVQBgZIiBVgxyKZBkhk+gNAVYfxyD4pXK08bpRIQ7SWK7MpOCq1Hq4ZahkLqe3MRUp9JQu + C9aDdFmDnSs2UVQL2ev0IJjkwEXGyvNnBoD7LUZAJCVEJffniwSEMIlIFCWkCiMFkYQvPwn0fdlE + f2FyTEJKMgEwRPz45DQiIYrgx6cRfYXxkWxCMCBRJEhKIhJEhDAuMVYoAGPC+IjYlEhhfDTRG6yL + TwB1XQh2IiCanEBAhjQpoSAJEosTiCJiQJffWxgrTE5jE1HC5HhIMwoQ5ROJfFGyMCIlli8iElNE + iQlJAsA+EpCNF8ZHiQAXQZwgPhmU3HgwRghSQYdIiuHHxiJW/BQgvQjJF5GQmCYSRsckEzEJsZEC + MNhbACTj944VUKyAUhGxfGEcm4jkx/GjBWhVAqAiQmi0dP1jBGgI8OODfxHJwoR4qEZEQnyyCHTZ + QEtRsnFpf2GSgE3wRcIkaJAoUQIgD80JViQgImBdvICiAk1NNPAIQIH9lCRBvSyRAn4soJUEF5si + cxzMtwXMtwX+gm3NtwX+vtsCtujHfGvgv/PWAOU98+0B8+0B8+0B8+2BxtncfIug4S0Cg3XMtwnM + twnMtwn+424TgL1J/a0Bhr13wyZgTb0Y9DfyMdwPfLLRN/v/7BXJLLO3xwEOnvyp+A4OCL/wU/Gd + nBD+uk/Fd3ZG+Gc/Fb9ZM4jPYH0qvosLwAefGPwLBRbCh2stgEsw3B1zwKdi7sw+WAeAwQPj3Rrh + 9jDBdQW4PgCXAzA+g9Qb4Rab4LYCuB0BLg9g8MF4n0a4R01wWwNcf4AbAjAEYDy+IS7AqMdtA3DZ + ALcrwOgDxpMa4SpNcD0BbiDA7Q4wEsB4GowXa2vc2ramZgV4zZ9vbYFbW1lb55eAV74lE7dkXSqE + L2sct2ahViFWyGTi1hbl5eXWNri13Z7CPYVLAZQCKAFgY4HbAAoGEizc0mJTNVxng+M2NAmKhg2k + YWOL29hXg1dFr4pesxFMBWBridtas1gs/dRx48ZN1VuxcCuaTKEtzrC1MNIpZLFwW8uZ4GVrh9s6 + VA+vHg6ols8iZhGTAYwDYGeJw//GsUlidjjDzkCMpmaHqNk54HZO1W7VbuV+5X4zY2bGQHXGW4+3 + Lra2t8LtbRjg1S2qGLyiulmzcGtLmmChPc6wtyxsSNLeCpK0d8TtnS95XPJ4+Nlx9lnFWcXB2KNH + 9009MLXGvsbewRp3sGWCV3hmDXxlhiNDnr1UTb0cGAwHy2rjC6uutrDEHayPwheKbEPcw33PkCpU + mXSbo6PaqbDN14rT2QRfq1SxiYgCrYJNRMvU2ehdC961MtCGv2VmE7FiveqvYSMZcCQH+PFcAj5b + UCJ5lpHFnl9Z2nSaEDPhuQNuxSgv9hwHhgoZOM61I20sLTo7MhnuFhgptrTtbImz8OJQBs4qTyL7 + kWyTEY+lbQs9wEaDkIDOQ2p0hQLPzz0gkN4mxFgtljFHrz2d/G3qa6/dc8M3rpL0S20/urzYLYUs + ZtWQxcy15UwGzmC4BAERf8wv7ILnuMu1SOAfSQejtLgFkCsPiclMYVm6MFKSuC5kM9ixdrHtL9Zl + yVWZerWK60w6wkErFyuRTKpUq6TctqQHHLF1cW3y1i7Xm/SC80wXt/r5ZLlSFpCkFys1RGIEn2zb + yoHbhexKhnJDQ8JCggaCbphJlyyq+lskcyDt4LydCysuIVHE9SU7UN22qgi5Bt7yiUwSEIKk+G5R + IbywgKDQ0NCAMH5oF24H0ofSyKNJjZKoG2dkMd7O1MK4BcYsxp0wMG7LKAbZeb2dT5vVh0v8WnS5 + WpM1xHKcXw5/YvPVX68JZgyvWB/1na3DuhUnHaIEtzYu9niiG/pe/ea7eQFznrXxKXnWr+rmwv6p + b+OOLA35/rr4SGYLRqvIF5Nco8sDbGdgG49MrO4jPRS26/LUzndqJgR917nafdNL3wWWpCasdofL + vsJjfYbPG3n1co1668xu0Vec7dZqSwaPaR/heOabVd7BJefW5c28ftnpi69aTfCZ1vrkgZE/rni2 + KZG9ZODRgZvwA6XF+/DXrgzZPdWuVljARItZk4dOC51qs2RXxiWV8vSl8j7nL5YuHjX6t5YZ1Xin + wATfVwOvv3jkedeR9Sxb0LbF6Grp3PPHv38f9fOI3TovBhPso2XFuA2wiAXpCUzq6chqyWpxavcz + 3qYSrtON1qWPeuzmvhrEcLJBMeTpw3IjWxa28Al+8ZsoSmNb1+t17uuqzptqQqqcyGSI4MWKI/uS + wvLocsGECPpem0SraHSDVpMth6OB9K1OXaDRjdCLyIkgKjkAhRxgaQ02poWFFY6zYsk+ZIyhTzIm + fEYzyMvLa4qBTPsnlPWkC5S3A8uetDWQZFo32pBMGCXzBmG/P1gWM+VaYtfM0vbV6hm7etV2XcmO + m8RendaDZzvi6JvBrVjzyIQT7+2Xjr/YYS+rm/Xz+Gt41UVVhCz+UneOQOOfcyJBntAyv+rnz3s8 + aL0urnJDDk/U3qJs5tmYc7ciX88Ut0wb+lNl55Q5S0SD91STvlb3z8T6FlTVPO8T4tA6bhl3/+8n + 3dtN87UJ7hX68+IYj8k5kyMWnfVP/nZ1qKLF4oP5iq2tv5mYvyxUuguffe9Cry+HNXNOLrUYeO7L + Kr++zRcHF08J9Bse6vwo0/1Use58Le91bdCyq71CvHeEDuJlqY+c7XwLF0tmlZXcuPNwE2Pjy+eD + 39QW1QSP+bbfhTZe90T3XpHFljhIY7dN0ti+25NejCpKvP0epbF9plazA2lszN+SLPzIjtSm9zKd + l8qIJHkmutEJHAu/4cJF2SyUDONyeSSAYCqb1XdJ/d8iHz3P/Mj8v8xGJZO3ta+xmrGgsMD1Tcfh + b7Ql7Fd/LCsrmRu1ddmRYZMCuwVx2s7Kf/XFGq9ifMuoI+47mIej7u6f//w1y/PxeNv37VQVjzO7 + 7/d1u+7n9ZRVypfcu/qD69Q6lwUhF8M0yerwe+sFNqRwz64Z5Hz7I7mHnuvmtMz7Zcr20gPW44m6 + tqtDHo3ce0mP9Z184vdZd8/kv5v2av3wku47v/fakF62e/+4ypkbzmzsfDL5dci5n0bOvtH2/b2R + 2Ue+tM7VX3LuF3PqEXYwJnaZVcj1NIe3X3x98MbAq+Ofnlng5DV95bVxrfacObzEEz/wNmaVy+yg + Mu8Y3ou97Zdim3clHR6r8h9U9CBMVfhk+z0Xu7uGbFQILPIFlW46wHRjrMyx1rhxpzJN0tWRM+nj + jg3veud95t7BJw5uX7u1xmUeKYLTzVggFy2PJgWNK00wyYNdC5fOvCCS5PI6S8LI4PQQmTgguGt6 + cEAwLygsICyoCy9AGhbCzRDzeCHBGZIGKTBGJb2eaHGy+JtWoaHttihXH85hzPl4CmwyQ6k1OpQF + QbiAOAZRDAIYxu8w+BZAhgaQYSgFik1SYAoJTismKVDwLxkYsuCfsNCT9lBwcMHynsUgsUbbmVnM + wDHLll7n++9NPOiTsLRf/q91L97+tPN09aOXbVLrkg7Koy1O7zty78qb+YPmDGsW5ldtIXC5tKCg + ZEfG2vPb7zJSfLZ298nnKze8eIQNLJ0/2eOozZzjCzwiyTUrWh74IXrQ087BU5bMGBBaE++xsd1h + 55/OFjuvCXm4od3BGe1XFk2p9fW4luE5qQfnfX9m3B7V2HLe3W+rAhNTh1hWuk496CnZqrO/emZU + R6dOcwWreGN7zO3RX5jnM+ldpfOBydetXfvt7zyQO6jriLmrl5dkz/VTP9q34c5OQauj6fFFW5Ld + o6fPW6GsVvn++MLX62Adscau8tHPdgtKr4xYJB9b0eVXJfFu/On3NdvKuti8695iz7wWa6onHH1Q + vGdtSvsIty0x4/MnHH95YlHP1r+1mHRz2pKs9iVZ4WsOFMZ3vGntHSt5+/VXrnFBW1KHJ/za5/uw + 6e85FyqHLY/IPpR/rHJ79oyxionab+6seL3kgvuZrm+kh5Q9rK9/MbZy/Y5lP3x+bG7q8lEDjjSP + Tj/h/eDNZ/u4ds8De0hXhKqHJ/bcGjkzodxuyq4xA54dyJwoPr943r6DU4+ooy9Xc0rrKp9tIpX3 + RghX356be3Cn9b534U836EItN6cea31q+9PSwxM9HheOwBO+a1Okqzo5qF3PbgPcakvuZ+4Trgr8 + vcOU7kOP3wuOnOW5Y5Z9bnGPB/vOBlSwGNNjXj64wDjGXAqKgBUoAg+oImArbpkVjHK/R+Mj7DCU + Tm1tZnec9NVjthRv3ZIJopHbmmzVYNDGGKwgDDtTebN9fd4UqdUgeYLQlWfIJWK9jODn6LPUWrm+ + ACZ3MpQMJoO4vJAgsitI7jwu6gaRsPvPnaH/VX5fUqGorD0fM7vTF9mc1pd3Xrm6f34/n8T1P19w + i2/vdP+XVb/ErteTRLO7VqeT57gKS9v0nr1h3mCy4zks+9bnO+9NsnJ67sia93DSUa8jQe0nLnr8 + R6YH+83nN0s879yMX1axxyfp8LRXgmM2x4duPL6pN2vpy5WKrzJ/9fs9KmnThOPX/aI4vusmJKSI + 7K8x2a9HzJxJqiY+SSMXvRpzpqzqlnfZmBcnXJ5Yb01Sir4VzFwSg/WJzmjm65+xuuzaScuiPktf + jlvVLLqFTfGScXUp+e/wBZ6J1uMxZzKqbutFn6jt+wKSl2xsm8/n5h1dWBs+9qsKMWOLp0Plm+cL + N+M/t+ub/P6lRc1ews6Q39cCi6winYwZx4Jkgg+TfN7k6RKmb08nFgvE3wTS2dKGrgmuOBzByKJ5 + VG4umkkWTSts4biueHivVN+y6x1c3nS6bJs0J+3a8grJcvHfHp7FzgXrW1b0KV+xPlY34A8rF46M + TKSKgpAEdag8opw/oeenn4uN0/AbjzCVo4KQbFIQYsgoMtKkIIT9lTMx1COCovqJ52Fga+eyyTWD + mZFdLtz+dn3e+Z8L+sXhlRz9yEFKe5e1P+/6fMY2zqnmS6cq07f1ZxyJJ1wS518Y1etK/+0bByzw + uOyJT1i3Pf/xlOP3wvH7V3bNsLU4OC3mysMk1wsJa2dfuzltxOnCPTdKH1sGjmfentWpfTvN62dv + ruXP5zg8t7qi2eEWv2h6tq12zraKrl9nBuzv53gnfXDPlvOmED2vWLnzXh7l9snldu+stTt4R9P9 + /Xhbl9q9tuLpD3/d1upu/JQv94d0Hrps990do+16f34qSet9nzy8PV82eBDeyraF44lzLeY9/ez7 + jAFVAYE3X46fcLRf6q1FmlLFuq6xp54V7P7GbVS6/4OlC/2DLfPc0w91b6v0Kn5od4C9/VhE1fWX + 90Zvubp8tT5kW/z+kT7NO+bafSaaOnJgVESLHVVVm+IyDy7p/b6wwLtwsSuZcat386HuBxe38z4e + cbvz7e1/xBxlnzrLK4zt2Cmm/bCBd1IfrLw4f9HhbuqdRb56y2b3c713Lyze45v8XeWI7pMqcsXf + qipcVu7+Jvphc/XbyTzF5ne1/Q5O9TmUsXOR58TmUkb3gI1pM7Zd876+ZdNhybf5yRan+JzEdaWb + VuSvrSqfm+P+2+yJLjntAnmrrVXlg6Z22F3+YNxh7zN32yYcWnBfeOk5LlNPsht9UH7whurOqrKf + uf7vHfcPGnw2rk3F2VeBi3tyUlpmH3JZ9pYsthpFFlukG0qB48wTqBQwG18GFJX8LamYR5LUhvT/ + lA1Zf0XABWUjjEeGdKWKRhfU5ZKw+49fsRQzPqwdDFg7GKB2gD239uErrbMHZ/1Z1TfFznHBPzz+ + boD3kt5tOmXfHpj4zTbLMHeW8Icva+zbXgjN/rH5WbuHYXvnW2462PU03oLb++QkhwLpxDGlw9sr + Ni4Wfn07a+iJ2oVJm23ZNRt/W9N5wyibjb/OTTs83N3idkbuLZ6oY/PAm2utE49VRW4dcnYfh5mz + NuvJEeWTboMrWv4R9cOlMOk6lTQkf2W5xCngZK+vXly9aOVwenDBCqH/TYdd5S55u0q7P3h9tfNA + Z6+4VL+lo7SXmnfbKhx6tq4uYtbY3z7f/PmENr/1qJw65NakhHHujysC067NDA/YEDRg/9Ye73gn + q5jdKzdvnB025sSiQvbT+NRZ3iEdarqqpF8m/fC10/rWPuOO/PEDc8K058MeHhftnlo6cUe1t77D + MDe/7476+oV1mNe1T5djX1TO3uDhs2pNxj2x14jLfsJFw0qudBhy0rtvD9G+Lf17tmc+/GXUoMDT + Plc1Q5z6ReVVvcAu71jHKB52vtq1amebUyl9b3atcLrtI9zhti3yC8G1PTXaUZe0N9vX7o6av//B + Xo/+58dOuxcnJFetnV57b9CSjW8ubMq4sqes6PO6M3V9bwr9V7n4rVw1OrPwxuT0/GGbA8f92v/r + wbvz/Pwe1Slr/GawZ/QKTdhzeXzkpH02sftPrYgI1M95rnqRTwxguwwZPmdBj4Sgcec2lbS6uDj+ + j7mbdkSVK+aduHSmZKqxdtaB2nm7ifJXXzybvC5pbVzQgsGyb2uLJaEbvREYv2Fd/aAom17xaAO6 + MbgzI75vYRF/+c6qA9xffCYFkwOp4gZ/hZpQHlfed4LwL/3SB+xbsGvBZjVelAwjg4bxeKjMDTUp + cyIykYw3KXO9P63M/Ql9PVm0BApPsIrKyKJSsmiW0UgcJlk0luxpYMfAWwb9q8ss+FcIQDO5Uqwt + kGh0nCy9kuxlJMAgg9vyCE8sFoMPPoH31Iehe+rUdzAKQE9HfztEZvyODIfwbOpCLPPxhBXzLiUX + uHNOntVntltoN7fZZcns+b3njj5RYD9zj2wYh93jRY32F+XYd7t63rI9HL47es2yJ/Lzkt3tQlaU + DZGNmzl6SlRiyln72V+ccO/r8eSz3lNExze9zb7aw4rjv/BG9zYrTm3xzCvteuW29FBk9/xRPk9c + Rq+cqR877Y8jHRlRnfZOdt6+fI2F/cK6rFdZnDnlnXp2yh4glHjZyFUD5829NvaP6hlPojpffBN+ + fGfIA1WHDdc3+tYdv/DEceN8v7J5cY7d7R5bTzrjVcNzu/Jwf8DPgxZ/K+xq+6Pt3h/Xb7i++bfz + riX9BAPCeCN93b+s/MP3xUV2N0I+b3PapCyVetVWfU0vC8uVeCe/HsU9XeIy7Kqr4p5envGlh9p1 + tGBV7vVenWTLaoaI0ifUeEq6lE2oPffkxeOWFQt8L/+0ouz4/SES/tVBVl9P7GGZZ/mLZWWOV4td + YvGWh7//2Ia1q5Z/wNHv/kVZ4L2yZxWD557FzlRE7Ux7UrbCpm+M8/xCr+OY//7KhSt6CvLahvx4 + YunSJaNGtXsVM8dr7eton8Kni1/szt7at+zK3Zx893t3QucXuPV9f6bKJyvnxsZXb6bctSu8Iw/f + +IasY8VOr63NUUpmdf9lUWp8wu7C/u0q8pvxvEc94NtW9ny9+ujyIXsqShb2H5kaHyOo7n1oYe4g + 28KY7LcFS/bsVCpHHBLpXBxGJf7ELWZtIotZ6xg4ThbN+acLV9O/Dqy/OVJetA8mHzqIbZhce9M7 + L0CK+p4d15E0nXUlfeoXsrggtb0tjVw1/fGjM0XNa/13KmeO++6u+0VSarLEnptKJpd3KvRr8qu7 + yR8+TaWiY2H7j+7sZONfERGNajOrGMeSoqevHPvdYvVAX8vz3KGiwO1V/ax6ch09R23Ii04evDs0 + 2CnU+WRSRvsUy3OiWa635i1oKdcOYm+ousbxd+7gGGX7Wj5xdrTix9nSvuf3TmbVZj3gTvj14reH + 18+qm7ay35fq/DU4a8fbHVu/P3i77u3+idi5m9sXSZedCD+gODDs9e3XP7geLwtT1HW2fPwgemKz + /OOe7/uH/3RlQNvUWwdKrJvvXamY//X119X+sheffcZcF/NtO/4o71U7brQ4OjPi9aA2dQm5bvxv + 3q6JcZocnrJtxN4dK3kXJM67ugyYbsHp6TFzyNJpN2+5T7pVOu+ngmc97npkFzuOwA/vSO2YtdzB + q7Zj8tm+7EHekyuKGX7geNK+3keW3GKGKxhqhkJz+j92Id70nTaTmBxCupmGpF39HUMcMDfOWHCd + 0C+Ou3BDeFz4GvhBREbcHhe+ONHvwN2OU11Vp6qzPBd+V9DokgnGCjfe5UvGpP5Mj7Q+Zfq7tmP7 + +Ae5+x8Y8uTc1cf3v1hbutDnFi+z+V37K+dOT4vvMKLjstoFhUPnB5zoMlTWYs1vVzeOaam8w291 + XH/hvfqBTUXvxY/7jPyyk2jgYq/7jKoAYWmk96n7L+2sxHdTCsZYF4wp07gMK5cN8rPwyjiw+WDG + olP3xRf5udFb3148d/1t8bvrkrRjP1zdXOYg33di5JxHT3Mjv7+0r+CXdz8v32a3hGuRdD122/bv + vVKGVDwZd3v2xWk7NtkV3XVZ1KPLiOyvjw7h/3J7+enzy6punTtvP9plwNne7FOq7b/6h4+729uh + eqxVv8vdnqxNi908ORd/sHGv/+OcFZO5XX+fFon9H5Hg2m4NCmVuZHN0cmVhbQ0KZW5kb2JqDQoy + MCAwIG9iag0KPDwvVHlwZS9NZXRhZGF0YS9TdWJ0eXBlL1hNTC9MZW5ndGggMzA4ND4+DQpzdHJl + YW0NCjw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+ + PHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iMy4xLTcwMSI+Cjxy + ZGY6UkRGIHhtbG5zOnJkZj0iaHR0cDovL3d3dy53My5vcmcvMTk5OS8wMi8yMi1yZGYtc3ludGF4 + LW5zIyI+CjxyZGY6RGVzY3JpcHRpb24gcmRmOmFib3V0PSIiICB4bWxuczpwZGY9Imh0dHA6Ly9u + cy5hZG9iZS5jb20vcGRmLzEuMy8iPgo8cGRmOlByb2R1Y2VyPk1pY3Jvc29mdMKuIFdvcmQgZm9y + IE9mZmljZSAzNjU8L3BkZjpQcm9kdWNlcj48L3JkZjpEZXNjcmlwdGlvbj4KPHJkZjpEZXNjcmlw + dGlvbiByZGY6YWJvdXQ9IiIgIHhtbG5zOmRjPSJodHRwOi8vcHVybC5vcmcvZGMvZWxlbWVudHMv + MS4xLyI+CjxkYzpjcmVhdG9yPjxyZGY6U2VxPjxyZGY6bGk+S3Jpc3RhIFByYXRpY288L3JkZjps + aT48L3JkZjpTZXE+PC9kYzpjcmVhdG9yPjwvcmRmOkRlc2NyaXB0aW9uPgo8cmRmOkRlc2NyaXB0 + aW9uIHJkZjphYm91dD0iIiAgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAv + Ij4KPHhtcDpDcmVhdG9yVG9vbD5NaWNyb3NvZnTCriBXb3JkIGZvciBPZmZpY2UgMzY1PC94bXA6 + Q3JlYXRvclRvb2w+PHhtcDpDcmVhdGVEYXRlPjIwMjAtMDMtMjBUMTA6NDQ6NDYtMDc6MDA8L3ht + cDpDcmVhdGVEYXRlPjx4bXA6TW9kaWZ5RGF0ZT4yMDIwLTAzLTIwVDEwOjQ0OjQ2LTA3OjAwPC94 + bXA6TW9kaWZ5RGF0ZT48L3JkZjpEZXNjcmlwdGlvbj4KPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJv + dXQ9IiIgIHhtbG5zOnhtcE1NPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvbW0vIj4KPHht + cE1NOkRvY3VtZW50SUQ+dXVpZDo4RjI5Q0E4Qy1FRThCLTQ3NTktQkM5Qi1BMDhFRkVFNjYyMDE8 + L3htcE1NOkRvY3VtZW50SUQ+PHhtcE1NOkluc3RhbmNlSUQ+dXVpZDo4RjI5Q0E4Qy1FRThCLTQ3 + NTktQkM5Qi1BMDhFRkVFNjYyMDE8L3htcE1NOkluc3RhbmNlSUQ+PC9yZGY6RGVzY3JpcHRpb24+ + CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg + ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAg + ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg + ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAg + ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg + ICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg + ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg + ICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg + ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAg + ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg + ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAg + ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg + ICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg + ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg + ICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg + ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAg + ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg + ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAg + ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg + ICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAg + ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg + ICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg + ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg + ICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg + ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAg + ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg + ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAg + ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg + ICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg + ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg + ICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg + ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAg + ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg + ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAg + ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg + ICAgICAgICAgICAgICAgICAgICAgICAgIAo8L3JkZjpSREY+PC94OnhtcG1ldGE+PD94cGFja2V0 + IGVuZD0idyI/Pg0KZW5kc3RyZWFtDQplbmRvYmoNCjIxIDAgb2JqDQo8PC9EaXNwbGF5RG9jVGl0 + bGUgdHJ1ZT4+DQplbmRvYmoNCjIyIDAgb2JqDQo8PC9UeXBlL1hSZWYvU2l6ZSAyMi9XWyAxIDQg + Ml0gL1Jvb3QgMSAwIFIvSW5mbyA5IDAgUi9JRFs8OENDQTI5OEY4QkVFNTk0N0JDOUJBMDhFRkVF + NjYyMDE+PDhDQ0EyOThGOEJFRTU5NDdCQzlCQTA4RUZFRTY2MjAxPl0gL0ZpbHRlci9GbGF0ZURl + Y29kZS9MZW5ndGggODM+Pg0Kc3RyZWFtDQp4nC3LsQFAQAyF4ZfcHbW1KJUKnTHYxgR6k1Ba48R7 + UuQrkh+IqdVid8DHLm5iD/GDpInkXmwibs7cRRJZFGHi/2yiKyfzdhALGVcyX8ALyoALUA0KZW5k + c3RyZWFtDQplbmRvYmoNCnhyZWYNCjAgMjMNCjAwMDAwMDAwMTAgNjU1MzUgZg0KMDAwMDAwMDAx + NyAwMDAwMCBuDQowMDAwMDAwMTY2IDAwMDAwIG4NCjAwMDAwMDAyMjIgMDAwMDAgbg0KMDAwMDAw + MDQ4NiAwMDAwMCBuDQowMDAwMDAwNjkyIDAwMDAwIG4NCjAwMDAwMDA4NTkgMDAwMDAgbg0KMDAw + MDAwMTA5OCAwMDAwMCBuDQowMDAwMDAxMTUxIDAwMDAwIG4NCjAwMDAwMDEyMDQgMDAwMDAgbg0K + MDAwMDAwMDAxMSA2NTUzNSBmDQowMDAwMDAwMDEyIDY1NTM1IGYNCjAwMDAwMDAwMTMgNjU1MzUg + Zg0KMDAwMDAwMDAxNCA2NTUzNSBmDQowMDAwMDAwMDE1IDY1NTM1IGYNCjAwMDAwMDAwMTYgNjU1 + MzUgZg0KMDAwMDAwMDAxNyA2NTUzNSBmDQowMDAwMDAwMDAwIDY1NTM1IGYNCjAwMDAwMDE4Njcg + MDAwMDAgbg0KMDAwMDAwMTg5NCAwMDAwMCBuDQowMDAwMDIxMzc0IDAwMDAwIG4NCjAwMDAwMjQ1 + NDEgMDAwMDAgbg0KMDAwMDAyNDU4NiAwMDAwMCBuDQp0cmFpbGVyDQo8PC9TaXplIDIzL1Jvb3Qg + MSAwIFIvSW5mbyA5IDAgUi9JRFs8OENDQTI5OEY4QkVFNTk0N0JDOUJBMDhFRkVFNjYyMDE+PDhD + Q0EyOThGOEJFRTU5NDdCQzlCQTA4RUZFRTY2MjAxPl0gPj4NCnN0YXJ0eHJlZg0KMjQ4NjgNCiUl + RU9GDQp4cmVmDQowIDANCnRyYWlsZXINCjw8L1NpemUgMjMvUm9vdCAxIDAgUi9JbmZvIDkgMCBS + L0lEWzw4Q0NBMjk4RjhCRUU1OTQ3QkM5QkEwOEVGRUU2NjIwMT48OENDQTI5OEY4QkVFNTk0N0JD + OUJBMDhFRkVFNjYyMDE+XSAvUHJldiAyNDg2OC9YUmVmU3RtIDI0NTg2Pj4NCnN0YXJ0eHJlZg0K + MjU0ODQNCiUlRU9G + - 0 + - null + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + Content-Length: + - '25662' + Content-Type: + - application/pdf + User-Agent: + - azsdk-python-ai-formrecognizer/1.0.0b4 Python/3.7.3 (Windows-10-10.0.18362-SP0) + Python/3.7.3 (Windows-10-10.0.18362-SP0) + method: POST + uri: https://centraluseuap.api.cognitive.microsoft.com/formrecognizer/v2.0-preview/custom/models/9a939b7b-6f9a-4413-8745-69bcaafed0d7/analyze?includeTextDetails=false + response: + body: + string: '' + headers: + apim-request-id: + - 9e3777e8-7b0d-4fdd-a4ac-c7ef3dd6dd83 + content-length: + - '0' + date: + - Mon, 15 Jun 2020 19:53:18 GMT + operation-location: + - https://centraluseuap.api.cognitive.microsoft.com/formrecognizer/v2.0-preview/custom/models/9a939b7b-6f9a-4413-8745-69bcaafed0d7/analyzeresults/5afeabb8-4ebd-4cc2-9166-435a79e3b495 + strict-transport-security: + - max-age=31536000; includeSubDomains; preload + x-content-type-options: + - nosniff + x-envoy-upstream-service-time: + - '141' + status: + code: 202 + message: Accepted +- request: + body: null + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + User-Agent: + - azsdk-python-ai-formrecognizer/1.0.0b4 Python/3.7.3 (Windows-10-10.0.18362-SP0) + Python/3.7.3 (Windows-10-10.0.18362-SP0) + method: GET + uri: https://centraluseuap.api.cognitive.microsoft.com/formrecognizer/v2.0-preview/custom/models/9a939b7b-6f9a-4413-8745-69bcaafed0d7/analyzeresults/5afeabb8-4ebd-4cc2-9166-435a79e3b495 + response: + body: + string: '{"status": "succeeded", "createdDateTime": "2020-06-15T19:53:18Z", + "lastUpdatedDateTime": "2020-06-15T19:53:22Z", "analyzeResult": {"version": + "2.0.0", "readResults": [{"page": 1, "language": "en", "angle": 0, "width": + 8.4967, "height": 10.9967, "unit": "inch"}], "pageResults": [{"page": 1, "tables": + []}], "documentResults": [{"docType": "custom:form", "pageRange": [1, 1], + "fields": {"CompanyAddress": null, "CompanyName": null, "CompanyPhoneNumber": + null, "DatedAs": null, "Email": null, "Merchant": null, "PhoneNumber": null, + "PurchaseOrderNumber": null, "Quantity": null, "Signature": null, "Subtotal": + null, "Tax": null, "Total": null, "VendorName": null, "Website": null}}], + "errors": []}}' + headers: + apim-request-id: + - 594ec1ca-bb97-482b-bcc6-d8d155420ba9 + content-length: + - '632' + content-type: + - application/json; charset=utf-8 + date: + - Mon, 15 Jun 2020 19:53:24 GMT + strict-transport-security: + - max-age=31536000; includeSubDomains; preload + x-content-type-options: + - nosniff + x-envoy-upstream-service-time: + - '225' + status: + code: 200 + message: OK +version: 1 diff --git a/sdk/formrecognizer/azure-ai-formrecognizer/tests/recordings/test_custom_forms.test_custom_form_unlabeled_blank_page.yaml b/sdk/formrecognizer/azure-ai-formrecognizer/tests/recordings/test_custom_forms.test_custom_form_unlabeled_blank_page.yaml new file mode 100644 index 000000000000..0d33ccc49c95 --- /dev/null +++ b/sdk/formrecognizer/azure-ai-formrecognizer/tests/recordings/test_custom_forms.test_custom_form_unlabeled_blank_page.yaml @@ -0,0 +1,696 @@ +interactions: +- request: + body: 'b''b\''{"source": "containersasurl", "sourceFilter": {"prefix": "", "includeSubFolders": + false}, "useLabelFile": false}\''''' + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + Content-Length: + - '288' + Content-Type: + - application/json + User-Agent: + - azsdk-python-ai-formrecognizer/1.0.0b4 Python/3.7.3 (Windows-10-10.0.18362-SP0) + Python/3.7.3 (Windows-10-10.0.18362-SP0) + method: POST + uri: https://centraluseuap.api.cognitive.microsoft.com/formrecognizer/v2.0-preview/custom/models + response: + body: + string: '' + headers: + apim-request-id: + - a2cfb478-359d-49fe-9530-c68011a729f4 + content-length: + - '0' + date: + - Mon, 15 Jun 2020 19:50:05 GMT + location: + - https://centraluseuap.api.cognitive.microsoft.com/formrecognizer/v2.0-preview/custom/models/5c86788f-f987-4d7f-885b-efde241d7a16 + strict-transport-security: + - max-age=31536000; includeSubDomains; preload + x-content-type-options: + - nosniff + x-envoy-upstream-service-time: + - '453' + status: + code: 201 + message: Created +- request: + body: null + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + User-Agent: + - azsdk-python-ai-formrecognizer/1.0.0b4 Python/3.7.3 (Windows-10-10.0.18362-SP0) + Python/3.7.3 (Windows-10-10.0.18362-SP0) + method: GET + uri: https://centraluseuap.api.cognitive.microsoft.com/formrecognizer/v2.0-preview/custom/models/5c86788f-f987-4d7f-885b-efde241d7a16?includeKeys=true + response: + body: + string: '{"modelInfo": {"modelId": "5c86788f-f987-4d7f-885b-efde241d7a16", "status": + "creating", "createdDateTime": "2020-06-15T19:50:06Z", "lastUpdatedDateTime": + "2020-06-15T19:50:06Z"}}' + headers: + apim-request-id: + - 8a736533-7ba1-4706-89da-06046fa5b490 + content-type: + - application/json; charset=utf-8 + date: + - Mon, 15 Jun 2020 19:50:11 GMT + strict-transport-security: + - max-age=31536000; includeSubDomains; preload + transfer-encoding: + - chunked + x-content-type-options: + - nosniff + x-envoy-upstream-service-time: + - '50' + status: + code: 200 + message: OK +- request: + body: null + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + User-Agent: + - azsdk-python-ai-formrecognizer/1.0.0b4 Python/3.7.3 (Windows-10-10.0.18362-SP0) + Python/3.7.3 (Windows-10-10.0.18362-SP0) + method: GET + uri: https://centraluseuap.api.cognitive.microsoft.com/formrecognizer/v2.0-preview/custom/models/5c86788f-f987-4d7f-885b-efde241d7a16?includeKeys=true + response: + body: + string: '{"modelInfo": {"modelId": "5c86788f-f987-4d7f-885b-efde241d7a16", "status": + "creating", "createdDateTime": "2020-06-15T19:50:06Z", "lastUpdatedDateTime": + "2020-06-15T19:50:06Z"}}' + headers: + apim-request-id: + - 72e4e444-1b22-48a2-9e8a-f4e419e61476 + content-type: + - application/json; charset=utf-8 + date: + - Mon, 15 Jun 2020 19:50:16 GMT + strict-transport-security: + - max-age=31536000; includeSubDomains; preload + transfer-encoding: + - chunked + x-content-type-options: + - nosniff + x-envoy-upstream-service-time: + - '172' + status: + code: 200 + message: OK +- request: + body: null + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + User-Agent: + - azsdk-python-ai-formrecognizer/1.0.0b4 Python/3.7.3 (Windows-10-10.0.18362-SP0) + Python/3.7.3 (Windows-10-10.0.18362-SP0) + method: GET + uri: https://centraluseuap.api.cognitive.microsoft.com/formrecognizer/v2.0-preview/custom/models/5c86788f-f987-4d7f-885b-efde241d7a16?includeKeys=true + response: + body: + string: '{"modelInfo": {"modelId": "5c86788f-f987-4d7f-885b-efde241d7a16", "status": + "ready", "createdDateTime": "2020-06-15T19:50:06Z", "lastUpdatedDateTime": + "2020-06-15T19:50:17Z"}, "keys": {"clusters": {"0": ["Additional Notes:", + "Address:", "Company Name:", "Company Phone:", "Dated As:", "Details", "Email:", + "Hero Limited", "Name:", "Phone:", "Purchase Order", "Purchase Order #:", + "Quantity", "SUBTOTAL", "Seattle, WA 93849 Phone:", "Shipped From", "Shipped + To", "TAX", "TOTAL", "Total", "Unit Price", "Vendor Name:", "Website:"]}}, + "trainResult": {"trainingDocuments": [{"documentName": "Form_1.jpg", "pages": + 1, "errors": [], "status": "succeeded"}, {"documentName": "Form_2.jpg", "pages": + 1, "errors": [], "status": "succeeded"}, {"documentName": "Form_3.jpg", "pages": + 1, "errors": [], "status": "succeeded"}, {"documentName": "Form_4.jpg", "pages": + 1, "errors": [], "status": "succeeded"}, {"documentName": "Form_5.jpg", "pages": + 1, "errors": [], "status": "succeeded"}], "errors": []}}' + headers: + apim-request-id: + - 9f297671-7955-4ed1-acc3-d17877d2fbdf + content-type: + - application/json; charset=utf-8 + date: + - Mon, 15 Jun 2020 19:50:20 GMT + strict-transport-security: + - max-age=31536000; includeSubDomains; preload + transfer-encoding: + - chunked + x-content-type-options: + - nosniff + x-envoy-upstream-service-time: + - '51' + status: + code: 200 + message: OK +- request: + body: !!python/object/new:_io.BytesIO + state: !!python/tuple + - !!binary | + JVBERi0xLjcNCiW1tbW1DQoxIDAgb2JqDQo8PC9UeXBlL0NhdGFsb2cvUGFnZXMgMiAwIFIvTGFu + Zyhlbi1VUykgL1N0cnVjdFRyZWVSb290IDEwIDAgUi9NYXJrSW5mbzw8L01hcmtlZCB0cnVlPj4v + TWV0YWRhdGEgMjAgMCBSL1ZpZXdlclByZWZlcmVuY2VzIDIxIDAgUj4+DQplbmRvYmoNCjIgMCBv + YmoNCjw8L1R5cGUvUGFnZXMvQ291bnQgMS9LaWRzWyAzIDAgUl0gPj4NCmVuZG9iag0KMyAwIG9i + ag0KPDwvVHlwZS9QYWdlL1BhcmVudCAyIDAgUi9SZXNvdXJjZXM8PC9Gb250PDwvRjEgNSAwIFI+ + Pi9FeHRHU3RhdGU8PC9HUzcgNyAwIFIvR1M4IDggMCBSPj4vUHJvY1NldFsvUERGL1RleHQvSW1h + Z2VCL0ltYWdlQy9JbWFnZUldID4+L01lZGlhQm94WyAwIDAgNjEyIDc5Ml0gL0NvbnRlbnRzIDQg + MCBSL0dyb3VwPDwvVHlwZS9Hcm91cC9TL1RyYW5zcGFyZW5jeS9DUy9EZXZpY2VSR0I+Pi9UYWJz + L1MvU3RydWN0UGFyZW50cyAwPj4NCmVuZG9iag0KNCAwIG9iag0KPDwvRmlsdGVyL0ZsYXRlRGVj + b2RlL0xlbmd0aCAxMzI+Pg0Kc3RyZWFtDQp4nC2MsQrCQBBE+4X9hynV4m73iJ4HIUUuMSgEFA8s + xFJTKaj/D67iFAPDPB78HnXtx7ztIE2Dtst4MomTb5IGCFbWMQW8rkynBR5MbWHyG4WqkwrlxqTG + CRQxOAkVoiS3tOdu3HCMmN7mxPRb6/8amM4zzC8oO6bejAcm9GPGB3fjHKoNCmVuZHN0cmVhbQ0K + ZW5kb2JqDQo1IDAgb2JqDQo8PC9UeXBlL0ZvbnQvU3VidHlwZS9UcnVlVHlwZS9OYW1lL0YxL0Jh + c2VGb250L0JDREVFRStDYWxpYnJpL0VuY29kaW5nL1dpbkFuc2lFbmNvZGluZy9Gb250RGVzY3Jp + cHRvciA2IDAgUi9GaXJzdENoYXIgMzIvTGFzdENoYXIgMzIvV2lkdGhzIDE4IDAgUj4+DQplbmRv + YmoNCjYgMCBvYmoNCjw8L1R5cGUvRm9udERlc2NyaXB0b3IvRm9udE5hbWUvQkNERUVFK0NhbGli + cmkvRmxhZ3MgMzIvSXRhbGljQW5nbGUgMC9Bc2NlbnQgNzUwL0Rlc2NlbnQgLTI1MC9DYXBIZWln + aHQgNzUwL0F2Z1dpZHRoIDUyMS9NYXhXaWR0aCAxNzQzL0ZvbnRXZWlnaHQgNDAwL1hIZWlnaHQg + MjUwL1N0ZW1WIDUyL0ZvbnRCQm94WyAtNTAzIC0yNTAgMTI0MCA3NTBdIC9Gb250RmlsZTIgMTkg + MCBSPj4NCmVuZG9iag0KNyAwIG9iag0KPDwvVHlwZS9FeHRHU3RhdGUvQk0vTm9ybWFsL2NhIDE+ + Pg0KZW5kb2JqDQo4IDAgb2JqDQo8PC9UeXBlL0V4dEdTdGF0ZS9CTS9Ob3JtYWwvQ0EgMT4+DQpl + bmRvYmoNCjkgMCBvYmoNCjw8L0F1dGhvcihLcmlzdGEgUHJhdGljbykgL0NyZWF0b3Io/v8ATQBp + AGMAcgBvAHMAbwBmAHQArgAgAFcAbwByAGQAIABmAG8AcgAgAE8AZgBmAGkAYwBlACAAMwA2ADUp + IC9DcmVhdGlvbkRhdGUoRDoyMDIwMDMyMDEwNDQ0Ni0wNycwMCcpIC9Nb2REYXRlKEQ6MjAyMDAz + MjAxMDQ0NDYtMDcnMDAnKSAvUHJvZHVjZXIo/v8ATQBpAGMAcgBvAHMAbwBmAHQArgAgAFcAbwBy + AGQAIABmAG8AcgAgAE8AZgBmAGkAYwBlACAAMwA2ADUpID4+DQplbmRvYmoNCjE3IDAgb2JqDQo8 + PC9UeXBlL09ialN0bS9OIDcvRmlyc3QgNDYvRmlsdGVyL0ZsYXRlRGVjb2RlL0xlbmd0aCAyOTY+ + Pg0Kc3RyZWFtDQp4nG1R0WrCMBR9F/yH+we3sa1jIMKYyoZYSivsofgQ610NtomkKejfL3ftsANf + wjk355ycJCKGAEQEsQDhQRCD8Oh1DmIGUTgDEUIU++EcopcAFgtMWR1AhjmmuL9fCXNnu9Kta2pw + W0BwAEwrCFmzXE4nvSUYLCtTdg1p98wpuEp2gME1UuwtUWaMw8zUtJNX7sh5qbQ+i3e5Lk84Jupj + RrsJ3dyW7iCG6I3P0sYRJrys9elB9l56NDfMqXT4QfJEtsfs+cOfulaa8rPkhjx40z5BOmX0wK1T + 39KDX/Zl7OVozOVxe560ZyLHJR3uZGnNiL+f/TriKyVrU40Gea1ONNL253hZZWWDG1V1loa7Jl3T + FvzH83+vm8iG2qKnj6efTn4AVAqiuw0KZW5kc3RyZWFtDQplbmRvYmoNCjE4IDAgb2JqDQpbIDIy + Nl0gDQplbmRvYmoNCjE5IDAgb2JqDQo8PC9GaWx0ZXIvRmxhdGVEZWNvZGUvTGVuZ3RoIDE5Mzg5 + L0xlbmd0aDEgODE3NDA+Pg0Kc3RyZWFtDQp4nOx9B3xUVdr+OfdOy8wkM5NkkkkmYWaYJASGFCCB + BJAMpNA7gwk1IYWAAQKEIgJGUdAo9l7Rta1YJgNqwO5iWQv2vhZ2XVdXse3qKgL5nnPfORDY1f+3 + 1fX7z5s88zznPeWe+t6TH8kPxhljdnzoWG3lqIoZBf1stzPumcAYf6Jy1ITyq5qr4hnPzGBMKZw8 + vWDgtY/W3YO8s1Crtn5JXetF716EsiddgvwP6le3eXe1vlHM2LYLGNM/0NS6cMnGd9UhjC1dy1h8 + YGHLyU2vVu4oYuwW1LF90NxY1/DtxJPDaM+K9gY3wxF/Z8Z+pCuQzmpe0rZ2xDjjAaQ/YmzRHS3L + 6uvyGvrezNi9hSg+c0nd2tZ8c/abyG9Gee+Sxra6q07ftppxXzLSZyytW9J43YGv5zP2KfpbuLJ1 + 2cq2bjfbzHjGQVG+dUVja9LC3mmMnXITHvcJE3NhGLpv9uI1H8+3Df+apZmYsPs/Wf+s4NfHrpn8 + /YFD7XGfmgYjGccURoZ6BnaY8T3mbd8fOLAt7lOtpR6WdofwuPuxdmZnw6EVcAHbwljiYO25nKm6 + AL+A6ZlJf6V+EJrsRay+wDYrzMQUm15RFJ2q6D5g+d2PsKxTtB7AJk73elmQsexnqQ/G65QcL+Pd + Ik+9T58gRsqSdQlHe8OfZ//fm+F1dsdP3Yf/K6ZrZDf81H34e8xg+Pf0V93/85qHf4fpiljtT92H + mP3zpjzNrvyp+/BzMOX3bMw/Uo9/w1r+1X2JWcxiFrOY/eOmXM3NP5hXy/b/J/vyczG1mJ3zU/ch + ZjGLWcxi9o+b7lHW9B9/5hJ23n/6mTGLWcxiFrOYxSxmMYtZzGIWs/+7Fvs5M2Yxi1nMYhazmMUs + ZjGLWcxiFrOYxey/23jst9FjFrOYxSxmMYtZzGIWs5jFLGYxi1nMYhazmMUsZjGLWcxiFrOYxSxm + MYtZzGIWs5jFLGYxi1nMYhazmMUsZjGLWcz+S6x790/dg5jF7Cc2NYqM6P8k1YEUlLKa6dhSpFOY + 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 + +xG3mmhL96QrfW1pvHxyGl+Wdmra+WmqzfW8Swm6+vavsqU+n/pe6uepuqRgat/8KpZiT/GmqE4x + tpSJM6o0LqsgHlCsjdWT4s+psjm5zelxKpWfO/lmpnIv54zbQaoJZXZyp6dKfZCLX6LTM84vYDMC + 47tMbNr4sGnK7DA/K5w9XXwGp84KG84Ks9Cs2dWdnJ9Xo/1OQjhZ/FKJlj5z61aWOWp8OHN6dUTd + ti1zVM34cLvQwaCmu4VmKFITmLdy1cpAdfAE5njf8YVDdT5sf96u2GzcZuu2KUEbOm9L8CQo4qM7 + QQ0mDBhSZYv3xCviozteTQnGwyPG18c6ZUaVzeKxKKEyy2SLErSUlVcFLXmFVX81zh1inPTkQNs8 + fMxb2RbQvpGq4atEMiC84ntlG9Lia5WWZoEfNSoGmr8S1iadbT9e67/d+E/dgZ+/0W/yjOxWzmAN + yibgdOA0oB04FdgIbADWA6cA64CTgbXAGmA1sApoA1YCy4FWYBmwFFgCtAAnAYuBRUAzsBBoAhqB + BqAeWADUAbXAfGAeMBeYA8wGZgE1QDVwIjATCAEzgOnANGAqMAWYDEwCJgITgPHAOGAsMAYYDVQB + lUAFUA6MAkYCQaAMGAGcAAwHhgFDgVKgBBgCDAaKgSJgEDAQGAAUAgVAPpAH9AcCQD+gL5AL9AFy + gGwgC/ADvQEf4AU8QC8gE8gA3EA6kAa4gFQgBXACyUASkAg4ADtgAxKAeMAKWAAzEAeYACNgAPSA + bmQ3PlVAATjAWAOHjx8GDgEHge+BA8B3wLfAX4BvgK+BPwN/Ar4CvgS+AD4HPgP2A58CnwB/BD4G + PgL+AHwI/B74APgd8FtgH/A+8B7wLvAO8BvgbeAt4E3gDeB14DXgVeAV4GXgJeBF4AXgeWAv8Bzw + LPAM8DTwa+Ap4EngCeBxYA/wK+Ax4FHgEeBh4CHgQeAB4H5gN7AL6ALuA+4F7gF2AjuACNAJhIG7 + gbuAO4E7gO3A7cAvgduAW4FbgJuBm4BfADcCNwDbgOuB64BrgWuAq4GrgCuBK4DLgcuAS4FLgIuB + i4ALgQuA84HzgK3AucA5QAdwNnAWsAXYDJzJGka2c5x/jvPPcf45zj/H+ec4/xznn+P8c5x/jvPP + cf45zj/H+ec4/xznn+P8c5x/jvPPVwCIARwxgCMGcMQAjhjAEQM4YgBHDOCIARwxgCMGcMQAjhjA + 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 + uHNOntVntltoN7fZZcns+b3njj5RYD9zj2wYh93jRY32F+XYd7t63rI9HL47es2yJ/Lzkt3tQlaU + DZGNmzl6SlRiyln72V+ccO/r8eSz3lNExze9zb7aw4rjv/BG9zYrTm3xzCvteuW29FBk9/xRPk9c + Rq+cqR877Y8jHRlRnfZOdt6+fI2F/cK6rFdZnDnlnXp2yh4glHjZyFUD5829NvaP6hlPojpffBN+ + fGfIA1WHDdc3+tYdv/DEceN8v7J5cY7d7R5bTzrjVcNzu/Jwf8DPgxZ/K+xq+6Pt3h/Xb7i++bfz + riX9BAPCeCN93b+s/MP3xUV2N0I+b3PapCyVetVWfU0vC8uVeCe/HsU9XeIy7Kqr4p5envGlh9p1 + tGBV7vVenWTLaoaI0ifUeEq6lE2oPffkxeOWFQt8L/+0ouz4/SES/tVBVl9P7GGZZ/mLZWWOV4td + YvGWh7//2Ia1q5Z/wNHv/kVZ4L2yZxWD557FzlRE7Ux7UrbCpm+M8/xCr+OY//7KhSt6CvLahvx4 + YunSJaNGtXsVM8dr7eton8Kni1/szt7at+zK3Zx893t3QucXuPV9f6bKJyvnxsZXb6bctSu8Iw/f + +IasY8VOr63NUUpmdf9lUWp8wu7C/u0q8pvxvEc94NtW9ny9+ujyIXsqShb2H5kaHyOo7n1oYe4g + 28KY7LcFS/bsVCpHHBLpXBxGJf7ELWZtIotZ6xg4ThbN+acLV9O/Dqy/OVJetA8mHzqIbZhce9M7 + L0CK+p4d15E0nXUlfeoXsrggtb0tjVw1/fGjM0XNa/13KmeO++6u+0VSarLEnptKJpd3KvRr8qu7 + yR8+TaWiY2H7j+7sZONfERGNajOrGMeSoqevHPvdYvVAX8vz3KGiwO1V/ax6ch09R23Ii04evDs0 + 2CnU+WRSRvsUy3OiWa635i1oKdcOYm+ousbxd+7gGGX7Wj5xdrTix9nSvuf3TmbVZj3gTvj14reH + 18+qm7ay35fq/DU4a8fbHVu/P3i77u3+idi5m9sXSZedCD+gODDs9e3XP7geLwtT1HW2fPwgemKz + /OOe7/uH/3RlQNvUWwdKrJvvXamY//X119X+sheffcZcF/NtO/4o71U7brQ4OjPi9aA2dQm5bvxv + 3q6JcZocnrJtxN4dK3kXJM67ugyYbsHp6TFzyNJpN2+5T7pVOu+ngmc97npkFzuOwA/vSO2YtdzB + q7Zj8tm+7EHekyuKGX7geNK+3keW3GKGKxhqhkJz+j92Id70nTaTmBxCupmGpF39HUMcMDfOWHCd + 0C+Ou3BDeFz4GvhBREbcHhe+ONHvwN2OU11Vp6qzPBd+V9DokgnGCjfe5UvGpP5Mj7Q+Zfq7tmP7 + +Ae5+x8Y8uTc1cf3v1hbutDnFi+z+V37K+dOT4vvMKLjstoFhUPnB5zoMlTWYs1vVzeOaam8w291 + XH/hvfqBTUXvxY/7jPyyk2jgYq/7jKoAYWmk96n7L+2sxHdTCsZYF4wp07gMK5cN8rPwyjiw+WDG + olP3xRf5udFb3148d/1t8bvrkrRjP1zdXOYg33di5JxHT3Mjv7+0r+CXdz8v32a3hGuRdD122/bv + vVKGVDwZd3v2xWk7NtkV3XVZ1KPLiOyvjw7h/3J7+enzy6punTtvP9plwNne7FOq7b/6h4+729uh + eqxVv8vdnqxNi908ORd/sHGv/+OcFZO5XX+fFon9H5Hg2m4NCmVuZHN0cmVhbQ0KZW5kb2JqDQoy + MCAwIG9iag0KPDwvVHlwZS9NZXRhZGF0YS9TdWJ0eXBlL1hNTC9MZW5ndGggMzA4ND4+DQpzdHJl + YW0NCjw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+ + PHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iMy4xLTcwMSI+Cjxy + ZGY6UkRGIHhtbG5zOnJkZj0iaHR0cDovL3d3dy53My5vcmcvMTk5OS8wMi8yMi1yZGYtc3ludGF4 + LW5zIyI+CjxyZGY6RGVzY3JpcHRpb24gcmRmOmFib3V0PSIiICB4bWxuczpwZGY9Imh0dHA6Ly9u + cy5hZG9iZS5jb20vcGRmLzEuMy8iPgo8cGRmOlByb2R1Y2VyPk1pY3Jvc29mdMKuIFdvcmQgZm9y + IE9mZmljZSAzNjU8L3BkZjpQcm9kdWNlcj48L3JkZjpEZXNjcmlwdGlvbj4KPHJkZjpEZXNjcmlw + dGlvbiByZGY6YWJvdXQ9IiIgIHhtbG5zOmRjPSJodHRwOi8vcHVybC5vcmcvZGMvZWxlbWVudHMv + MS4xLyI+CjxkYzpjcmVhdG9yPjxyZGY6U2VxPjxyZGY6bGk+S3Jpc3RhIFByYXRpY288L3JkZjps + aT48L3JkZjpTZXE+PC9kYzpjcmVhdG9yPjwvcmRmOkRlc2NyaXB0aW9uPgo8cmRmOkRlc2NyaXB0 + aW9uIHJkZjphYm91dD0iIiAgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAv + Ij4KPHhtcDpDcmVhdG9yVG9vbD5NaWNyb3NvZnTCriBXb3JkIGZvciBPZmZpY2UgMzY1PC94bXA6 + Q3JlYXRvclRvb2w+PHhtcDpDcmVhdGVEYXRlPjIwMjAtMDMtMjBUMTA6NDQ6NDYtMDc6MDA8L3ht + cDpDcmVhdGVEYXRlPjx4bXA6TW9kaWZ5RGF0ZT4yMDIwLTAzLTIwVDEwOjQ0OjQ2LTA3OjAwPC94 + bXA6TW9kaWZ5RGF0ZT48L3JkZjpEZXNjcmlwdGlvbj4KPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJv + dXQ9IiIgIHhtbG5zOnhtcE1NPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvbW0vIj4KPHht + cE1NOkRvY3VtZW50SUQ+dXVpZDo4RjI5Q0E4Qy1FRThCLTQ3NTktQkM5Qi1BMDhFRkVFNjYyMDE8 + L3htcE1NOkRvY3VtZW50SUQ+PHhtcE1NOkluc3RhbmNlSUQ+dXVpZDo4RjI5Q0E4Qy1FRThCLTQ3 + NTktQkM5Qi1BMDhFRkVFNjYyMDE8L3htcE1NOkluc3RhbmNlSUQ+PC9yZGY6RGVzY3JpcHRpb24+ + CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg + ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAg + ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg + ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAg + ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg + ICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg + ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg + ICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg + ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAg + ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg + ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAg + ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg + ICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg + ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg + ICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg + ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAg + ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg + ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAg + ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg + ICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAg + ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg + ICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg + ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg + ICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg + ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAg + ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg + ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAg + ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg + ICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg + ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg + ICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg + ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAg + ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg + ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAg + ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg + ICAgICAgICAgICAgICAgICAgICAgICAgIAo8L3JkZjpSREY+PC94OnhtcG1ldGE+PD94cGFja2V0 + IGVuZD0idyI/Pg0KZW5kc3RyZWFtDQplbmRvYmoNCjIxIDAgb2JqDQo8PC9EaXNwbGF5RG9jVGl0 + bGUgdHJ1ZT4+DQplbmRvYmoNCjIyIDAgb2JqDQo8PC9UeXBlL1hSZWYvU2l6ZSAyMi9XWyAxIDQg + Ml0gL1Jvb3QgMSAwIFIvSW5mbyA5IDAgUi9JRFs8OENDQTI5OEY4QkVFNTk0N0JDOUJBMDhFRkVF + NjYyMDE+PDhDQ0EyOThGOEJFRTU5NDdCQzlCQTA4RUZFRTY2MjAxPl0gL0ZpbHRlci9GbGF0ZURl + Y29kZS9MZW5ndGggODM+Pg0Kc3RyZWFtDQp4nC3LsQFAQAyF4ZfcHbW1KJUKnTHYxgR6k1Ba48R7 + UuQrkh+IqdVid8DHLm5iD/GDpInkXmwibs7cRRJZFGHi/2yiKyfzdhALGVcyX8ALyoALUA0KZW5k + c3RyZWFtDQplbmRvYmoNCnhyZWYNCjAgMjMNCjAwMDAwMDAwMTAgNjU1MzUgZg0KMDAwMDAwMDAx + NyAwMDAwMCBuDQowMDAwMDAwMTY2IDAwMDAwIG4NCjAwMDAwMDAyMjIgMDAwMDAgbg0KMDAwMDAw + MDQ4NiAwMDAwMCBuDQowMDAwMDAwNjkyIDAwMDAwIG4NCjAwMDAwMDA4NTkgMDAwMDAgbg0KMDAw + MDAwMTA5OCAwMDAwMCBuDQowMDAwMDAxMTUxIDAwMDAwIG4NCjAwMDAwMDEyMDQgMDAwMDAgbg0K + MDAwMDAwMDAxMSA2NTUzNSBmDQowMDAwMDAwMDEyIDY1NTM1IGYNCjAwMDAwMDAwMTMgNjU1MzUg + Zg0KMDAwMDAwMDAxNCA2NTUzNSBmDQowMDAwMDAwMDE1IDY1NTM1IGYNCjAwMDAwMDAwMTYgNjU1 + MzUgZg0KMDAwMDAwMDAxNyA2NTUzNSBmDQowMDAwMDAwMDAwIDY1NTM1IGYNCjAwMDAwMDE4Njcg + MDAwMDAgbg0KMDAwMDAwMTg5NCAwMDAwMCBuDQowMDAwMDIxMzc0IDAwMDAwIG4NCjAwMDAwMjQ1 + NDEgMDAwMDAgbg0KMDAwMDAyNDU4NiAwMDAwMCBuDQp0cmFpbGVyDQo8PC9TaXplIDIzL1Jvb3Qg + MSAwIFIvSW5mbyA5IDAgUi9JRFs8OENDQTI5OEY4QkVFNTk0N0JDOUJBMDhFRkVFNjYyMDE+PDhD + Q0EyOThGOEJFRTU5NDdCQzlCQTA4RUZFRTY2MjAxPl0gPj4NCnN0YXJ0eHJlZg0KMjQ4NjgNCiUl + RU9GDQp4cmVmDQowIDANCnRyYWlsZXINCjw8L1NpemUgMjMvUm9vdCAxIDAgUi9JbmZvIDkgMCBS + L0lEWzw4Q0NBMjk4RjhCRUU1OTQ3QkM5QkEwOEVGRUU2NjIwMT48OENDQTI5OEY4QkVFNTk0N0JD + OUJBMDhFRkVFNjYyMDE+XSAvUHJldiAyNDg2OC9YUmVmU3RtIDI0NTg2Pj4NCnN0YXJ0eHJlZg0K + MjU0ODQNCiUlRU9G + - 0 + - null + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + Content-Length: + - '25662' + Content-Type: + - application/pdf + User-Agent: + - azsdk-python-ai-formrecognizer/1.0.0b4 Python/3.7.3 (Windows-10-10.0.18362-SP0) + Python/3.7.3 (Windows-10-10.0.18362-SP0) + method: POST + uri: https://centraluseuap.api.cognitive.microsoft.com/formrecognizer/v2.0-preview/custom/models/5c86788f-f987-4d7f-885b-efde241d7a16/analyze?includeTextDetails=false + response: + body: + string: '' + headers: + apim-request-id: + - 3b868d44-886c-4abd-94ea-0910c91e965b + content-length: + - '0' + date: + - Mon, 15 Jun 2020 19:50:22 GMT + operation-location: + - https://centraluseuap.api.cognitive.microsoft.com/formrecognizer/v2.0-preview/custom/models/5c86788f-f987-4d7f-885b-efde241d7a16/analyzeresults/f195160b-42dc-490c-bd92-01a871c69d78 + strict-transport-security: + - max-age=31536000; includeSubDomains; preload + x-content-type-options: + - nosniff + x-envoy-upstream-service-time: + - '249' + status: + code: 202 + message: Accepted +- request: + body: null + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + User-Agent: + - azsdk-python-ai-formrecognizer/1.0.0b4 Python/3.7.3 (Windows-10-10.0.18362-SP0) + Python/3.7.3 (Windows-10-10.0.18362-SP0) + method: GET + uri: https://centraluseuap.api.cognitive.microsoft.com/formrecognizer/v2.0-preview/custom/models/5c86788f-f987-4d7f-885b-efde241d7a16/analyzeresults/f195160b-42dc-490c-bd92-01a871c69d78 + response: + body: + string: '{"status": "succeeded", "createdDateTime": "2020-06-15T19:50:22Z", + "lastUpdatedDateTime": "2020-06-15T19:50:27Z", "analyzeResult": {"version": + "2.0.0", "readResults": [{"page": 1, "angle": 0, "width": 8.5, "height": 11.0, + "unit": "inch", "lines": []}], "pageResults": [{"page": 1, "keyValuePairs": + [], "tables": [], "clusterId": null}], "documentResults": [], "errors": []}}' + headers: + apim-request-id: + - d126a110-37eb-4fbf-85af-6fc308f9b7c6 + content-length: + - '374' + content-type: + - application/json; charset=utf-8 + date: + - Mon, 15 Jun 2020 19:50:27 GMT + strict-transport-security: + - max-age=31536000; includeSubDomains; preload + x-content-type-options: + - nosniff + x-envoy-upstream-service-time: + - '50' + status: + code: 200 + message: OK +version: 1 diff --git a/sdk/formrecognizer/azure-ai-formrecognizer/tests/recordings/test_custom_forms_async.test_custom_form_labeled_blank_page.yaml b/sdk/formrecognizer/azure-ai-formrecognizer/tests/recordings/test_custom_forms_async.test_custom_form_labeled_blank_page.yaml new file mode 100644 index 000000000000..acedd12dc83f --- /dev/null +++ b/sdk/formrecognizer/azure-ai-formrecognizer/tests/recordings/test_custom_forms_async.test_custom_form_labeled_blank_page.yaml @@ -0,0 +1,577 @@ +interactions: +- request: + body: 'b''b\''{"source": "containersasurl", "sourceFilter": {"prefix": "", "includeSubFolders": + false}, "useLabelFile": true}\''''' + headers: + Content-Length: + - '287' + Content-Type: + - application/json + User-Agent: + - azsdk-python-ai-formrecognizer/1.0.0b4 Python/3.7.3 (Windows-10-10.0.18362-SP0) + Python/3.7.3 (Windows-10-10.0.18362-SP0) + method: POST + uri: https://centraluseuap.api.cognitive.microsoft.com/formrecognizer/v2.0-preview/custom/models + response: + body: + string: '' + headers: + apim-request-id: e6cd020f-a928-473e-90a9-7ab98aecad7b + content-length: '0' + date: Mon, 15 Jun 2020 20:12:14 GMT + location: https://centraluseuap.api.cognitive.microsoft.com/formrecognizer/v2.0-preview/custom/models/2d79c90f-083a-448f-8abb-57a4b3bbfb3b + strict-transport-security: max-age=31536000; includeSubDomains; preload + x-content-type-options: nosniff + x-envoy-upstream-service-time: '505' + status: + code: 201 + message: Created + url: https://centraluseuap.api.cognitive.microsoft.com//formrecognizer/v2.0-preview/custom/models +- request: + body: null + headers: + User-Agent: + - azsdk-python-ai-formrecognizer/1.0.0b4 Python/3.7.3 (Windows-10-10.0.18362-SP0) + Python/3.7.3 (Windows-10-10.0.18362-SP0) + method: GET + uri: https://centraluseuap.api.cognitive.microsoft.com/formrecognizer/v2.0-preview/custom/models/2d79c90f-083a-448f-8abb-57a4b3bbfb3b?includeKeys=true + response: + body: + string: '{"modelInfo": {"modelId": "2d79c90f-083a-448f-8abb-57a4b3bbfb3b", "status": + "ready", "createdDateTime": "2020-06-15T20:12:14Z", "lastUpdatedDateTime": + "2020-06-15T20:12:18Z"}, "trainResult": {"averageModelAccuracy": 0.973, "trainingDocuments": + [{"documentName": "Form_1.jpg", "pages": 1, "status": "succeeded"}, {"documentName": + "Form_2.jpg", "pages": 1, "status": "succeeded"}, {"documentName": "Form_3.jpg", + "pages": 1, "status": "succeeded"}, {"documentName": "Form_4.jpg", "pages": + 1, "status": "succeeded"}, {"documentName": "Form_5.jpg", "pages": 1, "status": + "succeeded"}], "fields": [{"fieldName": "CompanyAddress", "accuracy": 0.8}, + {"fieldName": "CompanyName", "accuracy": 1.0}, {"fieldName": "CompanyPhoneNumber", + "accuracy": 1.0}, {"fieldName": "DatedAs", "accuracy": 1.0}, {"fieldName": + "Email", "accuracy": 0.8}, {"fieldName": "Merchant", "accuracy": 1.0}, {"fieldName": + "PhoneNumber", "accuracy": 1.0}, {"fieldName": "PurchaseOrderNumber", "accuracy": + 1.0}, {"fieldName": "Quantity", "accuracy": 1.0}, {"fieldName": "Signature", + "accuracy": 1.0}, {"fieldName": "Subtotal", "accuracy": 1.0}, {"fieldName": + "Tax", "accuracy": 1.0}, {"fieldName": "Total", "accuracy": 1.0}, {"fieldName": + "VendorName", "accuracy": 1.0}, {"fieldName": "Website", "accuracy": 1.0}], + "errors": []}}' + headers: + apim-request-id: 20716af1-407e-45eb-b363-75c3f3fd0963 + content-type: application/json; charset=utf-8 + date: Mon, 15 Jun 2020 20:12:24 GMT + strict-transport-security: max-age=31536000; includeSubDomains; preload + transfer-encoding: chunked + x-content-type-options: nosniff + x-envoy-upstream-service-time: '5165' + status: + code: 200 + message: OK + url: https://centraluseuap.api.cognitive.microsoft.com/formrecognizer/v2.0-preview/custom/models/2d79c90f-083a-448f-8abb-57a4b3bbfb3b?includeKeys=true +- request: + body: !!binary | + JVBERi0xLjcNCiW1tbW1DQoxIDAgb2JqDQo8PC9UeXBlL0NhdGFsb2cvUGFnZXMgMiAwIFIvTGFu + Zyhlbi1VUykgL1N0cnVjdFRyZWVSb290IDEwIDAgUi9NYXJrSW5mbzw8L01hcmtlZCB0cnVlPj4v + TWV0YWRhdGEgMjAgMCBSL1ZpZXdlclByZWZlcmVuY2VzIDIxIDAgUj4+DQplbmRvYmoNCjIgMCBv + YmoNCjw8L1R5cGUvUGFnZXMvQ291bnQgMS9LaWRzWyAzIDAgUl0gPj4NCmVuZG9iag0KMyAwIG9i + ag0KPDwvVHlwZS9QYWdlL1BhcmVudCAyIDAgUi9SZXNvdXJjZXM8PC9Gb250PDwvRjEgNSAwIFI+ + Pi9FeHRHU3RhdGU8PC9HUzcgNyAwIFIvR1M4IDggMCBSPj4vUHJvY1NldFsvUERGL1RleHQvSW1h + Z2VCL0ltYWdlQy9JbWFnZUldID4+L01lZGlhQm94WyAwIDAgNjEyIDc5Ml0gL0NvbnRlbnRzIDQg + MCBSL0dyb3VwPDwvVHlwZS9Hcm91cC9TL1RyYW5zcGFyZW5jeS9DUy9EZXZpY2VSR0I+Pi9UYWJz + L1MvU3RydWN0UGFyZW50cyAwPj4NCmVuZG9iag0KNCAwIG9iag0KPDwvRmlsdGVyL0ZsYXRlRGVj + b2RlL0xlbmd0aCAxMzI+Pg0Kc3RyZWFtDQp4nC2MsQrCQBBE+4X9hynV4m73iJ4HIUUuMSgEFA8s + xFJTKaj/D67iFAPDPB78HnXtx7ztIE2Dtst4MomTb5IGCFbWMQW8rkynBR5MbWHyG4WqkwrlxqTG + CRQxOAkVoiS3tOdu3HCMmN7mxPRb6/8amM4zzC8oO6bejAcm9GPGB3fjHKoNCmVuZHN0cmVhbQ0K + ZW5kb2JqDQo1IDAgb2JqDQo8PC9UeXBlL0ZvbnQvU3VidHlwZS9UcnVlVHlwZS9OYW1lL0YxL0Jh + c2VGb250L0JDREVFRStDYWxpYnJpL0VuY29kaW5nL1dpbkFuc2lFbmNvZGluZy9Gb250RGVzY3Jp + cHRvciA2IDAgUi9GaXJzdENoYXIgMzIvTGFzdENoYXIgMzIvV2lkdGhzIDE4IDAgUj4+DQplbmRv + YmoNCjYgMCBvYmoNCjw8L1R5cGUvRm9udERlc2NyaXB0b3IvRm9udE5hbWUvQkNERUVFK0NhbGli + cmkvRmxhZ3MgMzIvSXRhbGljQW5nbGUgMC9Bc2NlbnQgNzUwL0Rlc2NlbnQgLTI1MC9DYXBIZWln + aHQgNzUwL0F2Z1dpZHRoIDUyMS9NYXhXaWR0aCAxNzQzL0ZvbnRXZWlnaHQgNDAwL1hIZWlnaHQg + MjUwL1N0ZW1WIDUyL0ZvbnRCQm94WyAtNTAzIC0yNTAgMTI0MCA3NTBdIC9Gb250RmlsZTIgMTkg + MCBSPj4NCmVuZG9iag0KNyAwIG9iag0KPDwvVHlwZS9FeHRHU3RhdGUvQk0vTm9ybWFsL2NhIDE+ + Pg0KZW5kb2JqDQo4IDAgb2JqDQo8PC9UeXBlL0V4dEdTdGF0ZS9CTS9Ob3JtYWwvQ0EgMT4+DQpl + bmRvYmoNCjkgMCBvYmoNCjw8L0F1dGhvcihLcmlzdGEgUHJhdGljbykgL0NyZWF0b3Io/v8ATQBp + AGMAcgBvAHMAbwBmAHQArgAgAFcAbwByAGQAIABmAG8AcgAgAE8AZgBmAGkAYwBlACAAMwA2ADUp + IC9DcmVhdGlvbkRhdGUoRDoyMDIwMDMyMDEwNDQ0Ni0wNycwMCcpIC9Nb2REYXRlKEQ6MjAyMDAz + MjAxMDQ0NDYtMDcnMDAnKSAvUHJvZHVjZXIo/v8ATQBpAGMAcgBvAHMAbwBmAHQArgAgAFcAbwBy + AGQAIABmAG8AcgAgAE8AZgBmAGkAYwBlACAAMwA2ADUpID4+DQplbmRvYmoNCjE3IDAgb2JqDQo8 + PC9UeXBlL09ialN0bS9OIDcvRmlyc3QgNDYvRmlsdGVyL0ZsYXRlRGVjb2RlL0xlbmd0aCAyOTY+ + Pg0Kc3RyZWFtDQp4nG1R0WrCMBR9F/yH+we3sa1jIMKYyoZYSivsofgQ610NtomkKejfL3ftsANf + wjk355ycJCKGAEQEsQDhQRCD8Oh1DmIGUTgDEUIU++EcopcAFgtMWR1AhjmmuL9fCXNnu9Kta2pw + W0BwAEwrCFmzXE4nvSUYLCtTdg1p98wpuEp2gME1UuwtUWaMw8zUtJNX7sh5qbQ+i3e5Lk84Jupj + RrsJ3dyW7iCG6I3P0sYRJrys9elB9l56NDfMqXT4QfJEtsfs+cOfulaa8rPkhjx40z5BOmX0wK1T + 39KDX/Zl7OVozOVxe560ZyLHJR3uZGnNiL+f/TriKyVrU40Gea1ONNL253hZZWWDG1V1loa7Jl3T + FvzH83+vm8iG2qKnj6efTn4AVAqiuw0KZW5kc3RyZWFtDQplbmRvYmoNCjE4IDAgb2JqDQpbIDIy + Nl0gDQplbmRvYmoNCjE5IDAgb2JqDQo8PC9GaWx0ZXIvRmxhdGVEZWNvZGUvTGVuZ3RoIDE5Mzg5 + L0xlbmd0aDEgODE3NDA+Pg0Kc3RyZWFtDQp4nOx9B3xUVdr+OfdOy8wkM5NkkkkmYWaYJASGFCCB + BJAMpNA7gwk1IYWAAQKEIgJGUdAo9l7Rta1YJgNqwO5iWQv2vhZ2XVdXse3qKgL5nnPfORDY1f+3 + 1fX7z5s88zznPeWe+t6TH8kPxhljdnzoWG3lqIoZBf1stzPumcAYf6Jy1ITyq5qr4hnPzGBMKZw8 + vWDgtY/W3YO8s1Crtn5JXetF716EsiddgvwP6le3eXe1vlHM2LYLGNM/0NS6cMnGd9UhjC1dy1h8 + YGHLyU2vVu4oYuwW1LF90NxY1/DtxJPDaM+K9gY3wxF/Z8Z+pCuQzmpe0rZ2xDjjAaQ/YmzRHS3L + 6uvyGvrezNi9hSg+c0nd2tZ8c/abyG9Gee+Sxra6q07ftppxXzLSZyytW9J43YGv5zP2KfpbuLJ1 + 2cq2bjfbzHjGQVG+dUVja9LC3mmMnXITHvcJE3NhGLpv9uI1H8+3Df+apZmYsPs/Wf+s4NfHrpn8 + /YFD7XGfmgYjGccURoZ6BnaY8T3mbd8fOLAt7lOtpR6WdofwuPuxdmZnw6EVcAHbwljiYO25nKm6 + AL+A6ZlJf6V+EJrsRay+wDYrzMQUm15RFJ2q6D5g+d2PsKxTtB7AJk73elmQsexnqQ/G65QcL+Pd + Ik+9T58gRsqSdQlHe8OfZ//fm+F1dsdP3Yf/K6ZrZDf81H34e8xg+Pf0V93/85qHf4fpiljtT92H + mP3zpjzNrvyp+/BzMOX3bMw/Uo9/w1r+1X2JWcxiFrOY/eOmXM3NP5hXy/b/J/vyczG1mJ3zU/ch + ZjGLWcxi9o+b7lHW9B9/5hJ23n/6mTGLWcxiFrOYxSxmMYtZzGIWs/+7Fvs5M2Yxi1nMYhazmMUs + ZjGLWcxiFrOYxey/23jst9FjFrOYxSxmMYtZzGIWs5jFLGYxi1nMYhazmMUsZjGLWcxiFrOYxSxm + MYtZzGIWs5jFLGYxi1nMYhazmMUsZjGLWcz+S6x790/dg5jF7Cc2NYqM6P8k1YEUlLKa6dhSpFOY + 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 + +xG3mmhL96QrfW1pvHxyGl+Wdmra+WmqzfW8Swm6+vavsqU+n/pe6uepuqRgat/8KpZiT/GmqE4x + tpSJM6o0LqsgHlCsjdWT4s+psjm5zelxKpWfO/lmpnIv54zbQaoJZXZyp6dKfZCLX6LTM84vYDMC + 47tMbNr4sGnK7DA/K5w9XXwGp84KG84Ks9Cs2dWdnJ9Xo/1OQjhZ/FKJlj5z61aWOWp8OHN6dUTd + ti1zVM34cLvQwaCmu4VmKFITmLdy1cpAdfAE5njf8YVDdT5sf96u2GzcZuu2KUEbOm9L8CQo4qM7 + QQ0mDBhSZYv3xCviozteTQnGwyPG18c6ZUaVzeKxKKEyy2SLErSUlVcFLXmFVX81zh1inPTkQNs8 + fMxb2RbQvpGq4atEMiC84ntlG9Lia5WWZoEfNSoGmr8S1iadbT9e67/d+E/dgZ+/0W/yjOxWzmAN + yibgdOA0oB04FdgIbADWA6cA64CTgbXAGmA1sApoA1YCy4FWYBmwFFgCtAAnAYuBRUAzsBBoAhqB + BqAeWADUAbXAfGAeMBeYA8wGZgE1QDVwIjATCAEzgOnANGAqMAWYDEwCJgITgPHAOGAsMAYYDVQB + lUAFUA6MAkYCQaAMGAGcAAwHhgFDgVKgBBgCDAaKgSJgEDAQGAAUAgVAPpAH9AcCQD+gL5AL9AFy + gGwgC/ADvQEf4AU8QC8gE8gA3EA6kAa4gFQgBXACyUASkAg4ADtgAxKAeMAKWAAzEAeYACNgAPSA + bmQ3PlVAATjAWAOHjx8GDgEHge+BA8B3wLfAX4BvgK+BPwN/Ar4CvgS+AD4HPgP2A58CnwB/BD4G + PgL+AHwI/B74APgd8FtgH/A+8B7wLvAO8BvgbeAt4E3gDeB14DXgVeAV4GXgJeBF4AXgeWAv8Bzw + LPAM8DTwa+Ap4EngCeBxYA/wK+Ax4FHgEeBh4CHgQeAB4H5gN7AL6ALuA+4F7gF2AjuACNAJhIG7 + gbuAO4E7gO3A7cAvgduAW4FbgJuBm4BfADcCNwDbgOuB64BrgWuAq4GrgCuBK4DLgcuAS4FLgIuB + i4ALgQuA84HzgK3AucA5QAdwNnAWsAXYDJzJGka2c5x/jvPPcf45zj/H+ec4/xznn+P8c5x/jvPP + cf45zj/H+ec4/xznn+P8c5x/jvPPVwCIARwxgCMGcMQAjhjAEQM4YgBHDOCIARwxgCMGcMQAjhjA + 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 + uHNOntVntltoN7fZZcns+b3njj5RYD9zj2wYh93jRY32F+XYd7t63rI9HL47es2yJ/Lzkt3tQlaU + DZGNmzl6SlRiyln72V+ccO/r8eSz3lNExze9zb7aw4rjv/BG9zYrTm3xzCvteuW29FBk9/xRPk9c + Rq+cqR877Y8jHRlRnfZOdt6+fI2F/cK6rFdZnDnlnXp2yh4glHjZyFUD5829NvaP6hlPojpffBN+ + fGfIA1WHDdc3+tYdv/DEceN8v7J5cY7d7R5bTzrjVcNzu/Jwf8DPgxZ/K+xq+6Pt3h/Xb7i++bfz + riX9BAPCeCN93b+s/MP3xUV2N0I+b3PapCyVetVWfU0vC8uVeCe/HsU9XeIy7Kqr4p5envGlh9p1 + tGBV7vVenWTLaoaI0ifUeEq6lE2oPffkxeOWFQt8L/+0ouz4/SES/tVBVl9P7GGZZ/mLZWWOV4td + YvGWh7//2Ia1q5Z/wNHv/kVZ4L2yZxWD557FzlRE7Ux7UrbCpm+M8/xCr+OY//7KhSt6CvLahvx4 + YunSJaNGtXsVM8dr7eton8Kni1/szt7at+zK3Zx893t3QucXuPV9f6bKJyvnxsZXb6bctSu8Iw/f + +IasY8VOr63NUUpmdf9lUWp8wu7C/u0q8pvxvEc94NtW9ny9+ujyIXsqShb2H5kaHyOo7n1oYe4g + 28KY7LcFS/bsVCpHHBLpXBxGJf7ELWZtIotZ6xg4ThbN+acLV9O/Dqy/OVJetA8mHzqIbZhce9M7 + L0CK+p4d15E0nXUlfeoXsrggtb0tjVw1/fGjM0XNa/13KmeO++6u+0VSarLEnptKJpd3KvRr8qu7 + yR8+TaWiY2H7j+7sZONfERGNajOrGMeSoqevHPvdYvVAX8vz3KGiwO1V/ax6ch09R23Ii04evDs0 + 2CnU+WRSRvsUy3OiWa635i1oKdcOYm+ousbxd+7gGGX7Wj5xdrTix9nSvuf3TmbVZj3gTvj14reH + 18+qm7ay35fq/DU4a8fbHVu/P3i77u3+idi5m9sXSZedCD+gODDs9e3XP7geLwtT1HW2fPwgemKz + /OOe7/uH/3RlQNvUWwdKrJvvXamY//X119X+sheffcZcF/NtO/4o71U7brQ4OjPi9aA2dQm5bvxv + 3q6JcZocnrJtxN4dK3kXJM67ugyYbsHp6TFzyNJpN2+5T7pVOu+ngmc97npkFzuOwA/vSO2YtdzB + q7Zj8tm+7EHekyuKGX7geNK+3keW3GKGKxhqhkJz+j92Id70nTaTmBxCupmGpF39HUMcMDfOWHCd + 0C+Ou3BDeFz4GvhBREbcHhe+ONHvwN2OU11Vp6qzPBd+V9DokgnGCjfe5UvGpP5Mj7Q+Zfq7tmP7 + +Ae5+x8Y8uTc1cf3v1hbutDnFi+z+V37K+dOT4vvMKLjstoFhUPnB5zoMlTWYs1vVzeOaam8w291 + XH/hvfqBTUXvxY/7jPyyk2jgYq/7jKoAYWmk96n7L+2sxHdTCsZYF4wp07gMK5cN8rPwyjiw+WDG + olP3xRf5udFb3148d/1t8bvrkrRjP1zdXOYg33di5JxHT3Mjv7+0r+CXdz8v32a3hGuRdD122/bv + vVKGVDwZd3v2xWk7NtkV3XVZ1KPLiOyvjw7h/3J7+enzy6punTtvP9plwNne7FOq7b/6h4+729uh + eqxVv8vdnqxNi908ORd/sHGv/+OcFZO5XX+fFon9H5Hg2m4NCmVuZHN0cmVhbQ0KZW5kb2JqDQoy + MCAwIG9iag0KPDwvVHlwZS9NZXRhZGF0YS9TdWJ0eXBlL1hNTC9MZW5ndGggMzA4ND4+DQpzdHJl + YW0NCjw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+ + PHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iMy4xLTcwMSI+Cjxy + ZGY6UkRGIHhtbG5zOnJkZj0iaHR0cDovL3d3dy53My5vcmcvMTk5OS8wMi8yMi1yZGYtc3ludGF4 + LW5zIyI+CjxyZGY6RGVzY3JpcHRpb24gcmRmOmFib3V0PSIiICB4bWxuczpwZGY9Imh0dHA6Ly9u + cy5hZG9iZS5jb20vcGRmLzEuMy8iPgo8cGRmOlByb2R1Y2VyPk1pY3Jvc29mdMKuIFdvcmQgZm9y + IE9mZmljZSAzNjU8L3BkZjpQcm9kdWNlcj48L3JkZjpEZXNjcmlwdGlvbj4KPHJkZjpEZXNjcmlw + dGlvbiByZGY6YWJvdXQ9IiIgIHhtbG5zOmRjPSJodHRwOi8vcHVybC5vcmcvZGMvZWxlbWVudHMv + MS4xLyI+CjxkYzpjcmVhdG9yPjxyZGY6U2VxPjxyZGY6bGk+S3Jpc3RhIFByYXRpY288L3JkZjps + aT48L3JkZjpTZXE+PC9kYzpjcmVhdG9yPjwvcmRmOkRlc2NyaXB0aW9uPgo8cmRmOkRlc2NyaXB0 + aW9uIHJkZjphYm91dD0iIiAgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAv + Ij4KPHhtcDpDcmVhdG9yVG9vbD5NaWNyb3NvZnTCriBXb3JkIGZvciBPZmZpY2UgMzY1PC94bXA6 + Q3JlYXRvclRvb2w+PHhtcDpDcmVhdGVEYXRlPjIwMjAtMDMtMjBUMTA6NDQ6NDYtMDc6MDA8L3ht + cDpDcmVhdGVEYXRlPjx4bXA6TW9kaWZ5RGF0ZT4yMDIwLTAzLTIwVDEwOjQ0OjQ2LTA3OjAwPC94 + bXA6TW9kaWZ5RGF0ZT48L3JkZjpEZXNjcmlwdGlvbj4KPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJv + dXQ9IiIgIHhtbG5zOnhtcE1NPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvbW0vIj4KPHht + cE1NOkRvY3VtZW50SUQ+dXVpZDo4RjI5Q0E4Qy1FRThCLTQ3NTktQkM5Qi1BMDhFRkVFNjYyMDE8 + L3htcE1NOkRvY3VtZW50SUQ+PHhtcE1NOkluc3RhbmNlSUQ+dXVpZDo4RjI5Q0E4Qy1FRThCLTQ3 + NTktQkM5Qi1BMDhFRkVFNjYyMDE8L3htcE1NOkluc3RhbmNlSUQ+PC9yZGY6RGVzY3JpcHRpb24+ + CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg + ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAg + ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg + ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAg + ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg + ICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg + ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg + ICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg + ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAg + ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg + ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAg + ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg + ICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg + ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg + ICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg + ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAg + ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg + ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAg + ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg + ICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAg + ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg + ICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg + ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg + ICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg + ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAg + ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg + ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAg + ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg + ICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg + ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg + ICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg + ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAg + ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg + ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAg + ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg + ICAgICAgICAgICAgICAgICAgICAgICAgIAo8L3JkZjpSREY+PC94OnhtcG1ldGE+PD94cGFja2V0 + IGVuZD0idyI/Pg0KZW5kc3RyZWFtDQplbmRvYmoNCjIxIDAgb2JqDQo8PC9EaXNwbGF5RG9jVGl0 + bGUgdHJ1ZT4+DQplbmRvYmoNCjIyIDAgb2JqDQo8PC9UeXBlL1hSZWYvU2l6ZSAyMi9XWyAxIDQg + Ml0gL1Jvb3QgMSAwIFIvSW5mbyA5IDAgUi9JRFs8OENDQTI5OEY4QkVFNTk0N0JDOUJBMDhFRkVF + NjYyMDE+PDhDQ0EyOThGOEJFRTU5NDdCQzlCQTA4RUZFRTY2MjAxPl0gL0ZpbHRlci9GbGF0ZURl + Y29kZS9MZW5ndGggODM+Pg0Kc3RyZWFtDQp4nC3LsQFAQAyF4ZfcHbW1KJUKnTHYxgR6k1Ba48R7 + UuQrkh+IqdVid8DHLm5iD/GDpInkXmwibs7cRRJZFGHi/2yiKyfzdhALGVcyX8ALyoALUA0KZW5k + c3RyZWFtDQplbmRvYmoNCnhyZWYNCjAgMjMNCjAwMDAwMDAwMTAgNjU1MzUgZg0KMDAwMDAwMDAx + NyAwMDAwMCBuDQowMDAwMDAwMTY2IDAwMDAwIG4NCjAwMDAwMDAyMjIgMDAwMDAgbg0KMDAwMDAw + MDQ4NiAwMDAwMCBuDQowMDAwMDAwNjkyIDAwMDAwIG4NCjAwMDAwMDA4NTkgMDAwMDAgbg0KMDAw + MDAwMTA5OCAwMDAwMCBuDQowMDAwMDAxMTUxIDAwMDAwIG4NCjAwMDAwMDEyMDQgMDAwMDAgbg0K + MDAwMDAwMDAxMSA2NTUzNSBmDQowMDAwMDAwMDEyIDY1NTM1IGYNCjAwMDAwMDAwMTMgNjU1MzUg + Zg0KMDAwMDAwMDAxNCA2NTUzNSBmDQowMDAwMDAwMDE1IDY1NTM1IGYNCjAwMDAwMDAwMTYgNjU1 + MzUgZg0KMDAwMDAwMDAxNyA2NTUzNSBmDQowMDAwMDAwMDAwIDY1NTM1IGYNCjAwMDAwMDE4Njcg + MDAwMDAgbg0KMDAwMDAwMTg5NCAwMDAwMCBuDQowMDAwMDIxMzc0IDAwMDAwIG4NCjAwMDAwMjQ1 + NDEgMDAwMDAgbg0KMDAwMDAyNDU4NiAwMDAwMCBuDQp0cmFpbGVyDQo8PC9TaXplIDIzL1Jvb3Qg + MSAwIFIvSW5mbyA5IDAgUi9JRFs8OENDQTI5OEY4QkVFNTk0N0JDOUJBMDhFRkVFNjYyMDE+PDhD + Q0EyOThGOEJFRTU5NDdCQzlCQTA4RUZFRTY2MjAxPl0gPj4NCnN0YXJ0eHJlZg0KMjQ4NjgNCiUl + RU9GDQp4cmVmDQowIDANCnRyYWlsZXINCjw8L1NpemUgMjMvUm9vdCAxIDAgUi9JbmZvIDkgMCBS + L0lEWzw4Q0NBMjk4RjhCRUU1OTQ3QkM5QkEwOEVGRUU2NjIwMT48OENDQTI5OEY4QkVFNTk0N0JD + OUJBMDhFRkVFNjYyMDE+XSAvUHJldiAyNDg2OC9YUmVmU3RtIDI0NTg2Pj4NCnN0YXJ0eHJlZg0K + MjU0ODQNCiUlRU9G + headers: + Content-Type: + - application/pdf + User-Agent: + - azsdk-python-ai-formrecognizer/1.0.0b4 Python/3.7.3 (Windows-10-10.0.18362-SP0) + Python/3.7.3 (Windows-10-10.0.18362-SP0) + method: POST + uri: https://centraluseuap.api.cognitive.microsoft.com/formrecognizer/v2.0-preview/custom/models/2d79c90f-083a-448f-8abb-57a4b3bbfb3b/analyze?includeTextDetails=false + response: + body: + string: '' + headers: + apim-request-id: 60476c1e-b7d5-4cf2-90aa-3fff2789c7d1 + content-length: '0' + date: Mon, 15 Jun 2020 20:12:25 GMT + operation-location: https://centraluseuap.api.cognitive.microsoft.com/formrecognizer/v2.0-preview/custom/models/2d79c90f-083a-448f-8abb-57a4b3bbfb3b/analyzeresults/68751e3b-2624-413e-b0f8-b609a99abcc7 + strict-transport-security: max-age=31536000; includeSubDomains; preload + x-content-type-options: nosniff + x-envoy-upstream-service-time: '369' + status: + code: 202 + message: Accepted + url: https://centraluseuap.api.cognitive.microsoft.com//formrecognizer/v2.0-preview/custom/models/2d79c90f-083a-448f-8abb-57a4b3bbfb3b/analyze?includeTextDetails=false +- request: + body: null + headers: + User-Agent: + - azsdk-python-ai-formrecognizer/1.0.0b4 Python/3.7.3 (Windows-10-10.0.18362-SP0) + Python/3.7.3 (Windows-10-10.0.18362-SP0) + method: GET + uri: https://centraluseuap.api.cognitive.microsoft.com/formrecognizer/v2.0-preview/custom/models/2d79c90f-083a-448f-8abb-57a4b3bbfb3b/analyzeresults/68751e3b-2624-413e-b0f8-b609a99abcc7 + response: + body: + string: '{"status": "succeeded", "createdDateTime": "2020-06-15T20:12:25Z", + "lastUpdatedDateTime": "2020-06-15T20:12:30Z", "analyzeResult": {"version": + "2.0.0", "readResults": [{"page": 1, "language": "en", "angle": 0, "width": + 8.4967, "height": 10.9967, "unit": "inch"}], "pageResults": [{"page": 1, "tables": + []}], "documentResults": [{"docType": "custom:form", "pageRange": [1, 1], + "fields": {"CompanyAddress": null, "CompanyName": null, "CompanyPhoneNumber": + null, "DatedAs": null, "Email": null, "Merchant": null, "PhoneNumber": null, + "PurchaseOrderNumber": null, "Quantity": null, "Signature": null, "Subtotal": + null, "Tax": null, "Total": null, "VendorName": null, "Website": null}}], + "errors": []}}' + headers: + apim-request-id: cf966d38-a06a-4369-a009-1accaa745507 + content-length: '632' + content-type: application/json; charset=utf-8 + date: Mon, 15 Jun 2020 20:12:30 GMT + strict-transport-security: max-age=31536000; includeSubDomains; preload + x-content-type-options: nosniff + x-envoy-upstream-service-time: '153' + status: + code: 200 + message: OK + url: https://centraluseuap.api.cognitive.microsoft.com/formrecognizer/v2.0-preview/custom/models/2d79c90f-083a-448f-8abb-57a4b3bbfb3b/analyzeresults/68751e3b-2624-413e-b0f8-b609a99abcc7 +version: 1 diff --git a/sdk/formrecognizer/azure-ai-formrecognizer/tests/recordings/test_custom_forms_async.test_custom_form_unlabeled_blank_page.yaml b/sdk/formrecognizer/azure-ai-formrecognizer/tests/recordings/test_custom_forms_async.test_custom_form_unlabeled_blank_page.yaml new file mode 100644 index 000000000000..eead0177c439 --- /dev/null +++ b/sdk/formrecognizer/azure-ai-formrecognizer/tests/recordings/test_custom_forms_async.test_custom_form_unlabeled_blank_page.yaml @@ -0,0 +1,618 @@ +interactions: +- request: + body: 'b''b\''{"source": "containersasurl", "sourceFilter": {"prefix": "", "includeSubFolders": + false}, "useLabelFile": false}\''''' + headers: + Content-Length: + - '288' + Content-Type: + - application/json + User-Agent: + - azsdk-python-ai-formrecognizer/1.0.0b4 Python/3.7.3 (Windows-10-10.0.18362-SP0) + Python/3.7.3 (Windows-10-10.0.18362-SP0) + method: POST + uri: https://centraluseuap.api.cognitive.microsoft.com/formrecognizer/v2.0-preview/custom/models + response: + body: + string: '' + headers: + apim-request-id: 9a38ee43-11b3-45fa-aa49-3aa02d5ea68f + content-length: '0' + date: Mon, 15 Jun 2020 20:08:11 GMT + location: https://centraluseuap.api.cognitive.microsoft.com/formrecognizer/v2.0-preview/custom/models/453f8f6c-6c05-49e4-aca5-021ca0b2d87f + strict-transport-security: max-age=31536000; includeSubDomains; preload + x-content-type-options: nosniff + x-envoy-upstream-service-time: '329' + status: + code: 201 + message: Created + url: https://centraluseuap.api.cognitive.microsoft.com//formrecognizer/v2.0-preview/custom/models +- request: + body: null + headers: + User-Agent: + - azsdk-python-ai-formrecognizer/1.0.0b4 Python/3.7.3 (Windows-10-10.0.18362-SP0) + Python/3.7.3 (Windows-10-10.0.18362-SP0) + method: GET + uri: https://centraluseuap.api.cognitive.microsoft.com/formrecognizer/v2.0-preview/custom/models/453f8f6c-6c05-49e4-aca5-021ca0b2d87f?includeKeys=true + response: + body: + string: '{"modelInfo": {"modelId": "453f8f6c-6c05-49e4-aca5-021ca0b2d87f", "status": + "creating", "createdDateTime": "2020-06-15T20:08:11Z", "lastUpdatedDateTime": + "2020-06-15T20:08:11Z"}}' + headers: + apim-request-id: a1148aad-27a3-4747-bcc8-2d1b0663032e + content-type: application/json; charset=utf-8 + date: Mon, 15 Jun 2020 20:08:16 GMT + strict-transport-security: max-age=31536000; includeSubDomains; preload + transfer-encoding: chunked + x-content-type-options: nosniff + x-envoy-upstream-service-time: '169' + status: + code: 200 + message: OK + url: https://centraluseuap.api.cognitive.microsoft.com/formrecognizer/v2.0-preview/custom/models/453f8f6c-6c05-49e4-aca5-021ca0b2d87f?includeKeys=true +- request: + body: null + headers: + User-Agent: + - azsdk-python-ai-formrecognizer/1.0.0b4 Python/3.7.3 (Windows-10-10.0.18362-SP0) + Python/3.7.3 (Windows-10-10.0.18362-SP0) + method: GET + uri: https://centraluseuap.api.cognitive.microsoft.com/formrecognizer/v2.0-preview/custom/models/453f8f6c-6c05-49e4-aca5-021ca0b2d87f?includeKeys=true + response: + body: + string: '{"modelInfo": {"modelId": "453f8f6c-6c05-49e4-aca5-021ca0b2d87f", "status": + "creating", "createdDateTime": "2020-06-15T20:08:11Z", "lastUpdatedDateTime": + "2020-06-15T20:08:11Z"}}' + headers: + apim-request-id: 32f7810d-65a3-424c-906a-5096f737ea38 + content-type: application/json; charset=utf-8 + date: Mon, 15 Jun 2020 20:08:22 GMT + strict-transport-security: max-age=31536000; includeSubDomains; preload + transfer-encoding: chunked + x-content-type-options: nosniff + x-envoy-upstream-service-time: '204' + status: + code: 200 + message: OK + url: https://centraluseuap.api.cognitive.microsoft.com/formrecognizer/v2.0-preview/custom/models/453f8f6c-6c05-49e4-aca5-021ca0b2d87f?includeKeys=true +- request: + body: null + headers: + User-Agent: + - azsdk-python-ai-formrecognizer/1.0.0b4 Python/3.7.3 (Windows-10-10.0.18362-SP0) + Python/3.7.3 (Windows-10-10.0.18362-SP0) + method: GET + uri: https://centraluseuap.api.cognitive.microsoft.com/formrecognizer/v2.0-preview/custom/models/453f8f6c-6c05-49e4-aca5-021ca0b2d87f?includeKeys=true + response: + body: + string: '{"modelInfo": {"modelId": "453f8f6c-6c05-49e4-aca5-021ca0b2d87f", "status": + "ready", "createdDateTime": "2020-06-15T20:08:11Z", "lastUpdatedDateTime": + "2020-06-15T20:08:24Z"}, "keys": {"clusters": {"0": ["Additional Notes:", + "Address:", "Company Name:", "Company Phone:", "Dated As:", "Details", "Email:", + "Hero Limited", "Name:", "Phone:", "Purchase Order", "Purchase Order #:", + "Quantity", "SUBTOTAL", "Seattle, WA 93849 Phone:", "Shipped From", "Shipped + To", "TAX", "TOTAL", "Total", "Unit Price", "Vendor Name:", "Website:"]}}, + "trainResult": {"trainingDocuments": [{"documentName": "Form_1.jpg", "pages": + 1, "errors": [], "status": "succeeded"}, {"documentName": "Form_2.jpg", "pages": + 1, "errors": [], "status": "succeeded"}, {"documentName": "Form_3.jpg", "pages": + 1, "errors": [], "status": "succeeded"}, {"documentName": "Form_4.jpg", "pages": + 1, "errors": [], "status": "succeeded"}, {"documentName": "Form_5.jpg", "pages": + 1, "errors": [], "status": "succeeded"}], "errors": []}}' + headers: + apim-request-id: d598b55b-90cd-4e19-a40f-4792210c2d59 + content-type: application/json; charset=utf-8 + date: Mon, 15 Jun 2020 20:08:27 GMT + strict-transport-security: max-age=31536000; includeSubDomains; preload + transfer-encoding: chunked + x-content-type-options: nosniff + x-envoy-upstream-service-time: '228' + status: + code: 200 + message: OK + url: https://centraluseuap.api.cognitive.microsoft.com/formrecognizer/v2.0-preview/custom/models/453f8f6c-6c05-49e4-aca5-021ca0b2d87f?includeKeys=true +- request: + body: !!binary | + JVBERi0xLjcNCiW1tbW1DQoxIDAgb2JqDQo8PC9UeXBlL0NhdGFsb2cvUGFnZXMgMiAwIFIvTGFu + Zyhlbi1VUykgL1N0cnVjdFRyZWVSb290IDEwIDAgUi9NYXJrSW5mbzw8L01hcmtlZCB0cnVlPj4v + TWV0YWRhdGEgMjAgMCBSL1ZpZXdlclByZWZlcmVuY2VzIDIxIDAgUj4+DQplbmRvYmoNCjIgMCBv + YmoNCjw8L1R5cGUvUGFnZXMvQ291bnQgMS9LaWRzWyAzIDAgUl0gPj4NCmVuZG9iag0KMyAwIG9i + ag0KPDwvVHlwZS9QYWdlL1BhcmVudCAyIDAgUi9SZXNvdXJjZXM8PC9Gb250PDwvRjEgNSAwIFI+ + Pi9FeHRHU3RhdGU8PC9HUzcgNyAwIFIvR1M4IDggMCBSPj4vUHJvY1NldFsvUERGL1RleHQvSW1h + Z2VCL0ltYWdlQy9JbWFnZUldID4+L01lZGlhQm94WyAwIDAgNjEyIDc5Ml0gL0NvbnRlbnRzIDQg + MCBSL0dyb3VwPDwvVHlwZS9Hcm91cC9TL1RyYW5zcGFyZW5jeS9DUy9EZXZpY2VSR0I+Pi9UYWJz + L1MvU3RydWN0UGFyZW50cyAwPj4NCmVuZG9iag0KNCAwIG9iag0KPDwvRmlsdGVyL0ZsYXRlRGVj + b2RlL0xlbmd0aCAxMzI+Pg0Kc3RyZWFtDQp4nC2MsQrCQBBE+4X9hynV4m73iJ4HIUUuMSgEFA8s + xFJTKaj/D67iFAPDPB78HnXtx7ztIE2Dtst4MomTb5IGCFbWMQW8rkynBR5MbWHyG4WqkwrlxqTG + CRQxOAkVoiS3tOdu3HCMmN7mxPRb6/8amM4zzC8oO6bejAcm9GPGB3fjHKoNCmVuZHN0cmVhbQ0K + ZW5kb2JqDQo1IDAgb2JqDQo8PC9UeXBlL0ZvbnQvU3VidHlwZS9UcnVlVHlwZS9OYW1lL0YxL0Jh + c2VGb250L0JDREVFRStDYWxpYnJpL0VuY29kaW5nL1dpbkFuc2lFbmNvZGluZy9Gb250RGVzY3Jp + cHRvciA2IDAgUi9GaXJzdENoYXIgMzIvTGFzdENoYXIgMzIvV2lkdGhzIDE4IDAgUj4+DQplbmRv + YmoNCjYgMCBvYmoNCjw8L1R5cGUvRm9udERlc2NyaXB0b3IvRm9udE5hbWUvQkNERUVFK0NhbGli + cmkvRmxhZ3MgMzIvSXRhbGljQW5nbGUgMC9Bc2NlbnQgNzUwL0Rlc2NlbnQgLTI1MC9DYXBIZWln + aHQgNzUwL0F2Z1dpZHRoIDUyMS9NYXhXaWR0aCAxNzQzL0ZvbnRXZWlnaHQgNDAwL1hIZWlnaHQg + MjUwL1N0ZW1WIDUyL0ZvbnRCQm94WyAtNTAzIC0yNTAgMTI0MCA3NTBdIC9Gb250RmlsZTIgMTkg + MCBSPj4NCmVuZG9iag0KNyAwIG9iag0KPDwvVHlwZS9FeHRHU3RhdGUvQk0vTm9ybWFsL2NhIDE+ + Pg0KZW5kb2JqDQo4IDAgb2JqDQo8PC9UeXBlL0V4dEdTdGF0ZS9CTS9Ob3JtYWwvQ0EgMT4+DQpl + bmRvYmoNCjkgMCBvYmoNCjw8L0F1dGhvcihLcmlzdGEgUHJhdGljbykgL0NyZWF0b3Io/v8ATQBp + AGMAcgBvAHMAbwBmAHQArgAgAFcAbwByAGQAIABmAG8AcgAgAE8AZgBmAGkAYwBlACAAMwA2ADUp + IC9DcmVhdGlvbkRhdGUoRDoyMDIwMDMyMDEwNDQ0Ni0wNycwMCcpIC9Nb2REYXRlKEQ6MjAyMDAz + MjAxMDQ0NDYtMDcnMDAnKSAvUHJvZHVjZXIo/v8ATQBpAGMAcgBvAHMAbwBmAHQArgAgAFcAbwBy + AGQAIABmAG8AcgAgAE8AZgBmAGkAYwBlACAAMwA2ADUpID4+DQplbmRvYmoNCjE3IDAgb2JqDQo8 + PC9UeXBlL09ialN0bS9OIDcvRmlyc3QgNDYvRmlsdGVyL0ZsYXRlRGVjb2RlL0xlbmd0aCAyOTY+ + Pg0Kc3RyZWFtDQp4nG1R0WrCMBR9F/yH+we3sa1jIMKYyoZYSivsofgQ610NtomkKejfL3ftsANf + wjk355ycJCKGAEQEsQDhQRCD8Oh1DmIGUTgDEUIU++EcopcAFgtMWR1AhjmmuL9fCXNnu9Kta2pw + W0BwAEwrCFmzXE4nvSUYLCtTdg1p98wpuEp2gME1UuwtUWaMw8zUtJNX7sh5qbQ+i3e5Lk84Jupj + RrsJ3dyW7iCG6I3P0sYRJrys9elB9l56NDfMqXT4QfJEtsfs+cOfulaa8rPkhjx40z5BOmX0wK1T + 39KDX/Zl7OVozOVxe560ZyLHJR3uZGnNiL+f/TriKyVrU40Gea1ONNL253hZZWWDG1V1loa7Jl3T + FvzH83+vm8iG2qKnj6efTn4AVAqiuw0KZW5kc3RyZWFtDQplbmRvYmoNCjE4IDAgb2JqDQpbIDIy + Nl0gDQplbmRvYmoNCjE5IDAgb2JqDQo8PC9GaWx0ZXIvRmxhdGVEZWNvZGUvTGVuZ3RoIDE5Mzg5 + L0xlbmd0aDEgODE3NDA+Pg0Kc3RyZWFtDQp4nOx9B3xUVdr+OfdOy8wkM5NkkkkmYWaYJASGFCCB + BJAMpNA7gwk1IYWAAQKEIgJGUdAo9l7Rta1YJgNqwO5iWQv2vhZ2XVdXse3qKgL5nnPfORDY1f+3 + 1fX7z5s88zznPeWe+t6TH8kPxhljdnzoWG3lqIoZBf1stzPumcAYf6Jy1ITyq5qr4hnPzGBMKZw8 + vWDgtY/W3YO8s1Crtn5JXetF716EsiddgvwP6le3eXe1vlHM2LYLGNM/0NS6cMnGd9UhjC1dy1h8 + YGHLyU2vVu4oYuwW1LF90NxY1/DtxJPDaM+K9gY3wxF/Z8Z+pCuQzmpe0rZ2xDjjAaQ/YmzRHS3L + 6uvyGvrezNi9hSg+c0nd2tZ8c/abyG9Gee+Sxra6q07ftppxXzLSZyytW9J43YGv5zP2KfpbuLJ1 + 2cq2bjfbzHjGQVG+dUVja9LC3mmMnXITHvcJE3NhGLpv9uI1H8+3Df+apZmYsPs/Wf+s4NfHrpn8 + /YFD7XGfmgYjGccURoZ6BnaY8T3mbd8fOLAt7lOtpR6WdofwuPuxdmZnw6EVcAHbwljiYO25nKm6 + AL+A6ZlJf6V+EJrsRay+wDYrzMQUm15RFJ2q6D5g+d2PsKxTtB7AJk73elmQsexnqQ/G65QcL+Pd + Ik+9T58gRsqSdQlHe8OfZ//fm+F1dsdP3Yf/K6ZrZDf81H34e8xg+Pf0V93/85qHf4fpiljtT92H + mP3zpjzNrvyp+/BzMOX3bMw/Uo9/w1r+1X2JWcxiFrOY/eOmXM3NP5hXy/b/J/vyczG1mJ3zU/ch + ZjGLWcxi9o+b7lHW9B9/5hJ23n/6mTGLWcxiFrOYxSxmMYtZzGIWs/+7Fvs5M2Yxi1nMYhazmMUs + ZjGLWcxiFrOYxey/23jst9FjFrOYxSxmMYtZzGIWs5jFLGYxi1nMYhazmMUsZjGLWcxiFrOYxSxm + MYtZzGIWs5jFLGYxi1nMYhazmMUsZjGLWcz+S6x790/dg5jF7Cc2NYqM6P8k1YEUlLKa6dhSpFOY + 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 + +xG3mmhL96QrfW1pvHxyGl+Wdmra+WmqzfW8Swm6+vavsqU+n/pe6uepuqRgat/8KpZiT/GmqE4x + tpSJM6o0LqsgHlCsjdWT4s+psjm5zelxKpWfO/lmpnIv54zbQaoJZXZyp6dKfZCLX6LTM84vYDMC + 47tMbNr4sGnK7DA/K5w9XXwGp84KG84Ks9Cs2dWdnJ9Xo/1OQjhZ/FKJlj5z61aWOWp8OHN6dUTd + ti1zVM34cLvQwaCmu4VmKFITmLdy1cpAdfAE5njf8YVDdT5sf96u2GzcZuu2KUEbOm9L8CQo4qM7 + QQ0mDBhSZYv3xCviozteTQnGwyPG18c6ZUaVzeKxKKEyy2SLErSUlVcFLXmFVX81zh1inPTkQNs8 + fMxb2RbQvpGq4atEMiC84ntlG9Lia5WWZoEfNSoGmr8S1iadbT9e67/d+E/dgZ+/0W/yjOxWzmAN + yibgdOA0oB04FdgIbADWA6cA64CTgbXAGmA1sApoA1YCy4FWYBmwFFgCtAAnAYuBRUAzsBBoAhqB + BqAeWADUAbXAfGAeMBeYA8wGZgE1QDVwIjATCAEzgOnANGAqMAWYDEwCJgITgPHAOGAsMAYYDVQB + lUAFUA6MAkYCQaAMGAGcAAwHhgFDgVKgBBgCDAaKgSJgEDAQGAAUAgVAPpAH9AcCQD+gL5AL9AFy + gGwgC/ADvQEf4AU8QC8gE8gA3EA6kAa4gFQgBXACyUASkAg4ADtgAxKAeMAKWAAzEAeYACNgAPSA + bmQ3PlVAATjAWAOHjx8GDgEHge+BA8B3wLfAX4BvgK+BPwN/Ar4CvgS+AD4HPgP2A58CnwB/BD4G + PgL+AHwI/B74APgd8FtgH/A+8B7wLvAO8BvgbeAt4E3gDeB14DXgVeAV4GXgJeBF4AXgeWAv8Bzw + LPAM8DTwa+Ap4EngCeBxYA/wK+Ax4FHgEeBh4CHgQeAB4H5gN7AL6ALuA+4F7gF2AjuACNAJhIG7 + gbuAO4E7gO3A7cAvgduAW4FbgJuBm4BfADcCNwDbgOuB64BrgWuAq4GrgCuBK4DLgcuAS4FLgIuB + i4ALgQuA84HzgK3AucA5QAdwNnAWsAXYDJzJGka2c5x/jvPPcf45zj/H+ec4/xznn+P8c5x/jvPP + cf45zj/H+ec4/xznn+P8c5x/jvPPVwCIARwxgCMGcMQAjhjAEQM4YgBHDOCIARwxgCMGcMQAjhjA + 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 + uHNOntVntltoN7fZZcns+b3njj5RYD9zj2wYh93jRY32F+XYd7t63rI9HL47es2yJ/Lzkt3tQlaU + DZGNmzl6SlRiyln72V+ccO/r8eSz3lNExze9zb7aw4rjv/BG9zYrTm3xzCvteuW29FBk9/xRPk9c + Rq+cqR877Y8jHRlRnfZOdt6+fI2F/cK6rFdZnDnlnXp2yh4glHjZyFUD5829NvaP6hlPojpffBN+ + fGfIA1WHDdc3+tYdv/DEceN8v7J5cY7d7R5bTzrjVcNzu/Jwf8DPgxZ/K+xq+6Pt3h/Xb7i++bfz + riX9BAPCeCN93b+s/MP3xUV2N0I+b3PapCyVetVWfU0vC8uVeCe/HsU9XeIy7Kqr4p5envGlh9p1 + tGBV7vVenWTLaoaI0ifUeEq6lE2oPffkxeOWFQt8L/+0ouz4/SES/tVBVl9P7GGZZ/mLZWWOV4td + YvGWh7//2Ia1q5Z/wNHv/kVZ4L2yZxWD557FzlRE7Ux7UrbCpm+M8/xCr+OY//7KhSt6CvLahvx4 + YunSJaNGtXsVM8dr7eton8Kni1/szt7at+zK3Zx893t3QucXuPV9f6bKJyvnxsZXb6bctSu8Iw/f + +IasY8VOr63NUUpmdf9lUWp8wu7C/u0q8pvxvEc94NtW9ny9+ujyIXsqShb2H5kaHyOo7n1oYe4g + 28KY7LcFS/bsVCpHHBLpXBxGJf7ELWZtIotZ6xg4ThbN+acLV9O/Dqy/OVJetA8mHzqIbZhce9M7 + L0CK+p4d15E0nXUlfeoXsrggtb0tjVw1/fGjM0XNa/13KmeO++6u+0VSarLEnptKJpd3KvRr8qu7 + yR8+TaWiY2H7j+7sZONfERGNajOrGMeSoqevHPvdYvVAX8vz3KGiwO1V/ax6ch09R23Ii04evDs0 + 2CnU+WRSRvsUy3OiWa635i1oKdcOYm+ousbxd+7gGGX7Wj5xdrTix9nSvuf3TmbVZj3gTvj14reH + 18+qm7ay35fq/DU4a8fbHVu/P3i77u3+idi5m9sXSZedCD+gODDs9e3XP7geLwtT1HW2fPwgemKz + /OOe7/uH/3RlQNvUWwdKrJvvXamY//X119X+sheffcZcF/NtO/4o71U7brQ4OjPi9aA2dQm5bvxv + 3q6JcZocnrJtxN4dK3kXJM67ugyYbsHp6TFzyNJpN2+5T7pVOu+ngmc97npkFzuOwA/vSO2YtdzB + q7Zj8tm+7EHekyuKGX7geNK+3keW3GKGKxhqhkJz+j92Id70nTaTmBxCupmGpF39HUMcMDfOWHCd + 0C+Ou3BDeFz4GvhBREbcHhe+ONHvwN2OU11Vp6qzPBd+V9DokgnGCjfe5UvGpP5Mj7Q+Zfq7tmP7 + +Ae5+x8Y8uTc1cf3v1hbutDnFi+z+V37K+dOT4vvMKLjstoFhUPnB5zoMlTWYs1vVzeOaam8w291 + XH/hvfqBTUXvxY/7jPyyk2jgYq/7jKoAYWmk96n7L+2sxHdTCsZYF4wp07gMK5cN8rPwyjiw+WDG + olP3xRf5udFb3148d/1t8bvrkrRjP1zdXOYg33di5JxHT3Mjv7+0r+CXdz8v32a3hGuRdD122/bv + vVKGVDwZd3v2xWk7NtkV3XVZ1KPLiOyvjw7h/3J7+enzy6punTtvP9plwNne7FOq7b/6h4+729uh + eqxVv8vdnqxNi908ORd/sHGv/+OcFZO5XX+fFon9H5Hg2m4NCmVuZHN0cmVhbQ0KZW5kb2JqDQoy + MCAwIG9iag0KPDwvVHlwZS9NZXRhZGF0YS9TdWJ0eXBlL1hNTC9MZW5ndGggMzA4ND4+DQpzdHJl + YW0NCjw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+ + PHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iMy4xLTcwMSI+Cjxy + ZGY6UkRGIHhtbG5zOnJkZj0iaHR0cDovL3d3dy53My5vcmcvMTk5OS8wMi8yMi1yZGYtc3ludGF4 + LW5zIyI+CjxyZGY6RGVzY3JpcHRpb24gcmRmOmFib3V0PSIiICB4bWxuczpwZGY9Imh0dHA6Ly9u + cy5hZG9iZS5jb20vcGRmLzEuMy8iPgo8cGRmOlByb2R1Y2VyPk1pY3Jvc29mdMKuIFdvcmQgZm9y + IE9mZmljZSAzNjU8L3BkZjpQcm9kdWNlcj48L3JkZjpEZXNjcmlwdGlvbj4KPHJkZjpEZXNjcmlw + dGlvbiByZGY6YWJvdXQ9IiIgIHhtbG5zOmRjPSJodHRwOi8vcHVybC5vcmcvZGMvZWxlbWVudHMv + MS4xLyI+CjxkYzpjcmVhdG9yPjxyZGY6U2VxPjxyZGY6bGk+S3Jpc3RhIFByYXRpY288L3JkZjps + aT48L3JkZjpTZXE+PC9kYzpjcmVhdG9yPjwvcmRmOkRlc2NyaXB0aW9uPgo8cmRmOkRlc2NyaXB0 + aW9uIHJkZjphYm91dD0iIiAgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAv + Ij4KPHhtcDpDcmVhdG9yVG9vbD5NaWNyb3NvZnTCriBXb3JkIGZvciBPZmZpY2UgMzY1PC94bXA6 + Q3JlYXRvclRvb2w+PHhtcDpDcmVhdGVEYXRlPjIwMjAtMDMtMjBUMTA6NDQ6NDYtMDc6MDA8L3ht + cDpDcmVhdGVEYXRlPjx4bXA6TW9kaWZ5RGF0ZT4yMDIwLTAzLTIwVDEwOjQ0OjQ2LTA3OjAwPC94 + bXA6TW9kaWZ5RGF0ZT48L3JkZjpEZXNjcmlwdGlvbj4KPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJv + dXQ9IiIgIHhtbG5zOnhtcE1NPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvbW0vIj4KPHht + cE1NOkRvY3VtZW50SUQ+dXVpZDo4RjI5Q0E4Qy1FRThCLTQ3NTktQkM5Qi1BMDhFRkVFNjYyMDE8 + L3htcE1NOkRvY3VtZW50SUQ+PHhtcE1NOkluc3RhbmNlSUQ+dXVpZDo4RjI5Q0E4Qy1FRThCLTQ3 + NTktQkM5Qi1BMDhFRkVFNjYyMDE8L3htcE1NOkluc3RhbmNlSUQ+PC9yZGY6RGVzY3JpcHRpb24+ + CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg + ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAg + ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg + ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAg + ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg + ICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg + ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg + ICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg + ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAg + ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg + ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAg + ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg + ICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg + ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg + ICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg + ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAg + ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg + ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAg + ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg + ICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAg + ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg + ICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg + ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg + ICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg + ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAg + ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg + ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAg + ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg + ICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg + ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg + ICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg + ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAg + ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg + ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAg + ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg + ICAgICAgICAgICAgICAgICAgICAgICAgIAo8L3JkZjpSREY+PC94OnhtcG1ldGE+PD94cGFja2V0 + IGVuZD0idyI/Pg0KZW5kc3RyZWFtDQplbmRvYmoNCjIxIDAgb2JqDQo8PC9EaXNwbGF5RG9jVGl0 + bGUgdHJ1ZT4+DQplbmRvYmoNCjIyIDAgb2JqDQo8PC9UeXBlL1hSZWYvU2l6ZSAyMi9XWyAxIDQg + Ml0gL1Jvb3QgMSAwIFIvSW5mbyA5IDAgUi9JRFs8OENDQTI5OEY4QkVFNTk0N0JDOUJBMDhFRkVF + NjYyMDE+PDhDQ0EyOThGOEJFRTU5NDdCQzlCQTA4RUZFRTY2MjAxPl0gL0ZpbHRlci9GbGF0ZURl + Y29kZS9MZW5ndGggODM+Pg0Kc3RyZWFtDQp4nC3LsQFAQAyF4ZfcHbW1KJUKnTHYxgR6k1Ba48R7 + UuQrkh+IqdVid8DHLm5iD/GDpInkXmwibs7cRRJZFGHi/2yiKyfzdhALGVcyX8ALyoALUA0KZW5k + c3RyZWFtDQplbmRvYmoNCnhyZWYNCjAgMjMNCjAwMDAwMDAwMTAgNjU1MzUgZg0KMDAwMDAwMDAx + NyAwMDAwMCBuDQowMDAwMDAwMTY2IDAwMDAwIG4NCjAwMDAwMDAyMjIgMDAwMDAgbg0KMDAwMDAw + MDQ4NiAwMDAwMCBuDQowMDAwMDAwNjkyIDAwMDAwIG4NCjAwMDAwMDA4NTkgMDAwMDAgbg0KMDAw + MDAwMTA5OCAwMDAwMCBuDQowMDAwMDAxMTUxIDAwMDAwIG4NCjAwMDAwMDEyMDQgMDAwMDAgbg0K + MDAwMDAwMDAxMSA2NTUzNSBmDQowMDAwMDAwMDEyIDY1NTM1IGYNCjAwMDAwMDAwMTMgNjU1MzUg + Zg0KMDAwMDAwMDAxNCA2NTUzNSBmDQowMDAwMDAwMDE1IDY1NTM1IGYNCjAwMDAwMDAwMTYgNjU1 + MzUgZg0KMDAwMDAwMDAxNyA2NTUzNSBmDQowMDAwMDAwMDAwIDY1NTM1IGYNCjAwMDAwMDE4Njcg + MDAwMDAgbg0KMDAwMDAwMTg5NCAwMDAwMCBuDQowMDAwMDIxMzc0IDAwMDAwIG4NCjAwMDAwMjQ1 + NDEgMDAwMDAgbg0KMDAwMDAyNDU4NiAwMDAwMCBuDQp0cmFpbGVyDQo8PC9TaXplIDIzL1Jvb3Qg + MSAwIFIvSW5mbyA5IDAgUi9JRFs8OENDQTI5OEY4QkVFNTk0N0JDOUJBMDhFRkVFNjYyMDE+PDhD + Q0EyOThGOEJFRTU5NDdCQzlCQTA4RUZFRTY2MjAxPl0gPj4NCnN0YXJ0eHJlZg0KMjQ4NjgNCiUl + RU9GDQp4cmVmDQowIDANCnRyYWlsZXINCjw8L1NpemUgMjMvUm9vdCAxIDAgUi9JbmZvIDkgMCBS + L0lEWzw4Q0NBMjk4RjhCRUU1OTQ3QkM5QkEwOEVGRUU2NjIwMT48OENDQTI5OEY4QkVFNTk0N0JD + OUJBMDhFRkVFNjYyMDE+XSAvUHJldiAyNDg2OC9YUmVmU3RtIDI0NTg2Pj4NCnN0YXJ0eHJlZg0K + MjU0ODQNCiUlRU9G + headers: + Content-Type: + - application/pdf + User-Agent: + - azsdk-python-ai-formrecognizer/1.0.0b4 Python/3.7.3 (Windows-10-10.0.18362-SP0) + Python/3.7.3 (Windows-10-10.0.18362-SP0) + method: POST + uri: https://centraluseuap.api.cognitive.microsoft.com/formrecognizer/v2.0-preview/custom/models/453f8f6c-6c05-49e4-aca5-021ca0b2d87f/analyze?includeTextDetails=false + response: + body: + string: '' + headers: + apim-request-id: f783555a-4a07-4f70-88a6-6231346db487 + content-length: '0' + date: Mon, 15 Jun 2020 20:08:28 GMT + operation-location: https://centraluseuap.api.cognitive.microsoft.com/formrecognizer/v2.0-preview/custom/models/453f8f6c-6c05-49e4-aca5-021ca0b2d87f/analyzeresults/dfacccb6-1a1b-4607-86bd-db85568ff01a + strict-transport-security: max-age=31536000; includeSubDomains; preload + x-content-type-options: nosniff + x-envoy-upstream-service-time: '361' + status: + code: 202 + message: Accepted + url: https://centraluseuap.api.cognitive.microsoft.com//formrecognizer/v2.0-preview/custom/models/453f8f6c-6c05-49e4-aca5-021ca0b2d87f/analyze?includeTextDetails=false +- request: + body: null + headers: + User-Agent: + - azsdk-python-ai-formrecognizer/1.0.0b4 Python/3.7.3 (Windows-10-10.0.18362-SP0) + Python/3.7.3 (Windows-10-10.0.18362-SP0) + method: GET + uri: https://centraluseuap.api.cognitive.microsoft.com/formrecognizer/v2.0-preview/custom/models/453f8f6c-6c05-49e4-aca5-021ca0b2d87f/analyzeresults/dfacccb6-1a1b-4607-86bd-db85568ff01a + response: + body: + string: '{"status": "succeeded", "createdDateTime": "2020-06-15T20:08:28Z", + "lastUpdatedDateTime": "2020-06-15T20:08:32Z", "analyzeResult": {"version": + "2.0.0", "readResults": [{"page": 1, "angle": 0, "width": 8.5, "height": 11.0, + "unit": "inch", "lines": []}], "pageResults": [{"page": 1, "keyValuePairs": + [], "tables": [], "clusterId": null}], "documentResults": [], "errors": []}}' + headers: + apim-request-id: 3d04e0d7-03ed-4f5c-ab34-96ecce4138eb + content-length: '374' + content-type: application/json; charset=utf-8 + date: Mon, 15 Jun 2020 20:08:33 GMT + strict-transport-security: max-age=31536000; includeSubDomains; preload + x-content-type-options: nosniff + x-envoy-upstream-service-time: '199' + status: + code: 200 + message: OK + url: https://centraluseuap.api.cognitive.microsoft.com/formrecognizer/v2.0-preview/custom/models/453f8f6c-6c05-49e4-aca5-021ca0b2d87f/analyzeresults/dfacccb6-1a1b-4607-86bd-db85568ff01a +version: 1 diff --git a/sdk/formrecognizer/azure-ai-formrecognizer/tests/recordings/test_custom_forms_from_url.test_custom_forms_encoded_url.yaml b/sdk/formrecognizer/azure-ai-formrecognizer/tests/recordings/test_custom_forms_from_url.test_custom_forms_encoded_url.yaml new file mode 100644 index 000000000000..c9d7f7d50ef4 --- /dev/null +++ b/sdk/formrecognizer/azure-ai-formrecognizer/tests/recordings/test_custom_forms_from_url.test_custom_forms_encoded_url.yaml @@ -0,0 +1,42 @@ +interactions: +- request: + body: 'b''{"source": "https://fakeuri.com/blank%20space"}''' + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + Content-Length: + - '47' + Content-Type: + - application/json + User-Agent: + - azsdk-python-ai-formrecognizer/1.0.0b4 Python/3.7.3 (Windows-10-10.0.18362-SP0) + Python/3.7.3 (Windows-10-10.0.18362-SP0) + method: POST + uri: https://centraluseuap.api.cognitive.microsoft.com/formrecognizer/v2.0-preview/custom/models/00000000-0000-0000-0000-000000000000/analyze?includeTextDetails=false + response: + body: + string: '{"error": {"code": "1001", "message": "Specified model not found or + not ready, Model Id: 00000000-0000-0000-0000-000000000000"}}' + headers: + apim-request-id: + - 5b94e3e4-4dbf-47f7-94d8-4e0e85982941 + content-type: + - application/json; charset=utf-8 + date: + - Mon, 15 Jun 2020 18:21:57 GMT + strict-transport-security: + - max-age=31536000; includeSubDomains; preload + transfer-encoding: + - chunked + x-content-type-options: + - nosniff + x-envoy-upstream-service-time: + - '13' + status: + code: 400 + message: Bad Request +version: 1 diff --git a/sdk/formrecognizer/azure-ai-formrecognizer/tests/recordings/test_custom_forms_from_url_async.test_custom_forms_encoded_url.yaml b/sdk/formrecognizer/azure-ai-formrecognizer/tests/recordings/test_custom_forms_from_url_async.test_custom_forms_encoded_url.yaml new file mode 100644 index 000000000000..9ffa6e6e91f2 --- /dev/null +++ b/sdk/formrecognizer/azure-ai-formrecognizer/tests/recordings/test_custom_forms_from_url_async.test_custom_forms_encoded_url.yaml @@ -0,0 +1,30 @@ +interactions: +- request: + body: 'b''{"source": "https://fakeuri.com/blank%20space"}''' + headers: + Content-Length: + - '47' + Content-Type: + - application/json + User-Agent: + - azsdk-python-ai-formrecognizer/1.0.0b4 Python/3.7.3 (Windows-10-10.0.18362-SP0) + Python/3.7.3 (Windows-10-10.0.18362-SP0) + method: POST + uri: https://centraluseuap.api.cognitive.microsoft.com/formrecognizer/v2.0-preview/custom/models/00000000-0000-0000-0000-000000000000/analyze?includeTextDetails=false + response: + body: + string: '{"error": {"code": "1001", "message": "Specified model not found or + not ready, Model Id: 00000000-0000-0000-0000-000000000000"}}' + headers: + apim-request-id: b0aacb56-81aa-4ba0-bcb0-d8ffeb7d81fb + content-type: application/json; charset=utf-8 + date: Mon, 15 Jun 2020 18:24:07 GMT + strict-transport-security: max-age=31536000; includeSubDomains; preload + transfer-encoding: chunked + x-content-type-options: nosniff + x-envoy-upstream-service-time: '1018' + status: + code: 400 + message: Bad Request + url: https://centraluseuap.api.cognitive.microsoft.com//formrecognizer/v2.0-preview/custom/models/00000000-0000-0000-0000-000000000000/analyze?includeTextDetails=false +version: 1 diff --git a/sdk/formrecognizer/azure-ai-formrecognizer/tests/recordings/test_receipt_from_url.test_receipts_encoded_url.yaml b/sdk/formrecognizer/azure-ai-formrecognizer/tests/recordings/test_receipt_from_url.test_receipts_encoded_url.yaml new file mode 100644 index 000000000000..927d4d5156ba --- /dev/null +++ b/sdk/formrecognizer/azure-ai-formrecognizer/tests/recordings/test_receipt_from_url.test_receipts_encoded_url.yaml @@ -0,0 +1,43 @@ +interactions: +- request: + body: 'b''{"source": "https://fakeuri.com/blank%20space"}''' + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + Content-Length: + - '47' + Content-Type: + - application/json + User-Agent: + - azsdk-python-ai-formrecognizer/1.0.0b4 Python/3.7.3 (Windows-10-10.0.18362-SP0) + Python/3.7.3 (Windows-10-10.0.18362-SP0) + method: POST + uri: https://centraluseuap.api.cognitive.microsoft.com/formrecognizer/v2.0-preview/prebuilt/receipt/analyze?includeTextDetails=false + response: + body: + string: '{"error": {"code": "FailedToDownloadImage", "innerError": {"requestId": + "25ef376a-66b0-400f-96bf-033e614f965f"}, "message": "Failed to download image + from input URL."}}' + headers: + apim-request-id: + - 25ef376a-66b0-400f-96bf-033e614f965f + content-type: + - application/json; charset=utf-8 + date: + - Mon, 15 Jun 2020 18:26:28 GMT + strict-transport-security: + - max-age=31536000; includeSubDomains; preload + transfer-encoding: + - chunked + x-content-type-options: + - nosniff + x-envoy-upstream-service-time: + - '5975' + status: + code: 400 + message: Bad Request +version: 1 diff --git a/sdk/formrecognizer/azure-ai-formrecognizer/tests/recordings/test_receipt_from_url_async.test_receipts_encoded_url.yaml b/sdk/formrecognizer/azure-ai-formrecognizer/tests/recordings/test_receipt_from_url_async.test_receipts_encoded_url.yaml new file mode 100644 index 000000000000..a0b87d103190 --- /dev/null +++ b/sdk/formrecognizer/azure-ai-formrecognizer/tests/recordings/test_receipt_from_url_async.test_receipts_encoded_url.yaml @@ -0,0 +1,31 @@ +interactions: +- request: + body: 'b''{"source": "https://fakeuri.com/blank%20space"}''' + headers: + Content-Length: + - '47' + Content-Type: + - application/json + User-Agent: + - azsdk-python-ai-formrecognizer/1.0.0b4 Python/3.7.3 (Windows-10-10.0.18362-SP0) + Python/3.7.3 (Windows-10-10.0.18362-SP0) + method: POST + uri: https://centraluseuap.api.cognitive.microsoft.com/formrecognizer/v2.0-preview/prebuilt/receipt/analyze?includeTextDetails=false + response: + body: + string: '{"error": {"code": "FailedToDownloadImage", "innerError": {"requestId": + "b256740a-1a9f-4d69-8304-928f7e29befb"}, "message": "Failed to download image + from input URL."}}' + headers: + apim-request-id: b256740a-1a9f-4d69-8304-928f7e29befb + content-type: application/json; charset=utf-8 + date: Mon, 15 Jun 2020 18:28:14 GMT + strict-transport-security: max-age=31536000; includeSubDomains; preload + transfer-encoding: chunked + x-content-type-options: nosniff + x-envoy-upstream-service-time: '3921' + status: + code: 400 + message: Bad Request + url: https://centraluseuap.api.cognitive.microsoft.com//formrecognizer/v2.0-preview/prebuilt/receipt/analyze?includeTextDetails=false +version: 1 diff --git a/sdk/formrecognizer/azure-ai-formrecognizer/tests/recordings/test_training.test_training_encoded_url.yaml b/sdk/formrecognizer/azure-ai-formrecognizer/tests/recordings/test_training.test_training_encoded_url.yaml new file mode 100644 index 000000000000..e4de7a89865d --- /dev/null +++ b/sdk/formrecognizer/azure-ai-formrecognizer/tests/recordings/test_training.test_training_encoded_url.yaml @@ -0,0 +1,638 @@ +interactions: +- request: + body: 'b''{"source": "https://fakeuri.com/blank%20space", "sourceFilter": {"prefix": + "", "includeSubFolders": false}, "useLabelFile": false}''' + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + Content-Length: + - '130' + Content-Type: + - application/json + User-Agent: + - azsdk-python-ai-formrecognizer/1.0.0b4 Python/3.7.3 (Windows-10-10.0.18362-SP0) + Python/3.7.3 (Windows-10-10.0.18362-SP0) + method: POST + uri: https://centraluseuap.api.cognitive.microsoft.com/formrecognizer/v2.0-preview/custom/models + response: + body: + string: '' + headers: + apim-request-id: + - 0b8029d2-9586-4db7-b72d-dc71a3f5eb57 + content-length: + - '0' + date: + - Mon, 15 Jun 2020 18:58:04 GMT + location: + - https://centraluseuap.api.cognitive.microsoft.com/formrecognizer/v2.0-preview/custom/models/f3ac1100-fae4-4edb-b428-a0d50c74e742 + strict-transport-security: + - max-age=31536000; includeSubDomains; preload + x-content-type-options: + - nosniff + x-envoy-upstream-service-time: + - '1313' + status: + code: 201 + message: Created +- request: + body: null + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + User-Agent: + - azsdk-python-ai-formrecognizer/1.0.0b4 Python/3.7.3 (Windows-10-10.0.18362-SP0) + Python/3.7.3 (Windows-10-10.0.18362-SP0) + method: GET + uri: https://centraluseuap.api.cognitive.microsoft.com/formrecognizer/v2.0-preview/custom/models/f3ac1100-fae4-4edb-b428-a0d50c74e742?includeKeys=true + response: + body: + string: '{"modelInfo": {"modelId": "f3ac1100-fae4-4edb-b428-a0d50c74e742", "status": + "creating", "createdDateTime": "2020-06-15T18:58:03Z", "lastUpdatedDateTime": + "2020-06-15T18:58:03Z"}}' + headers: + apim-request-id: + - 1c07f69a-22e3-4da9-b83e-ac12558f1d63 + content-type: + - application/json; charset=utf-8 + date: + - Mon, 15 Jun 2020 18:58:09 GMT + strict-transport-security: + - max-age=31536000; includeSubDomains; preload + transfer-encoding: + - chunked + x-content-type-options: + - nosniff + x-envoy-upstream-service-time: + - '209' + status: + code: 200 + message: OK +- request: + body: null + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + User-Agent: + - azsdk-python-ai-formrecognizer/1.0.0b4 Python/3.7.3 (Windows-10-10.0.18362-SP0) + Python/3.7.3 (Windows-10-10.0.18362-SP0) + method: GET + uri: https://centraluseuap.api.cognitive.microsoft.com/formrecognizer/v2.0-preview/custom/models/f3ac1100-fae4-4edb-b428-a0d50c74e742?includeKeys=true + response: + body: + string: '{"modelInfo": {"modelId": "f3ac1100-fae4-4edb-b428-a0d50c74e742", "status": + "creating", "createdDateTime": "2020-06-15T18:58:03Z", "lastUpdatedDateTime": + "2020-06-15T18:58:03Z"}}' + headers: + apim-request-id: + - 913fbe7a-01e1-48e7-a1d2-30620d76e3a2 + content-type: + - application/json; charset=utf-8 + date: + - Mon, 15 Jun 2020 18:58:14 GMT + strict-transport-security: + - max-age=31536000; includeSubDomains; preload + transfer-encoding: + - chunked + x-content-type-options: + - nosniff + x-envoy-upstream-service-time: + - '157' + status: + code: 200 + message: OK +- request: + body: null + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + User-Agent: + - azsdk-python-ai-formrecognizer/1.0.0b4 Python/3.7.3 (Windows-10-10.0.18362-SP0) + Python/3.7.3 (Windows-10-10.0.18362-SP0) + method: GET + uri: https://centraluseuap.api.cognitive.microsoft.com/formrecognizer/v2.0-preview/custom/models/f3ac1100-fae4-4edb-b428-a0d50c74e742?includeKeys=true + response: + body: + string: '{"modelInfo": {"modelId": "f3ac1100-fae4-4edb-b428-a0d50c74e742", "status": + "creating", "createdDateTime": "2020-06-15T18:58:03Z", "lastUpdatedDateTime": + "2020-06-15T18:58:03Z"}}' + headers: + apim-request-id: + - f57fdaa5-0627-49b0-a4b9-33f1817ec9d8 + content-type: + - application/json; charset=utf-8 + date: + - Mon, 15 Jun 2020 18:58:20 GMT + strict-transport-security: + - max-age=31536000; includeSubDomains; preload + transfer-encoding: + - chunked + x-content-type-options: + - nosniff + x-envoy-upstream-service-time: + - '171' + status: + code: 200 + message: OK +- request: + body: null + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + User-Agent: + - azsdk-python-ai-formrecognizer/1.0.0b4 Python/3.7.3 (Windows-10-10.0.18362-SP0) + Python/3.7.3 (Windows-10-10.0.18362-SP0) + method: GET + uri: https://centraluseuap.api.cognitive.microsoft.com/formrecognizer/v2.0-preview/custom/models/f3ac1100-fae4-4edb-b428-a0d50c74e742?includeKeys=true + response: + body: + string: '{"modelInfo": {"modelId": "f3ac1100-fae4-4edb-b428-a0d50c74e742", "status": + "creating", "createdDateTime": "2020-06-15T18:58:03Z", "lastUpdatedDateTime": + "2020-06-15T18:58:03Z"}}' + headers: + apim-request-id: + - 94407af0-86fb-47e3-90c1-44e900220ad4 + content-type: + - application/json; charset=utf-8 + date: + - Mon, 15 Jun 2020 18:58:25 GMT + strict-transport-security: + - max-age=31536000; includeSubDomains; preload + transfer-encoding: + - chunked + x-content-type-options: + - nosniff + x-envoy-upstream-service-time: + - '157' + status: + code: 200 + message: OK +- request: + body: null + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + User-Agent: + - azsdk-python-ai-formrecognizer/1.0.0b4 Python/3.7.3 (Windows-10-10.0.18362-SP0) + Python/3.7.3 (Windows-10-10.0.18362-SP0) + method: GET + uri: https://centraluseuap.api.cognitive.microsoft.com/formrecognizer/v2.0-preview/custom/models/f3ac1100-fae4-4edb-b428-a0d50c74e742?includeKeys=true + response: + body: + string: '{"modelInfo": {"modelId": "f3ac1100-fae4-4edb-b428-a0d50c74e742", "status": + "creating", "createdDateTime": "2020-06-15T18:58:03Z", "lastUpdatedDateTime": + "2020-06-15T18:58:03Z"}}' + headers: + apim-request-id: + - ab67daae-9642-4927-9d21-5d3e112dcbe0 + content-type: + - application/json; charset=utf-8 + date: + - Mon, 15 Jun 2020 18:58:29 GMT + strict-transport-security: + - max-age=31536000; includeSubDomains; preload + transfer-encoding: + - chunked + x-content-type-options: + - nosniff + x-envoy-upstream-service-time: + - '52' + status: + code: 200 + message: OK +- request: + body: null + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + User-Agent: + - azsdk-python-ai-formrecognizer/1.0.0b4 Python/3.7.3 (Windows-10-10.0.18362-SP0) + Python/3.7.3 (Windows-10-10.0.18362-SP0) + method: GET + uri: https://centraluseuap.api.cognitive.microsoft.com/formrecognizer/v2.0-preview/custom/models/f3ac1100-fae4-4edb-b428-a0d50c74e742?includeKeys=true + response: + body: + string: '{"modelInfo": {"modelId": "f3ac1100-fae4-4edb-b428-a0d50c74e742", "status": + "creating", "createdDateTime": "2020-06-15T18:58:03Z", "lastUpdatedDateTime": + "2020-06-15T18:58:03Z"}}' + headers: + apim-request-id: + - e9d797d5-a225-4849-9cb9-1d6c8f4e35b7 + content-type: + - application/json; charset=utf-8 + date: + - Mon, 15 Jun 2020 18:58:35 GMT + strict-transport-security: + - max-age=31536000; includeSubDomains; preload + transfer-encoding: + - chunked + x-content-type-options: + - nosniff + x-envoy-upstream-service-time: + - '188' + status: + code: 200 + message: OK +- request: + body: null + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + User-Agent: + - azsdk-python-ai-formrecognizer/1.0.0b4 Python/3.7.3 (Windows-10-10.0.18362-SP0) + Python/3.7.3 (Windows-10-10.0.18362-SP0) + method: GET + uri: https://centraluseuap.api.cognitive.microsoft.com/formrecognizer/v2.0-preview/custom/models/f3ac1100-fae4-4edb-b428-a0d50c74e742?includeKeys=true + response: + body: + string: '{"modelInfo": {"modelId": "f3ac1100-fae4-4edb-b428-a0d50c74e742", "status": + "creating", "createdDateTime": "2020-06-15T18:58:03Z", "lastUpdatedDateTime": + "2020-06-15T18:58:03Z"}}' + headers: + apim-request-id: + - b3dd9764-e86f-4b2d-8612-cdb021fe4ae0 + content-type: + - application/json; charset=utf-8 + date: + - Mon, 15 Jun 2020 18:58:46 GMT + strict-transport-security: + - max-age=31536000; includeSubDomains; preload + transfer-encoding: + - chunked + x-content-type-options: + - nosniff + x-envoy-upstream-service-time: + - '5190' + status: + code: 200 + message: OK +- request: + body: null + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + User-Agent: + - azsdk-python-ai-formrecognizer/1.0.0b4 Python/3.7.3 (Windows-10-10.0.18362-SP0) + Python/3.7.3 (Windows-10-10.0.18362-SP0) + method: GET + uri: https://centraluseuap.api.cognitive.microsoft.com/formrecognizer/v2.0-preview/custom/models/f3ac1100-fae4-4edb-b428-a0d50c74e742?includeKeys=true + response: + body: + string: '{"modelInfo": {"modelId": "f3ac1100-fae4-4edb-b428-a0d50c74e742", "status": + "creating", "createdDateTime": "2020-06-15T18:58:03Z", "lastUpdatedDateTime": + "2020-06-15T18:58:03Z"}}' + headers: + apim-request-id: + - 1b3f4c13-2991-4e77-90b7-d1156432485a + content-type: + - application/json; charset=utf-8 + date: + - Mon, 15 Jun 2020 18:58:51 GMT + strict-transport-security: + - max-age=31536000; includeSubDomains; preload + transfer-encoding: + - chunked + x-content-type-options: + - nosniff + x-envoy-upstream-service-time: + - '152' + status: + code: 200 + message: OK +- request: + body: null + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + User-Agent: + - azsdk-python-ai-formrecognizer/1.0.0b4 Python/3.7.3 (Windows-10-10.0.18362-SP0) + Python/3.7.3 (Windows-10-10.0.18362-SP0) + method: GET + uri: https://centraluseuap.api.cognitive.microsoft.com/formrecognizer/v2.0-preview/custom/models/f3ac1100-fae4-4edb-b428-a0d50c74e742?includeKeys=true + response: + body: + string: '{"modelInfo": {"modelId": "f3ac1100-fae4-4edb-b428-a0d50c74e742", "status": + "creating", "createdDateTime": "2020-06-15T18:58:03Z", "lastUpdatedDateTime": + "2020-06-15T18:58:03Z"}}' + headers: + apim-request-id: + - 7b404408-c76e-461f-87f6-5261567ad59a + content-type: + - application/json; charset=utf-8 + date: + - Mon, 15 Jun 2020 18:58:56 GMT + strict-transport-security: + - max-age=31536000; includeSubDomains; preload + transfer-encoding: + - chunked + x-content-type-options: + - nosniff + x-envoy-upstream-service-time: + - '166' + status: + code: 200 + message: OK +- request: + body: null + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + User-Agent: + - azsdk-python-ai-formrecognizer/1.0.0b4 Python/3.7.3 (Windows-10-10.0.18362-SP0) + Python/3.7.3 (Windows-10-10.0.18362-SP0) + method: GET + uri: https://centraluseuap.api.cognitive.microsoft.com/formrecognizer/v2.0-preview/custom/models/f3ac1100-fae4-4edb-b428-a0d50c74e742?includeKeys=true + response: + body: + string: '{"modelInfo": {"modelId": "f3ac1100-fae4-4edb-b428-a0d50c74e742", "status": + "creating", "createdDateTime": "2020-06-15T18:58:03Z", "lastUpdatedDateTime": + "2020-06-15T18:58:03Z"}}' + headers: + apim-request-id: + - 972a96b3-fc68-4ff4-9995-5d7451268393 + content-type: + - application/json; charset=utf-8 + date: + - Mon, 15 Jun 2020 18:59:02 GMT + strict-transport-security: + - max-age=31536000; includeSubDomains; preload + transfer-encoding: + - chunked + x-content-type-options: + - nosniff + x-envoy-upstream-service-time: + - '157' + status: + code: 200 + message: OK +- request: + body: null + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + User-Agent: + - azsdk-python-ai-formrecognizer/1.0.0b4 Python/3.7.3 (Windows-10-10.0.18362-SP0) + Python/3.7.3 (Windows-10-10.0.18362-SP0) + method: GET + uri: https://centraluseuap.api.cognitive.microsoft.com/formrecognizer/v2.0-preview/custom/models/f3ac1100-fae4-4edb-b428-a0d50c74e742?includeKeys=true + response: + body: + string: '{"modelInfo": {"modelId": "f3ac1100-fae4-4edb-b428-a0d50c74e742", "status": + "creating", "createdDateTime": "2020-06-15T18:58:03Z", "lastUpdatedDateTime": + "2020-06-15T18:58:03Z"}}' + headers: + apim-request-id: + - aab792f7-5c62-4206-9ca5-480879aad146 + content-type: + - application/json; charset=utf-8 + date: + - Mon, 15 Jun 2020 18:59:12 GMT + strict-transport-security: + - max-age=31536000; includeSubDomains; preload + transfer-encoding: + - chunked + x-content-type-options: + - nosniff + x-envoy-upstream-service-time: + - '5169' + status: + code: 200 + message: OK +- request: + body: null + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + User-Agent: + - azsdk-python-ai-formrecognizer/1.0.0b4 Python/3.7.3 (Windows-10-10.0.18362-SP0) + Python/3.7.3 (Windows-10-10.0.18362-SP0) + method: GET + uri: https://centraluseuap.api.cognitive.microsoft.com/formrecognizer/v2.0-preview/custom/models/f3ac1100-fae4-4edb-b428-a0d50c74e742?includeKeys=true + response: + body: + string: '{"modelInfo": {"modelId": "f3ac1100-fae4-4edb-b428-a0d50c74e742", "status": + "creating", "createdDateTime": "2020-06-15T18:58:03Z", "lastUpdatedDateTime": + "2020-06-15T18:58:03Z"}}' + headers: + apim-request-id: + - 06433fab-6468-4dd5-8171-282997cac442 + content-type: + - application/json; charset=utf-8 + date: + - Mon, 15 Jun 2020 18:59:17 GMT + strict-transport-security: + - max-age=31536000; includeSubDomains; preload + transfer-encoding: + - chunked + x-content-type-options: + - nosniff + x-envoy-upstream-service-time: + - '152' + status: + code: 200 + message: OK +- request: + body: null + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + User-Agent: + - azsdk-python-ai-formrecognizer/1.0.0b4 Python/3.7.3 (Windows-10-10.0.18362-SP0) + Python/3.7.3 (Windows-10-10.0.18362-SP0) + method: GET + uri: https://centraluseuap.api.cognitive.microsoft.com/formrecognizer/v2.0-preview/custom/models/f3ac1100-fae4-4edb-b428-a0d50c74e742?includeKeys=true + response: + body: + string: '{"modelInfo": {"modelId": "f3ac1100-fae4-4edb-b428-a0d50c74e742", "status": + "creating", "createdDateTime": "2020-06-15T18:58:03Z", "lastUpdatedDateTime": + "2020-06-15T18:58:03Z"}}' + headers: + apim-request-id: + - f2338ce8-420b-47a9-9fde-d53ab9b3e70b + content-type: + - application/json; charset=utf-8 + date: + - Mon, 15 Jun 2020 18:59:23 GMT + strict-transport-security: + - max-age=31536000; includeSubDomains; preload + transfer-encoding: + - chunked + x-content-type-options: + - nosniff + x-envoy-upstream-service-time: + - '156' + status: + code: 200 + message: OK +- request: + body: null + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + User-Agent: + - azsdk-python-ai-formrecognizer/1.0.0b4 Python/3.7.3 (Windows-10-10.0.18362-SP0) + Python/3.7.3 (Windows-10-10.0.18362-SP0) + method: GET + uri: https://centraluseuap.api.cognitive.microsoft.com/formrecognizer/v2.0-preview/custom/models/f3ac1100-fae4-4edb-b428-a0d50c74e742?includeKeys=true + response: + body: + string: '{"modelInfo": {"modelId": "f3ac1100-fae4-4edb-b428-a0d50c74e742", "status": + "creating", "createdDateTime": "2020-06-15T18:58:03Z", "lastUpdatedDateTime": + "2020-06-15T18:58:03Z"}}' + headers: + apim-request-id: + - 9e3345e5-1d2f-4836-91a0-3c94edc84922 + content-type: + - application/json; charset=utf-8 + date: + - Mon, 15 Jun 2020 18:59:27 GMT + strict-transport-security: + - max-age=31536000; includeSubDomains; preload + transfer-encoding: + - chunked + x-content-type-options: + - nosniff + x-envoy-upstream-service-time: + - '53' + status: + code: 200 + message: OK +- request: + body: null + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + User-Agent: + - azsdk-python-ai-formrecognizer/1.0.0b4 Python/3.7.3 (Windows-10-10.0.18362-SP0) + Python/3.7.3 (Windows-10-10.0.18362-SP0) + method: GET + uri: https://centraluseuap.api.cognitive.microsoft.com/formrecognizer/v2.0-preview/custom/models/f3ac1100-fae4-4edb-b428-a0d50c74e742?includeKeys=true + response: + body: + string: '{"modelInfo": {"modelId": "f3ac1100-fae4-4edb-b428-a0d50c74e742", "status": + "creating", "createdDateTime": "2020-06-15T18:58:03Z", "lastUpdatedDateTime": + "2020-06-15T18:58:03Z"}}' + headers: + apim-request-id: + - 4c3d5398-01f4-4616-9d55-6683ef7f7f7b + content-type: + - application/json; charset=utf-8 + date: + - Mon, 15 Jun 2020 18:59:33 GMT + strict-transport-security: + - max-age=31536000; includeSubDomains; preload + transfer-encoding: + - chunked + x-content-type-options: + - nosniff + x-envoy-upstream-service-time: + - '53' + status: + code: 200 + message: OK +- request: + body: null + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + User-Agent: + - azsdk-python-ai-formrecognizer/1.0.0b4 Python/3.7.3 (Windows-10-10.0.18362-SP0) + Python/3.7.3 (Windows-10-10.0.18362-SP0) + method: GET + uri: https://centraluseuap.api.cognitive.microsoft.com/formrecognizer/v2.0-preview/custom/models/f3ac1100-fae4-4edb-b428-a0d50c74e742?includeKeys=true + response: + body: + string: '{"modelInfo": {"modelId": "f3ac1100-fae4-4edb-b428-a0d50c74e742", "status": + "invalid", "createdDateTime": "2020-06-15T18:58:03Z", "lastUpdatedDateTime": + "2020-06-15T18:59:35Z"}, "keys": {"clusters": {}}, "trainResult": {"trainingDocuments": + [], "errors": [{"code": "2012", "message": "Unable to list blobs on the Azure + blob storage account."}]}}' + headers: + apim-request-id: + - f751b1c9-2e05-44c5-909a-55c8966ab354 + content-type: + - application/json; charset=utf-8 + date: + - Mon, 15 Jun 2020 18:59:38 GMT + strict-transport-security: + - max-age=31536000; includeSubDomains; preload + transfer-encoding: + - chunked + x-content-type-options: + - nosniff + x-envoy-upstream-service-time: + - '961' + x-ms-cs-error-code: + - '2012' + status: + code: 200 + message: OK +version: 1 diff --git a/sdk/formrecognizer/azure-ai-formrecognizer/tests/recordings/test_training_async.test_training_encoded_url.yaml b/sdk/formrecognizer/azure-ai-formrecognizer/tests/recordings/test_training_async.test_training_encoded_url.yaml new file mode 100644 index 000000000000..8e9e4496258d --- /dev/null +++ b/sdk/formrecognizer/azure-ai-formrecognizer/tests/recordings/test_training_async.test_training_encoded_url.yaml @@ -0,0 +1,433 @@ +interactions: +- request: + body: 'b''{"source": "https://fakeuri.com/blank%20space", "sourceFilter": {"prefix": + "", "includeSubFolders": false}, "useLabelFile": false}''' + headers: + Content-Length: + - '130' + Content-Type: + - application/json + User-Agent: + - azsdk-python-ai-formrecognizer/1.0.0b4 Python/3.7.3 (Windows-10-10.0.18362-SP0) + Python/3.7.3 (Windows-10-10.0.18362-SP0) + method: POST + uri: https://centraluseuap.api.cognitive.microsoft.com/formrecognizer/v2.0-preview/custom/models + response: + body: + string: '' + headers: + apim-request-id: 8f9f92f4-ab5e-4c58-a420-dda9e2c05512 + content-length: '0' + date: Mon, 15 Jun 2020 19:04:51 GMT + location: https://centraluseuap.api.cognitive.microsoft.com/formrecognizer/v2.0-preview/custom/models/39f91c56-f66f-407e-812c-3b9a001feacd + strict-transport-security: max-age=31536000; includeSubDomains; preload + x-content-type-options: nosniff + x-envoy-upstream-service-time: '228' + status: + code: 201 + message: Created + url: https://centraluseuap.api.cognitive.microsoft.com//formrecognizer/v2.0-preview/custom/models +- request: + body: null + headers: + User-Agent: + - azsdk-python-ai-formrecognizer/1.0.0b4 Python/3.7.3 (Windows-10-10.0.18362-SP0) + Python/3.7.3 (Windows-10-10.0.18362-SP0) + method: GET + uri: https://centraluseuap.api.cognitive.microsoft.com/formrecognizer/v2.0-preview/custom/models/39f91c56-f66f-407e-812c-3b9a001feacd?includeKeys=true + response: + body: + string: '{"modelInfo": {"modelId": "39f91c56-f66f-407e-812c-3b9a001feacd", "status": + "creating", "createdDateTime": "2020-06-15T19:04:52Z", "lastUpdatedDateTime": + "2020-06-15T19:04:52Z"}}' + headers: + apim-request-id: cc2ac788-855b-4635-a6ec-dc46e77e413a + content-type: application/json; charset=utf-8 + date: Mon, 15 Jun 2020 19:04:57 GMT + strict-transport-security: max-age=31536000; includeSubDomains; preload + transfer-encoding: chunked + x-content-type-options: nosniff + x-envoy-upstream-service-time: '148' + status: + code: 200 + message: OK + url: https://centraluseuap.api.cognitive.microsoft.com/formrecognizer/v2.0-preview/custom/models/39f91c56-f66f-407e-812c-3b9a001feacd?includeKeys=true +- request: + body: null + headers: + User-Agent: + - azsdk-python-ai-formrecognizer/1.0.0b4 Python/3.7.3 (Windows-10-10.0.18362-SP0) + Python/3.7.3 (Windows-10-10.0.18362-SP0) + method: GET + uri: https://centraluseuap.api.cognitive.microsoft.com/formrecognizer/v2.0-preview/custom/models/39f91c56-f66f-407e-812c-3b9a001feacd?includeKeys=true + response: + body: + string: '{"modelInfo": {"modelId": "39f91c56-f66f-407e-812c-3b9a001feacd", "status": + "creating", "createdDateTime": "2020-06-15T19:04:52Z", "lastUpdatedDateTime": + "2020-06-15T19:04:52Z"}}' + headers: + apim-request-id: 773a3922-676d-4781-af86-622449bdbc6e + content-type: application/json; charset=utf-8 + date: Mon, 15 Jun 2020 19:05:02 GMT + strict-transport-security: max-age=31536000; includeSubDomains; preload + transfer-encoding: chunked + x-content-type-options: nosniff + x-envoy-upstream-service-time: '160' + status: + code: 200 + message: OK + url: https://centraluseuap.api.cognitive.microsoft.com/formrecognizer/v2.0-preview/custom/models/39f91c56-f66f-407e-812c-3b9a001feacd?includeKeys=true +- request: + body: null + headers: + User-Agent: + - azsdk-python-ai-formrecognizer/1.0.0b4 Python/3.7.3 (Windows-10-10.0.18362-SP0) + Python/3.7.3 (Windows-10-10.0.18362-SP0) + method: GET + uri: https://centraluseuap.api.cognitive.microsoft.com/formrecognizer/v2.0-preview/custom/models/39f91c56-f66f-407e-812c-3b9a001feacd?includeKeys=true + response: + body: + string: '{"modelInfo": {"modelId": "39f91c56-f66f-407e-812c-3b9a001feacd", "status": + "creating", "createdDateTime": "2020-06-15T19:04:52Z", "lastUpdatedDateTime": + "2020-06-15T19:04:52Z"}}' + headers: + apim-request-id: a68288e9-29cf-4bc7-a9ba-3d4bddbc99b8 + content-type: application/json; charset=utf-8 + date: Mon, 15 Jun 2020 19:05:07 GMT + strict-transport-security: max-age=31536000; includeSubDomains; preload + transfer-encoding: chunked + x-content-type-options: nosniff + x-envoy-upstream-service-time: '150' + status: + code: 200 + message: OK + url: https://centraluseuap.api.cognitive.microsoft.com/formrecognizer/v2.0-preview/custom/models/39f91c56-f66f-407e-812c-3b9a001feacd?includeKeys=true +- request: + body: null + headers: + User-Agent: + - azsdk-python-ai-formrecognizer/1.0.0b4 Python/3.7.3 (Windows-10-10.0.18362-SP0) + Python/3.7.3 (Windows-10-10.0.18362-SP0) + method: GET + uri: https://centraluseuap.api.cognitive.microsoft.com/formrecognizer/v2.0-preview/custom/models/39f91c56-f66f-407e-812c-3b9a001feacd?includeKeys=true + response: + body: + string: '{"modelInfo": {"modelId": "39f91c56-f66f-407e-812c-3b9a001feacd", "status": + "creating", "createdDateTime": "2020-06-15T19:04:52Z", "lastUpdatedDateTime": + "2020-06-15T19:04:52Z"}}' + headers: + apim-request-id: 7ffff9b7-1c16-4832-9ddb-604fa1ebe317 + content-type: application/json; charset=utf-8 + date: Mon, 15 Jun 2020 19:05:13 GMT + strict-transport-security: max-age=31536000; includeSubDomains; preload + transfer-encoding: chunked + x-content-type-options: nosniff + x-envoy-upstream-service-time: '143' + status: + code: 200 + message: OK + url: https://centraluseuap.api.cognitive.microsoft.com/formrecognizer/v2.0-preview/custom/models/39f91c56-f66f-407e-812c-3b9a001feacd?includeKeys=true +- request: + body: null + headers: + User-Agent: + - azsdk-python-ai-formrecognizer/1.0.0b4 Python/3.7.3 (Windows-10-10.0.18362-SP0) + Python/3.7.3 (Windows-10-10.0.18362-SP0) + method: GET + uri: https://centraluseuap.api.cognitive.microsoft.com/formrecognizer/v2.0-preview/custom/models/39f91c56-f66f-407e-812c-3b9a001feacd?includeKeys=true + response: + body: + string: '{"modelInfo": {"modelId": "39f91c56-f66f-407e-812c-3b9a001feacd", "status": + "creating", "createdDateTime": "2020-06-15T19:04:52Z", "lastUpdatedDateTime": + "2020-06-15T19:04:52Z"}}' + headers: + apim-request-id: 083fbfac-88c4-4628-8c27-a6ee9f7423d4 + content-type: application/json; charset=utf-8 + date: Mon, 15 Jun 2020 19:05:18 GMT + strict-transport-security: max-age=31536000; includeSubDomains; preload + transfer-encoding: chunked + x-content-type-options: nosniff + x-envoy-upstream-service-time: '157' + status: + code: 200 + message: OK + url: https://centraluseuap.api.cognitive.microsoft.com/formrecognizer/v2.0-preview/custom/models/39f91c56-f66f-407e-812c-3b9a001feacd?includeKeys=true +- request: + body: null + headers: + User-Agent: + - azsdk-python-ai-formrecognizer/1.0.0b4 Python/3.7.3 (Windows-10-10.0.18362-SP0) + Python/3.7.3 (Windows-10-10.0.18362-SP0) + method: GET + uri: https://centraluseuap.api.cognitive.microsoft.com/formrecognizer/v2.0-preview/custom/models/39f91c56-f66f-407e-812c-3b9a001feacd?includeKeys=true + response: + body: + string: '{"modelInfo": {"modelId": "39f91c56-f66f-407e-812c-3b9a001feacd", "status": + "creating", "createdDateTime": "2020-06-15T19:04:52Z", "lastUpdatedDateTime": + "2020-06-15T19:04:52Z"}}' + headers: + apim-request-id: 1a10f67f-28a8-4749-8f4f-deea10dd0fe4 + content-type: application/json; charset=utf-8 + date: Mon, 15 Jun 2020 19:05:23 GMT + strict-transport-security: max-age=31536000; includeSubDomains; preload + transfer-encoding: chunked + x-content-type-options: nosniff + x-envoy-upstream-service-time: '58' + status: + code: 200 + message: OK + url: https://centraluseuap.api.cognitive.microsoft.com/formrecognizer/v2.0-preview/custom/models/39f91c56-f66f-407e-812c-3b9a001feacd?includeKeys=true +- request: + body: null + headers: + User-Agent: + - azsdk-python-ai-formrecognizer/1.0.0b4 Python/3.7.3 (Windows-10-10.0.18362-SP0) + Python/3.7.3 (Windows-10-10.0.18362-SP0) + method: GET + uri: https://centraluseuap.api.cognitive.microsoft.com/formrecognizer/v2.0-preview/custom/models/39f91c56-f66f-407e-812c-3b9a001feacd?includeKeys=true + response: + body: + string: '{"modelInfo": {"modelId": "39f91c56-f66f-407e-812c-3b9a001feacd", "status": + "creating", "createdDateTime": "2020-06-15T19:04:52Z", "lastUpdatedDateTime": + "2020-06-15T19:04:52Z"}}' + headers: + apim-request-id: 6af74830-a874-4da4-a9fa-9df635ed5a57 + content-type: application/json; charset=utf-8 + date: Mon, 15 Jun 2020 19:05:28 GMT + strict-transport-security: max-age=31536000; includeSubDomains; preload + transfer-encoding: chunked + x-content-type-options: nosniff + x-envoy-upstream-service-time: '189' + status: + code: 200 + message: OK + url: https://centraluseuap.api.cognitive.microsoft.com/formrecognizer/v2.0-preview/custom/models/39f91c56-f66f-407e-812c-3b9a001feacd?includeKeys=true +- request: + body: null + headers: + User-Agent: + - azsdk-python-ai-formrecognizer/1.0.0b4 Python/3.7.3 (Windows-10-10.0.18362-SP0) + Python/3.7.3 (Windows-10-10.0.18362-SP0) + method: GET + uri: https://centraluseuap.api.cognitive.microsoft.com/formrecognizer/v2.0-preview/custom/models/39f91c56-f66f-407e-812c-3b9a001feacd?includeKeys=true + response: + body: + string: '{"modelInfo": {"modelId": "39f91c56-f66f-407e-812c-3b9a001feacd", "status": + "creating", "createdDateTime": "2020-06-15T19:04:52Z", "lastUpdatedDateTime": + "2020-06-15T19:04:52Z"}}' + headers: + apim-request-id: ce288b5b-8fe3-41b8-a529-9bc86a8dc7e7 + content-type: application/json; charset=utf-8 + date: Mon, 15 Jun 2020 19:05:34 GMT + strict-transport-security: max-age=31536000; includeSubDomains; preload + transfer-encoding: chunked + x-content-type-options: nosniff + x-envoy-upstream-service-time: '174' + status: + code: 200 + message: OK + url: https://centraluseuap.api.cognitive.microsoft.com/formrecognizer/v2.0-preview/custom/models/39f91c56-f66f-407e-812c-3b9a001feacd?includeKeys=true +- request: + body: null + headers: + User-Agent: + - azsdk-python-ai-formrecognizer/1.0.0b4 Python/3.7.3 (Windows-10-10.0.18362-SP0) + Python/3.7.3 (Windows-10-10.0.18362-SP0) + method: GET + uri: https://centraluseuap.api.cognitive.microsoft.com/formrecognizer/v2.0-preview/custom/models/39f91c56-f66f-407e-812c-3b9a001feacd?includeKeys=true + response: + body: + string: '{"modelInfo": {"modelId": "39f91c56-f66f-407e-812c-3b9a001feacd", "status": + "creating", "createdDateTime": "2020-06-15T19:04:52Z", "lastUpdatedDateTime": + "2020-06-15T19:04:52Z"}}' + headers: + apim-request-id: cbafe13a-dc66-4764-ac0d-cabb961a068d + content-type: application/json; charset=utf-8 + date: Mon, 15 Jun 2020 19:05:39 GMT + strict-transport-security: max-age=31536000; includeSubDomains; preload + transfer-encoding: chunked + x-content-type-options: nosniff + x-envoy-upstream-service-time: '45' + status: + code: 200 + message: OK + url: https://centraluseuap.api.cognitive.microsoft.com/formrecognizer/v2.0-preview/custom/models/39f91c56-f66f-407e-812c-3b9a001feacd?includeKeys=true +- request: + body: null + headers: + User-Agent: + - azsdk-python-ai-formrecognizer/1.0.0b4 Python/3.7.3 (Windows-10-10.0.18362-SP0) + Python/3.7.3 (Windows-10-10.0.18362-SP0) + method: GET + uri: https://centraluseuap.api.cognitive.microsoft.com/formrecognizer/v2.0-preview/custom/models/39f91c56-f66f-407e-812c-3b9a001feacd?includeKeys=true + response: + body: + string: '{"modelInfo": {"modelId": "39f91c56-f66f-407e-812c-3b9a001feacd", "status": + "creating", "createdDateTime": "2020-06-15T19:04:52Z", "lastUpdatedDateTime": + "2020-06-15T19:04:52Z"}}' + headers: + apim-request-id: ab0a49dc-bbf7-405b-ba72-ef4a139e847e + content-type: application/json; charset=utf-8 + date: Mon, 15 Jun 2020 19:05:44 GMT + strict-transport-security: max-age=31536000; includeSubDomains; preload + transfer-encoding: chunked + x-content-type-options: nosniff + x-envoy-upstream-service-time: '202' + status: + code: 200 + message: OK + url: https://centraluseuap.api.cognitive.microsoft.com/formrecognizer/v2.0-preview/custom/models/39f91c56-f66f-407e-812c-3b9a001feacd?includeKeys=true +- request: + body: null + headers: + User-Agent: + - azsdk-python-ai-formrecognizer/1.0.0b4 Python/3.7.3 (Windows-10-10.0.18362-SP0) + Python/3.7.3 (Windows-10-10.0.18362-SP0) + method: GET + uri: https://centraluseuap.api.cognitive.microsoft.com/formrecognizer/v2.0-preview/custom/models/39f91c56-f66f-407e-812c-3b9a001feacd?includeKeys=true + response: + body: + string: '{"modelInfo": {"modelId": "39f91c56-f66f-407e-812c-3b9a001feacd", "status": + "creating", "createdDateTime": "2020-06-15T19:04:52Z", "lastUpdatedDateTime": + "2020-06-15T19:04:52Z"}}' + headers: + apim-request-id: e36d9517-1d97-4fa7-be87-53eb0a4bdbf6 + content-type: application/json; charset=utf-8 + date: Mon, 15 Jun 2020 19:05:50 GMT + strict-transport-security: max-age=31536000; includeSubDomains; preload + transfer-encoding: chunked + x-content-type-options: nosniff + x-envoy-upstream-service-time: '1001' + status: + code: 200 + message: OK + url: https://centraluseuap.api.cognitive.microsoft.com/formrecognizer/v2.0-preview/custom/models/39f91c56-f66f-407e-812c-3b9a001feacd?includeKeys=true +- request: + body: null + headers: + User-Agent: + - azsdk-python-ai-formrecognizer/1.0.0b4 Python/3.7.3 (Windows-10-10.0.18362-SP0) + Python/3.7.3 (Windows-10-10.0.18362-SP0) + method: GET + uri: https://centraluseuap.api.cognitive.microsoft.com/formrecognizer/v2.0-preview/custom/models/39f91c56-f66f-407e-812c-3b9a001feacd?includeKeys=true + response: + body: + string: '{"modelInfo": {"modelId": "39f91c56-f66f-407e-812c-3b9a001feacd", "status": + "creating", "createdDateTime": "2020-06-15T19:04:52Z", "lastUpdatedDateTime": + "2020-06-15T19:04:52Z"}}' + headers: + apim-request-id: 936bb6e2-6182-4899-b1f0-f485de49d07f + content-type: application/json; charset=utf-8 + date: Mon, 15 Jun 2020 19:05:55 GMT + strict-transport-security: max-age=31536000; includeSubDomains; preload + transfer-encoding: chunked + x-content-type-options: nosniff + x-envoy-upstream-service-time: '55' + status: + code: 200 + message: OK + url: https://centraluseuap.api.cognitive.microsoft.com/formrecognizer/v2.0-preview/custom/models/39f91c56-f66f-407e-812c-3b9a001feacd?includeKeys=true +- request: + body: null + headers: + User-Agent: + - azsdk-python-ai-formrecognizer/1.0.0b4 Python/3.7.3 (Windows-10-10.0.18362-SP0) + Python/3.7.3 (Windows-10-10.0.18362-SP0) + method: GET + uri: https://centraluseuap.api.cognitive.microsoft.com/formrecognizer/v2.0-preview/custom/models/39f91c56-f66f-407e-812c-3b9a001feacd?includeKeys=true + response: + body: + string: '{"modelInfo": {"modelId": "39f91c56-f66f-407e-812c-3b9a001feacd", "status": + "creating", "createdDateTime": "2020-06-15T19:04:52Z", "lastUpdatedDateTime": + "2020-06-15T19:04:52Z"}}' + headers: + apim-request-id: 42f73d9c-751c-4b12-9199-a6268775a5eb + content-type: application/json; charset=utf-8 + date: Mon, 15 Jun 2020 19:06:01 GMT + strict-transport-security: max-age=31536000; includeSubDomains; preload + transfer-encoding: chunked + x-content-type-options: nosniff + x-envoy-upstream-service-time: '53' + status: + code: 200 + message: OK + url: https://centraluseuap.api.cognitive.microsoft.com/formrecognizer/v2.0-preview/custom/models/39f91c56-f66f-407e-812c-3b9a001feacd?includeKeys=true +- request: + body: null + headers: + User-Agent: + - azsdk-python-ai-formrecognizer/1.0.0b4 Python/3.7.3 (Windows-10-10.0.18362-SP0) + Python/3.7.3 (Windows-10-10.0.18362-SP0) + method: GET + uri: https://centraluseuap.api.cognitive.microsoft.com/formrecognizer/v2.0-preview/custom/models/39f91c56-f66f-407e-812c-3b9a001feacd?includeKeys=true + response: + body: + string: '{"modelInfo": {"modelId": "39f91c56-f66f-407e-812c-3b9a001feacd", "status": + "creating", "createdDateTime": "2020-06-15T19:04:52Z", "lastUpdatedDateTime": + "2020-06-15T19:04:52Z"}}' + headers: + apim-request-id: 86718cdb-68a6-4134-9684-3084eeecc406 + content-type: application/json; charset=utf-8 + date: Mon, 15 Jun 2020 19:06:07 GMT + strict-transport-security: max-age=31536000; includeSubDomains; preload + transfer-encoding: chunked + x-content-type-options: nosniff + x-envoy-upstream-service-time: '1009' + status: + code: 200 + message: OK + url: https://centraluseuap.api.cognitive.microsoft.com/formrecognizer/v2.0-preview/custom/models/39f91c56-f66f-407e-812c-3b9a001feacd?includeKeys=true +- request: + body: null + headers: + User-Agent: + - azsdk-python-ai-formrecognizer/1.0.0b4 Python/3.7.3 (Windows-10-10.0.18362-SP0) + Python/3.7.3 (Windows-10-10.0.18362-SP0) + method: GET + uri: https://centraluseuap.api.cognitive.microsoft.com/formrecognizer/v2.0-preview/custom/models/39f91c56-f66f-407e-812c-3b9a001feacd?includeKeys=true + response: + body: + string: '{"modelInfo": {"modelId": "39f91c56-f66f-407e-812c-3b9a001feacd", "status": + "creating", "createdDateTime": "2020-06-15T19:04:52Z", "lastUpdatedDateTime": + "2020-06-15T19:04:52Z"}}' + headers: + apim-request-id: 0ed716e4-2620-4d7f-99f5-014715244fc4 + content-type: application/json; charset=utf-8 + date: Mon, 15 Jun 2020 19:06:12 GMT + strict-transport-security: max-age=31536000; includeSubDomains; preload + transfer-encoding: chunked + x-content-type-options: nosniff + x-envoy-upstream-service-time: '197' + status: + code: 200 + message: OK + url: https://centraluseuap.api.cognitive.microsoft.com/formrecognizer/v2.0-preview/custom/models/39f91c56-f66f-407e-812c-3b9a001feacd?includeKeys=true +- request: + body: null + headers: + User-Agent: + - azsdk-python-ai-formrecognizer/1.0.0b4 Python/3.7.3 (Windows-10-10.0.18362-SP0) + Python/3.7.3 (Windows-10-10.0.18362-SP0) + method: GET + uri: https://centraluseuap.api.cognitive.microsoft.com/formrecognizer/v2.0-preview/custom/models/39f91c56-f66f-407e-812c-3b9a001feacd?includeKeys=true + response: + body: + string: '{"modelInfo": {"modelId": "39f91c56-f66f-407e-812c-3b9a001feacd", "status": + "invalid", "createdDateTime": "2020-06-15T19:04:52Z", "lastUpdatedDateTime": + "2020-06-15T19:06:17Z"}, "keys": {"clusters": {}}, "trainResult": {"trainingDocuments": + [], "errors": [{"code": "2012", "message": "Unable to list blobs on the Azure + blob storage account."}]}}' + headers: + apim-request-id: 8d03ed67-430b-4b51-af2f-f008284854ce + content-type: application/json; charset=utf-8 + date: Mon, 15 Jun 2020 19:06:17 GMT + strict-transport-security: max-age=31536000; includeSubDomains; preload + transfer-encoding: chunked + x-content-type-options: nosniff + x-envoy-upstream-service-time: '50' + x-ms-cs-error-code: '2012' + status: + code: 200 + message: OK + url: https://centraluseuap.api.cognitive.microsoft.com/formrecognizer/v2.0-preview/custom/models/39f91c56-f66f-407e-812c-3b9a001feacd?includeKeys=true +version: 1 diff --git a/sdk/formrecognizer/azure-ai-formrecognizer/tests/test_content_from_url.py b/sdk/formrecognizer/azure-ai-formrecognizer/tests/test_content_from_url.py index caeef8752c99..6726de20b83f 100644 --- a/sdk/formrecognizer/azure-ai-formrecognizer/tests/test_content_from_url.py +++ b/sdk/formrecognizer/azure-ai-formrecognizer/tests/test_content_from_url.py @@ -20,6 +20,14 @@ class TestContentFromUrl(FormRecognizerTest): + @GlobalFormRecognizerAccountPreparer() + @GlobalClientPreparer() + def test_content_encoded_url(self, client): + try: + poller = client.begin_recognize_content_from_url("https://fakeuri.com/blank%20space") + except HttpResponseError as e: + self.assertIn("https://fakeuri.com/blank%20space", e.response.request.body) + @GlobalFormRecognizerAccountPreparer() def test_content_url_bad_endpoint(self, resource_group, location, form_recognizer_account, form_recognizer_account_key): with self.assertRaises(ServiceRequestError): diff --git a/sdk/formrecognizer/azure-ai-formrecognizer/tests/test_content_from_url_async.py b/sdk/formrecognizer/azure-ai-formrecognizer/tests/test_content_from_url_async.py index c40afea312f3..0c94b7a9fb66 100644 --- a/sdk/formrecognizer/azure-ai-formrecognizer/tests/test_content_from_url_async.py +++ b/sdk/formrecognizer/azure-ai-formrecognizer/tests/test_content_from_url_async.py @@ -21,6 +21,14 @@ class TestContentFromUrlAsync(AsyncFormRecognizerTest): + @GlobalFormRecognizerAccountPreparer() + @GlobalClientPreparer() + async def test_content_encoded_url(self, client): + try: + poller = await client.begin_recognize_content_from_url("https://fakeuri.com/blank%20space") + except HttpResponseError as e: + self.assertIn("https://fakeuri.com/blank%20space", e.response.request.body) + @GlobalFormRecognizerAccountPreparer() async def test_content_url_bad_endpoint(self, resource_group, location, form_recognizer_account, form_recognizer_account_key): with self.assertRaises(ServiceRequestError): diff --git a/sdk/formrecognizer/azure-ai-formrecognizer/tests/test_content_type.py b/sdk/formrecognizer/azure-ai-formrecognizer/tests/test_content_type.py index 1cd393cf3a58..f0c89a96bb9a 100644 --- a/sdk/formrecognizer/azure-ai-formrecognizer/tests/test_content_type.py +++ b/sdk/formrecognizer/azure-ai-formrecognizer/tests/test_content_type.py @@ -44,13 +44,17 @@ def test_png_bytes(self): content_type = get_content_type(myfile) self.assertEqual(content_type, "image/png") - def test_tiff(self): + def test_tiff_little_endian(self): with open(self.invoice_tiff, "rb") as fd: content_type = get_content_type(fd) self.assertEqual(content_type, "image/tiff") - def test_tiff_bytes(self): + def test_tiff_little_endian_bytes(self): with open(self.invoice_tiff, "rb") as fd: myfile = fd.read() content_type = get_content_type(myfile) self.assertEqual(content_type, "image/tiff") + + def test_tiff_big_endian(self): + content_type = get_content_type(b"\x4D\x4D\x00\x2A") + self.assertEqual(content_type, "image/tiff") diff --git a/sdk/formrecognizer/azure-ai-formrecognizer/tests/test_copy_model.py b/sdk/formrecognizer/azure-ai-formrecognizer/tests/test_copy_model.py index 8904bbe29745..97413a8ce155 100644 --- a/sdk/formrecognizer/azure-ai-formrecognizer/tests/test_copy_model.py +++ b/sdk/formrecognizer/azure-ai-formrecognizer/tests/test_copy_model.py @@ -66,6 +66,21 @@ def test_copy_model_fail(self, client, container_sas_url, location, resource_id) poller = client.begin_copy_model(model.model_id, target=target) copy = poller.result() + @GlobalFormRecognizerAccountPreparer() + @GlobalClientPreparer(training=True, copy=True) + def test_copy_model_fail_bad_model_id(self, client, container_sas_url, location, resource_id): + pytest.skip("service team will tell us when to enable this test") + + poller = client.begin_training(container_sas_url, use_training_labels=False) + model = poller.result() + + target = client.get_copy_authorization(resource_region=location, resource_id=resource_id) + + with self.assertRaises(HttpResponseError): + # give bad model_id + poller = client.begin_copy_model("00000000-0000-0000-0000-000000000000", target=target) + copy = poller.result() + @GlobalFormRecognizerAccountPreparer() @GlobalClientPreparer(training=True, copy=True) def test_copy_model_transform(self, client, container_sas_url, location, resource_id): diff --git a/sdk/formrecognizer/azure-ai-formrecognizer/tests/test_copy_model_async.py b/sdk/formrecognizer/azure-ai-formrecognizer/tests/test_copy_model_async.py index 18ff025e80de..0aeb818d1c12 100644 --- a/sdk/formrecognizer/azure-ai-formrecognizer/tests/test_copy_model_async.py +++ b/sdk/formrecognizer/azure-ai-formrecognizer/tests/test_copy_model_async.py @@ -67,6 +67,21 @@ async def test_copy_model_fail(self, client, container_sas_url, location, resour poller = await client.begin_copy_model(model.model_id, target=target) copy = await poller.result() + @GlobalFormRecognizerAccountPreparer() + @GlobalClientPreparer(training=True, copy=True) + async def test_copy_model_fail_bad_model_id(self, client, container_sas_url, location, resource_id): + pytest.skip("service team will tell us when to enable this test") + + poller = await client.begin_training(container_sas_url, use_training_labels=False) + model = await poller.result() + + target = await client.get_copy_authorization(resource_region=location, resource_id=resource_id) + + with self.assertRaises(HttpResponseError): + # give bad model_id + poller = await client.begin_copy_model("00000000-0000-0000-0000-000000000000", target=target) + copy = await poller.result() + @GlobalFormRecognizerAccountPreparer() @GlobalClientPreparer(training=True, copy=True) async def test_copy_model_transform(self, client, container_sas_url, location, resource_id): diff --git a/sdk/formrecognizer/azure-ai-formrecognizer/tests/test_custom_forms.py b/sdk/formrecognizer/azure-ai-formrecognizer/tests/test_custom_forms.py index 752d24666cf1..babe62dcae11 100644 --- a/sdk/formrecognizer/azure-ai-formrecognizer/tests/test_custom_forms.py +++ b/sdk/formrecognizer/azure-ai-formrecognizer/tests/test_custom_forms.py @@ -79,6 +79,48 @@ def test_custom_form_damaged_file(self, client, container_sas_url): ) form = poller.result() + @GlobalFormRecognizerAccountPreparer() + @GlobalClientPreparer(training=True) + def test_custom_form_unlabeled_blank_page(self, client, container_sas_url): + fr_client = client.get_form_recognizer_client() + + poller = client.begin_training(container_sas_url, use_training_labels=False) + model = poller.result() + + with open(self.blank_pdf, "rb") as fd: + blank = fd.read() + poller = fr_client.begin_recognize_custom_forms( + model.model_id, + blank + ) + form = poller.result() + + self.assertEqual(len(form), 1) + self.assertEqual(form[0].page_range.first_page_number, 1) + self.assertEqual(form[0].page_range.last_page_number, 1) + self.assertIsNotNone(form[0].pages) + + @GlobalFormRecognizerAccountPreparer() + @GlobalClientPreparer(training=True) + def test_custom_form_labeled_blank_page(self, client, container_sas_url): + fr_client = client.get_form_recognizer_client() + + poller = client.begin_training(container_sas_url, use_training_labels=True) + model = poller.result() + + with open(self.blank_pdf, "rb") as fd: + blank = fd.read() + poller = fr_client.begin_recognize_custom_forms( + model.model_id, + blank + ) + form = poller.result() + + self.assertEqual(len(form), 1) + self.assertEqual(form[0].page_range.first_page_number, 1) + self.assertEqual(form[0].page_range.last_page_number, 1) + self.assertIsNotNone(form[0].pages) + @GlobalFormRecognizerAccountPreparer() @GlobalClientPreparer(training=True) def test_custom_form_unlabeled(self, client, container_sas_url): diff --git a/sdk/formrecognizer/azure-ai-formrecognizer/tests/test_custom_forms_async.py b/sdk/formrecognizer/azure-ai-formrecognizer/tests/test_custom_forms_async.py index 7b08ea902f42..575125c77d10 100644 --- a/sdk/formrecognizer/azure-ai-formrecognizer/tests/test_custom_forms_async.py +++ b/sdk/formrecognizer/azure-ai-formrecognizer/tests/test_custom_forms_async.py @@ -87,6 +87,48 @@ async def test_custom_form_damaged_file(self, client, container_sas_url): ) result = await poller.result() + @GlobalFormRecognizerAccountPreparer() + @GlobalClientPreparer(training=True) + async def test_custom_form_unlabeled_blank_page(self, client, container_sas_url): + fr_client = client.get_form_recognizer_client() + + poller = await client.begin_training(container_sas_url, use_training_labels=False) + model = await poller.result() + + with open(self.blank_pdf, "rb") as fd: + blank = fd.read() + poller = await fr_client.begin_recognize_custom_forms( + model.model_id, + blank + ) + form = await poller.result() + + self.assertEqual(len(form), 1) + self.assertEqual(form[0].page_range.first_page_number, 1) + self.assertEqual(form[0].page_range.last_page_number, 1) + self.assertIsNotNone(form[0].pages) + + @GlobalFormRecognizerAccountPreparer() + @GlobalClientPreparer(training=True) + async def test_custom_form_labeled_blank_page(self, client, container_sas_url): + fr_client = client.get_form_recognizer_client() + + poller = await client.begin_training(container_sas_url, use_training_labels=True) + model = await poller.result() + + with open(self.blank_pdf, "rb") as fd: + blank = fd.read() + poller = await fr_client.begin_recognize_custom_forms( + model.model_id, + blank + ) + form = await poller.result() + + self.assertEqual(len(form), 1) + self.assertEqual(form[0].page_range.first_page_number, 1) + self.assertEqual(form[0].page_range.last_page_number, 1) + self.assertIsNotNone(form[0].pages) + @GlobalFormRecognizerAccountPreparer() @GlobalClientPreparer(training=True) async def test_custom_form_unlabeled(self, client, container_sas_url): diff --git a/sdk/formrecognizer/azure-ai-formrecognizer/tests/test_custom_forms_from_url.py b/sdk/formrecognizer/azure-ai-formrecognizer/tests/test_custom_forms_from_url.py index de974ac79afd..c23cddbe848f 100644 --- a/sdk/formrecognizer/azure-ai-formrecognizer/tests/test_custom_forms_from_url.py +++ b/sdk/formrecognizer/azure-ai-formrecognizer/tests/test_custom_forms_from_url.py @@ -20,6 +20,17 @@ class TestCustomFormsFromUrl(FormRecognizerTest): + @GlobalFormRecognizerAccountPreparer() + def test_custom_forms_encoded_url(self, resource_group, location, form_recognizer_account, form_recognizer_account_key): + client = FormRecognizerClient(form_recognizer_account, AzureKeyCredential(form_recognizer_account_key)) + try: + poller = client.begin_recognize_custom_forms_from_url( + model_id="00000000-0000-0000-0000-000000000000", + form_url="https://fakeuri.com/blank%20space" + ) + except HttpResponseError as e: + self.assertIn("https://fakeuri.com/blank%20space", e.response.request.body) + @GlobalFormRecognizerAccountPreparer() def test_custom_form_none_model_id(self, resource_group, location, form_recognizer_account, form_recognizer_account_key): client = FormRecognizerClient(form_recognizer_account, AzureKeyCredential(form_recognizer_account_key)) diff --git a/sdk/formrecognizer/azure-ai-formrecognizer/tests/test_custom_forms_from_url_async.py b/sdk/formrecognizer/azure-ai-formrecognizer/tests/test_custom_forms_from_url_async.py index da5a03505a28..abdde34862dc 100644 --- a/sdk/formrecognizer/azure-ai-formrecognizer/tests/test_custom_forms_from_url_async.py +++ b/sdk/formrecognizer/azure-ai-formrecognizer/tests/test_custom_forms_from_url_async.py @@ -21,6 +21,17 @@ class TestCustomFormsFromUrlAsync(AsyncFormRecognizerTest): + @GlobalFormRecognizerAccountPreparer() + async def test_custom_forms_encoded_url(self, resource_group, location, form_recognizer_account, form_recognizer_account_key): + client = FormRecognizerClient(form_recognizer_account, AzureKeyCredential(form_recognizer_account_key)) + try: + poller = await client.begin_recognize_custom_forms_from_url( + model_id="00000000-0000-0000-0000-000000000000", + form_url="https://fakeuri.com/blank%20space" + ) + except HttpResponseError as e: + self.assertIn("https://fakeuri.com/blank%20space", e.response.request.body) + @GlobalFormRecognizerAccountPreparer() async def test_custom_form_none_model_id(self, resource_group, location, form_recognizer_account, form_recognizer_account_key): client = FormRecognizerClient(form_recognizer_account, AzureKeyCredential(form_recognizer_account_key)) diff --git a/sdk/formrecognizer/azure-ai-formrecognizer/tests/test_receipt_from_url.py b/sdk/formrecognizer/azure-ai-formrecognizer/tests/test_receipt_from_url.py index c3f6945d9190..18a97f0f03e2 100644 --- a/sdk/formrecognizer/azure-ai-formrecognizer/tests/test_receipt_from_url.py +++ b/sdk/formrecognizer/azure-ai-formrecognizer/tests/test_receipt_from_url.py @@ -42,6 +42,14 @@ def test_active_directory_auth(self): result = poller.result() self.assertIsNotNone(result) + @GlobalFormRecognizerAccountPreparer() + @GlobalClientPreparer() + def test_receipts_encoded_url(self, client): + try: + poller = client.begin_recognize_receipts_from_url("https://fakeuri.com/blank%20space") + except HttpResponseError as e: + self.assertIn("https://fakeuri.com/blank%20space", e.response.request.body) + @GlobalFormRecognizerAccountPreparer() def test_receipt_url_bad_endpoint(self, resource_group, location, form_recognizer_account, form_recognizer_account_key): with self.assertRaises(ServiceRequestError): diff --git a/sdk/formrecognizer/azure-ai-formrecognizer/tests/test_receipt_from_url_async.py b/sdk/formrecognizer/azure-ai-formrecognizer/tests/test_receipt_from_url_async.py index 6770833364f7..5b8911aa4f84 100644 --- a/sdk/formrecognizer/azure-ai-formrecognizer/tests/test_receipt_from_url_async.py +++ b/sdk/formrecognizer/azure-ai-formrecognizer/tests/test_receipt_from_url_async.py @@ -46,6 +46,14 @@ async def test_active_directory_auth_async(self): result = await poller.result() self.assertIsNotNone(result) + @GlobalFormRecognizerAccountPreparer() + @GlobalClientPreparer() + async def test_receipts_encoded_url(self, client): + try: + poller = await client.begin_recognize_receipts_from_url("https://fakeuri.com/blank%20space") + except HttpResponseError as e: + self.assertIn("https://fakeuri.com/blank%20space", e.response.request.body) + @GlobalFormRecognizerAccountPreparer() async def test_receipt_url_bad_endpoint(self, resource_group, location, form_recognizer_account, form_recognizer_account_key): with self.assertRaises(ServiceRequestError): diff --git a/sdk/formrecognizer/azure-ai-formrecognizer/tests/test_training.py b/sdk/formrecognizer/azure-ai-formrecognizer/tests/test_training.py index 5810557853da..29a547a23480 100644 --- a/sdk/formrecognizer/azure-ai-formrecognizer/tests/test_training.py +++ b/sdk/formrecognizer/azure-ai-formrecognizer/tests/test_training.py @@ -36,6 +36,17 @@ def check_poll_value(poll): poller2.wait() check_poll_value(poller2._polling_method._timeout) # goes back to client default + @GlobalFormRecognizerAccountPreparer() + @GlobalClientPreparer() + def test_training_encoded_url(self, client): + with self.assertRaises(HttpResponseError): + poller = client.begin_training( + training_files_url="https://fakeuri.com/blank%20space", + use_training_labels=False + ) + self.assertIn("https://fakeuri.com/blank%20space", poller._polling_method._initial_response.http_request.body) + poller.wait() + @GlobalFormRecognizerAccountPreparer() def test_training_auth_bad_key(self, resource_group, location, form_recognizer_account, form_recognizer_account_key): client = FormTrainingClient(form_recognizer_account, AzureKeyCredential("xxxx")) diff --git a/sdk/formrecognizer/azure-ai-formrecognizer/tests/test_training_async.py b/sdk/formrecognizer/azure-ai-formrecognizer/tests/test_training_async.py index 87ba872fd296..ddba4551403f 100644 --- a/sdk/formrecognizer/azure-ai-formrecognizer/tests/test_training_async.py +++ b/sdk/formrecognizer/azure-ai-formrecognizer/tests/test_training_async.py @@ -37,6 +37,17 @@ def check_poll_value(poll): await poller2.wait() check_poll_value(poller2._polling_method._timeout) # goes back to client default + @GlobalFormRecognizerAccountPreparer() + @GlobalClientPreparer() + async def test_training_encoded_url(self, client): + with self.assertRaises(HttpResponseError): + poller = await client.begin_training( + training_files_url="https://fakeuri.com/blank%20space", + use_training_labels=False + ) + self.assertIn("https://fakeuri.com/blank%20space", poller._polling_method._initial_response.http_request.body) + await poller.wait() + @GlobalFormRecognizerAccountPreparer() async def test_training_auth_bad_key(self, resource_group, location, form_recognizer_account, form_recognizer_account_key): client = FormTrainingClient(form_recognizer_account, AzureKeyCredential("xxxx")) From fddce40f40d0d5384ae0f5c4003d996badd4cde0 Mon Sep 17 00:00:00 2001 From: Charles Lowell Date: Wed, 17 Jun 2020 10:25:08 -0700 Subject: [PATCH 18/21] Update Key Vault minimum azure-core to 1.4.0 (#12074) --- sdk/keyvault/azure-keyvault-certificates/CHANGELOG.md | 2 +- sdk/keyvault/azure-keyvault-certificates/setup.py | 2 +- sdk/keyvault/azure-keyvault-keys/CHANGELOG.md | 1 + sdk/keyvault/azure-keyvault-keys/setup.py | 2 +- sdk/keyvault/azure-keyvault-secrets/CHANGELOG.md | 2 +- sdk/keyvault/azure-keyvault-secrets/setup.py | 2 +- shared_requirements.txt | 6 +++--- 7 files changed, 9 insertions(+), 8 deletions(-) diff --git a/sdk/keyvault/azure-keyvault-certificates/CHANGELOG.md b/sdk/keyvault/azure-keyvault-certificates/CHANGELOG.md index a137663b2a28..cfcc7560a734 100644 --- a/sdk/keyvault/azure-keyvault-certificates/CHANGELOG.md +++ b/sdk/keyvault/azure-keyvault-certificates/CHANGELOG.md @@ -1,7 +1,7 @@ # Release History ## 4.2.0b2 (Unreleased) - +- Updated minimum `azure-core` version to 1.4.0 ## 4.2.0b1 (2020-03-10) - Support for Key Vault API version 7.1-preview diff --git a/sdk/keyvault/azure-keyvault-certificates/setup.py b/sdk/keyvault/azure-keyvault-certificates/setup.py index 1eaefc9b5704..3585c09becb8 100644 --- a/sdk/keyvault/azure-keyvault-certificates/setup.py +++ b/sdk/keyvault/azure-keyvault-certificates/setup.py @@ -79,7 +79,7 @@ "azure.keyvault", ] ), - install_requires=["azure-core<2.0.0,>=1.2.1", "msrest>=0.6.0"], + install_requires=["azure-core<2.0.0,>=1.4.0", "msrest>=0.6.0"], extras_require={ ":python_version<'3.0'": ["azure-keyvault-nspkg"], ":python_version<'3.4'": ["enum34>=1.0.4"], diff --git a/sdk/keyvault/azure-keyvault-keys/CHANGELOG.md b/sdk/keyvault/azure-keyvault-keys/CHANGELOG.md index 5812490d2289..09e6365abe27 100644 --- a/sdk/keyvault/azure-keyvault-keys/CHANGELOG.md +++ b/sdk/keyvault/azure-keyvault-keys/CHANGELOG.md @@ -1,6 +1,7 @@ # Release History ## 4.2.0b2 (Unreleased) +- Updated minimum `azure-core` version to 1.4.0 - `CryptographyClient` will no longer perform encrypt or wrap operations when its key has expired or is not yet valid. diff --git a/sdk/keyvault/azure-keyvault-keys/setup.py b/sdk/keyvault/azure-keyvault-keys/setup.py index d752a4f1646a..5c71d5207c9a 100644 --- a/sdk/keyvault/azure-keyvault-keys/setup.py +++ b/sdk/keyvault/azure-keyvault-keys/setup.py @@ -79,7 +79,7 @@ "azure.keyvault", ] ), - install_requires=["azure-core<2.0.0,>=1.2.1", "cryptography>=2.1.4", "msrest>=0.6.0"], + install_requires=["azure-core<2.0.0,>=1.4.0", "cryptography>=2.1.4", "msrest>=0.6.0"], extras_require={ ":python_version<'3.0'": ["azure-keyvault-nspkg"], ":python_version<'3.4'": ["enum34>=1.0.4"], diff --git a/sdk/keyvault/azure-keyvault-secrets/CHANGELOG.md b/sdk/keyvault/azure-keyvault-secrets/CHANGELOG.md index 2571f01a90cd..206b9feea689 100644 --- a/sdk/keyvault/azure-keyvault-secrets/CHANGELOG.md +++ b/sdk/keyvault/azure-keyvault-secrets/CHANGELOG.md @@ -1,7 +1,7 @@ # Release History ## 4.2.0b2 (Unreleased) - +- Updated minimum `azure-core` version to 1.4.0 ## 4.2.0b1 (2020-03-10) - Support for Key Vault API version 7.1-preview diff --git a/sdk/keyvault/azure-keyvault-secrets/setup.py b/sdk/keyvault/azure-keyvault-secrets/setup.py index 4bdd3c4111ee..6d138dca8801 100644 --- a/sdk/keyvault/azure-keyvault-secrets/setup.py +++ b/sdk/keyvault/azure-keyvault-secrets/setup.py @@ -79,7 +79,7 @@ "azure.keyvault", ] ), - install_requires=["azure-core<2.0.0,>=1.2.1", "msrest>=0.6.0"], + install_requires=["azure-core<2.0.0,>=1.4.0", "msrest>=0.6.0"], extras_require={ ":python_version<'3.0'": ["azure-keyvault-nspkg"], ":python_version<'3.4'": ["enum34>=1.0.4"], diff --git a/shared_requirements.txt b/shared_requirements.txt index 215ced1ea7a6..70acf0e54fe9 100644 --- a/shared_requirements.txt +++ b/shared_requirements.txt @@ -119,9 +119,9 @@ six>=1.6 #override azure-keyvault-certificates msrest>=0.6.0 #override azure-keyvault-keys msrest>=0.6.0 #override azure-keyvault-secrets msrest>=0.6.0 -#override azure-keyvault-certificates azure-core<2.0.0,>=1.2.1 -#override azure-keyvault-keys azure-core<2.0.0,>=1.2.1 -#override azure-keyvault-secrets azure-core<2.0.0,>=1.2.1 +#override azure-keyvault-certificates azure-core<2.0.0,>=1.4.0 +#override azure-keyvault-keys azure-core<2.0.0,>=1.4.0 +#override azure-keyvault-secrets azure-core<2.0.0,>=1.4.0 #override azure-ai-textanalytics msrest>=0.6.0 #override azure-ai-textanalytics azure-core<2.0.0,>=1.4.0 #override azure-search-documents azure-core<2.0.0,>=1.4.0 From 02275b1b76d2b8956fb5c528d064c3b65302bd80 Mon Sep 17 00:00:00 2001 From: Xiang Yan Date: Wed, 17 Jun 2020 11:24:46 -0700 Subject: [PATCH 19/21] readme & sample updates (#12095) --- sdk/search/azure-search-documents/README.md | 334 ++++++++++++------ .../documents/indexes/_internal/_utils.py | 6 +- .../sample_data_source_operations_async.py | 2 +- .../sample_index_crud_operations_async.py | 2 +- .../sample_indexers_operations_async.py | 28 +- .../sample_synonym_map_operations_async.py | 4 +- .../samples/sample_index_crud_operations.py | 4 +- .../samples/sample_indexers_operations.py | 16 +- .../samples/sample_synonym_map_operations.py | 4 +- 9 files changed, 262 insertions(+), 138 deletions(-) diff --git a/sdk/search/azure-search-documents/README.md b/sdk/search/azure-search-documents/README.md index b9d00d8b7c88..dcd717ccf93e 100644 --- a/sdk/search/azure-search-documents/README.md +++ b/sdk/search/azure-search-documents/README.md @@ -1,6 +1,38 @@ # Azure Cognitive Search client library for Python -Azure Cognitive Search is a fully managed cloud search service that provides a rich search experience to custom applications. +[Azure Cognitive Search](https://docs.microsoft.com/azure/search/) is a +search-as-a-service cloud solution that gives developers APIs and tools +for adding a rich search experience over private, heterogeneous content +in web, mobile, and enterprise applications. + +The Azure Cognitive Search service is well suited for the following + application scenarios: + +* Consolidate varied content types into a single searchable index. + To populate an index, you can push JSON documents that contain your content, + or if your data is already in Azure, create an indexer to pull in data + automatically. +* Attach skillsets to an indexer to create searchable content from images + and large text documents. A skillset leverages AI from Cognitive Services + for built-in OCR, entity recognition, key phrase extraction, language + detection, text translation, and sentiment analysis. You can also add + custom skills to integrate external processing of your content during + data ingestion. +* In a search client application, implement query logic and user experiences + similar to commercial web search engines. + +Use the Azure.Search.Documents client library to: + +* Submit queries for simple and advanced query forms that include fuzzy + search, wildcard search, regular expressions. +* Implement filtered queries for faceted navigation, geospatial search, + or to narrow results based on filter criteria. +* Create and manage search indexes. +* Upload and update documents in the search index. +* Create and manage indexers that pull data from Azure into an index. +* Create and manage skillsets that add AI enrichment to data ingestion. +* Create and manage analyzers for advanced text analysis or multi-lingual content. +* Optimize results through scoring profiles to factor in business logic or freshness. [Source code](https://github.com/Azure/azure-sdk-for-python/tree/master/sdk/search/azure-search-documents) | [Package (PyPI)](https://pypi.org/project/azure-search-documents/) | @@ -22,172 +54,197 @@ pip install azure-search-documents --pre ### Prerequisites * Python 2.7, or 3.5 or later is required to use this package. -* You must have an [Azure subscription][azure_sub] and an existing +* You need an [Azure subscription][azure_sub] and a [Azure Cognitive Search service][search_resource] to use this package. -If you need to create the resource, you can use the [Azure portal][create_search_service_docs], [Azure PowerShell][create_search_service_ps], or the [Azure CLI][create_search_service_cli]. +To create a new search service, you can use the [Azure portal][create_search_service_docs], [Azure PowerShell][create_search_service_ps], or the [Azure CLI][create_search_service_cli]. -If you use the Azure CLI, replace `` and `` with your own unique names: - -```PowerShell -az search service create --resource-group --name --sku Standard +```Powershell +az search service create --name --resource-group --sku free --location westus ``` -The above creates a resource with the "Standard" pricing tier. See [choosing a pricing tier](https://docs.microsoft.com/en-us/azure/search/search-sku-tier) for more information. +See [choosing a pricing tier](https://docs.microsoft.com/azure/search/search-sku-tier) + for more information about available options. ### Authenticate the client -In order to interact with the Cognitive Search service you'll need to create an instance of the Search Client class. -To make this possible you will need an [api-key of the Cognitive Search service](https://docs.microsoft.com/en-us/azure/search/search-security-api-keys). - -The SDK provides three clients. +All requests to a search service need an api-key that was generated specifically +for your service. [The api-key is the sole mechanism for authenticating access to +your search service endpoint.](https://docs.microsoft.com/azure/search/search-security-api-keys) +You can obtain your api-key from the +[Azure portal](https://portal.azure.com/) or via the Azure CLI: -1. SearchClient for all document operations. -2. SearchIndexClient for all CRUD operations on index resources. -3. SearchIndexerClient for all CRUD operations on indexer resources. +```Powershell +az search admin-key show --service-name --resource-group +``` -#### Create a SearchClient +There are two types of keys used to access your search service: **admin** +*(read-write)* and **query** *(read-only)* keys. Restricting access and +operations in client apps is essential to safeguarding the search assets on your +service. Always use a query key rather than an admin key for any query +originating from a client app. -To create a SearchClient, you will need an existing index name as well as the values of the Cognitive Search Service -[service endpoint](https://docs.microsoft.com/en-us/azure/search/search-create-service-portal#get-a-key-and-url-endpoint) and -[api key](https://docs.microsoft.com/en-us/azure/search/search-security-api-keys). -Note that you will need an admin key to index documents (query keys only work for queries). +*Note: The example Azure CLI snippet above retrieves an admin key so it's easier +to get started exploring APIs, but it should be managed carefully.* +We can use the api-key to create a new `SearchClient`. ```python +import os from azure.core.credentials import AzureKeyCredential from azure.search.documents import SearchClient -credential = AzureKeyCredential("") +index_name = "nycjobs"; +# Get the service endpoint and API key from the environment +endpoint = os.environ["SEARCH_ENDPOINT"] +key = os.environ["SEARCH_API_KEY"] -client = SearchClient(endpoint="", - index_name="", +# Create a client +credential = AzureKeyCredential(key) +client = SearchClient(endpoint=endpoint, + index_name=index_name, credential=credential) ``` -#### Create a SearchIndexClient - -Once you have the values of the Cognitive Search Service [service endpoint](https://docs.microsoft.com/en-us/azure/search/search-create-service-portal#get-a-key-and-url-endpoint) -and [api key](https://docs.microsoft.com/en-us/azure/search/search-security-api-keys) you can create the Search Index client: - -```python -from azure.core.credentials import AzureKeyCredential -from azure.search.documents.indexes import SearchIndexClient -credential = AzureKeyCredential("") - -client = SearchIndexClient(endpoint="", - credential=credential) -``` +### Send your first search request -#### Create a SearchIndexerClient +To get running immediately, we're going to connect to a well known sandbox +Search service provided by Microsoft. This means you do not need an Azure +subscription or Azure Cognitive Search service to try out this query. -Once you have the values of the Cognitive Search Service [service endpoint](https://docs.microsoft.com/en-us/azure/search/search-create-service-portal#get-a-key-and-url-endpoint) -and [api key](https://docs.microsoft.com/en-us/azure/search/search-security-api-keys) you can create the Search Indexer client: ```python from azure.core.credentials import AzureKeyCredential -from azure.search.documents.indexes import SearchIndexerClient - -credential = AzureKeyCredential("") - -client = SearchIndexerClient(endpoint="", - credential=credential) -``` +from azure.search.documents import SearchClient -### Send your first search request +# We'll connect to the Azure Cognitive Search public sandbox and send a +# query to its "nycjobs" index built from a public dataset of available jobs +# in New York. +service_name = "azs-playground" +index_name = "nycjobs" +api_key = "252044BE3886FE4A8E3BAA4F595114BB" + +# Create a SearchClient to send queries +endpoint = "https://{}.search.windows.net/".format(service_name) +credential = AzureKeyCredential(api_key) +client = SearchClient(endpoint=endpoint, + index_name=index_name, + credential=credential) -You can use the `SearchClient` you created in the first section above to make a basic search request: -```python -results = client.search(search_text="spa") +# Let's get the top 5 jobs related to Microsoft +results = client.search(search_text="Microsoft", include_total_result_count=5) -print("Hotels containing 'spa' in the name (or other fields):") for result in results: - print(" Name: {} (rating {})".format(result["HotelName"], result["Rating"])) + # Print out the title and job description + print("{}\n{}\n)".format(result["business_title"], result["job_description"])) ``` ## Key concepts -Azure Cognitive Search has the concepts of search services and indexes and documents, where a search service contains -one or more indexes that provides persistent storage of searchable data, and data is loaded in the form of JSON documents. -Data can be pushed to an index from an external data source, but if you use an indexer, it's possible to crawl a data -source to extract and load data into an index. - -There are several types of operations that can be executed against the service: - -- **Index management operations** Create, delete, update, or configure a search index. ([API Reference](https://azuresdkdocs.blob.core.windows.net/$web/python/azure-search-documents/latest/azure.search.documents.html#azure.search.documents.SearchIndexesClient), [Service Docs](https://docs.microsoft.com/en-us/rest/api/searchservice/index-operations)) -- **Document operations** Add, update, or delete documents in the index, query the index, or look up specific documents by ID. ([API Reference](https://azuresdkdocs.blob.core.windows.net/$web/python/azure-search-documents/latest/azure.search.documents.html#azure.search.documents.SearchClient), [Service Docs](https://docs.microsoft.com/en-us/rest/api/searchservice/document-operations)) -- **Datasource operations** Create, delete, update, or configure data sources for Search Indexers ([API Reference](https://azuresdkdocs.blob.core.windows.net/$web/python/azure-search-documents/latest/azure.search.documents.html#azure.search.documents.SearchDataSourcesClient), [Service Docs](https://docs.microsoft.com/en-us/rest/api/searchservice/indexer-operations)) -- **Indexer operations** Automate aspects of an indexing operation by configuring a data source and an indexer that you can schedule or run on demand. This feature is supported for a limited number of data source types. ([API Reference](https://azuresdkdocs.blob.core.windows.net/$web/python/azure-search-documents/latest/azure.search.documents.html#azure.search.documents.SearchIndexersClient), [Service Docs](https://docs.microsoft.com/en-us/rest/api/searchservice/indexer-operations)) -- **Skillset operations** Part of a cognitive search workload, a skillset defines a series of a series of enrichment processing steps. A skillset is consumed by an indexer. ([API Reference](https://azuresdkdocs.blob.core.windows.net/$web/python/azure-search-documents/latest/azure.search.documents.html#azure.search.documents.SearchSkillsetsClient), [Service Docs](https://docs.microsoft.com/en-us/rest/api/searchservice/skillset-operations)) -- **Synonym map operations** A synonym map is a service-level resource that contains user-defined synonyms. This resource is maintained independently from search indexes. Once uploaded, you can point any searchable field to the synonym map (one per field). ([API Reference](https://azuresdkdocs.blob.core.windows.net/$web/python/azure-search-documents/latest/azure.search.documents.html#azure.search.documents.SearchSynonymMapsClient), [Service Docs](https://docs.microsoft.com/en-us/rest/api/searchservice/synonym-map-operations)) +An Azure Cognitive Search service contains one or more indexes that provide +persistent storage of searchable data in the form of JSON documents. _(If +you're brand new to search, you can make a very rough analogy between +indexes and database tables.)_ The Azure.Search.Documents client library +exposes operations on these resources through two main client types. + +* `SearchClient` helps with: + * [Searching](https://docs.microsoft.com/azure/search/search-lucene-query-architecture) + your indexed documents using + [rich queries](https://docs.microsoft.com/azure/search/search-query-overview) + and [powerful data shaping](https://docs.microsoft.com/azure/search/search-filters) + * [Autocompleting](https://docs.microsoft.com/rest/api/searchservice/autocomplete) + partially typed search terms based on documents in the index + * [Suggesting](https://docs.microsoft.com/rest/api/searchservice/suggestions) + the most likely matching text in documents as a user types + * [Adding, Updating or Deleting Documents](https://docs.microsoft.com/rest/api/searchservice/addupdate-or-delete-documents) + documents from an index + +* `SearchIndexClient` allows you to: + * [Create, delete, update, or configure a search index](https://docs.microsoft.com/rest/api/searchservice/index-operations) + * [Declare custom synonym maps to expand or rewrite queries](https://docs.microsoft.com/rest/api/searchservice/synonym-map-operations) + * Most of the `SearchServiceClient` functionality is not yet available in our current preview + +* `SearchIndexerClient` allows you to: + * [Start indexers to automatically crawl data sources](https://docs.microsoft.com/rest/api/searchservice/indexer-operations) + * [Define AI powered Skillsets to transform and enrich your data](https://docs.microsoft.com/rest/api/searchservice/skillset-operations) + +_The `Azure.Search.Documents` client library (v1) is a brand new offering for +Python developers who want to use search technology in their applications. There +is an older, fully featured `Microsoft.Azure.Search` client library (v10) with +many similar looking APIs, so please be careful to avoid confusion when +exploring online resources._ ## Examples -The following sections contain snippets for some common operations: +The following examples all use a simple [Hotel data set](https://docs.microsoft.com/samples/azure-samples/azure-search-sample-data/azure-search-sample-data/) +that you can [import into your own index from the Azure portal.](https://docs.microsoft.com/azure/search/search-get-started-portal#step-1---start-the-import-data-wizard-and-create-a-data-source) +These are just a few of the basics - please [check out our Samples](samples) for +much more. -* [Perform a simple text search](#perform-a-simple-text-search-on-documents) -* [Retrieve a specific document](#retrieve-a-specific-document-from-an-index) -* [Get search suggestions](#get-search-suggestions) -* [Create an index](#create-an-index) -* [Upload documents to an index](#upload-documents-to-an-index) -More examples, covering topics such as indexers, skillets, and synonym maps can be found in the [Samples directory](samples). +### Querying -### Perform a simple text search on documents -Search the entire index or documents matching a simple search text, e.g. find -hotels with the text "spa": -```python -from azure.core.credentials import AzureKeyCredential -from azure.search.documents import SearchClient -client = SearchClient("", "", AzureKeyCredential("")) +Let's start by importing our namespaces. -results = client.search(search_text="spa") - -print("Hotels containing 'spa' in the name (or other fields):") -for result in results: - print(" Name: {} (rating {})".format(result["HotelName"], result["Rating"])) -``` - -### Retrieve a specific document from an index -Get a specific document from the index, e.f. obtain the document for hotel "23": ```python +import os from azure.core.credentials import AzureKeyCredential from azure.search.documents import SearchClient -client = SearchClient("", "", AzureKeyCredential("")) +``` -result = client.get_document(key="23") +We'll then create a `SearchClient` to access our hotels search index. -print("Details for hotel '23' are:") -print(" Name: {}".format(result["HotelName"])) -print(" Rating: {}".format(result["Rating"])) -print(" Category: {}".format(result["Category"])) +```python +index_name = "hotels" +# Get the service endpoint and API key from the environment +endpoint = os.environ["SEARCH_ENDPOINT"] +key = os.environ["SEARCH_API_KEY"] + +# Create a client +credential = AzureKeyCredential(key) +client = SearchClient(endpoint=endpoint, + index_name=index_name, + credential=credential) ``` -### Get search suggestions +Let's search for a "luxury" hotel. -Get search suggestions for related terms, e.g. find search suggestions for -the term "coffee": ```python -from azure.core.credentials import AzureKeyCredential -from azure.search.documents import SearchClient -client = SearchClient("", "", AzureKeyCredential("")) +results = client.search(search_text="luxury") -results = client.suggest(search_text="coffee", suggester_name="sg") - -print("Search suggestions for 'coffee'") for result in results: - hotel = client.get_document(key=result["HotelId"]) - print(" Text: {} for Hotel: {}".format(repr(result["text"]), hotel["HotelName"])) + print("{}: {})".format(result["hotelId"], result["hotelName"])) ``` ### Create an index +You can use the `SearchIndexClient` to create a search index. Fields can be +defined using convenient `SimpleField`, `SearchableField`, or `ComplexField` +models. Indexes can also define suggesters, lexical analyzers, and more. + ```python +import os from azure.core.credentials import AzureKeyCredential -from azure.search.documents.indexes import SearchIndexClient, CorsOptions, SearchIndex, ScoringProfile -client = SearchIndexClient("", AzureKeyCredential("")) +from azure.search.documents.indexes import SearchIndexClient +from azure.search.documents.indexes.models import ( + ComplexField, + CorsOptions, + SearchIndex, + ScoringProfile, + SearchFieldDataType, + SimpleField, + SearchableField +) + +endpoint = os.environ["SEARCH_ENDPOINT"] +key = os.environ["SEARCH_API_KEY"] + +# Create a service client +client = SearchIndexClient(endpoint, AzureKeyCredential(key)) + +# Create the index name = "hotels" fields = [ SimpleField(name="hotelId", type=SearchFieldDataType.String, key=True), @@ -210,14 +267,49 @@ index = SearchIndex( result = client.create_index(index) ``` -### Upload documents to an index -Add documents (or update existing ones), e.g add a new document for a new hotel: +### Retrieve a specific document from an index + +In addition to querying for documents using keywords and optional filters, +you can retrieve a specific document from your index if you already know the +key. You could get the key from a query, for example, and want to show more +information about it or navigate your customer to that document. + +```python +import os +from azure.core.credentials import AzureKeyCredential +from azure.search.documents import SearchClient + +index_name = "hotels" +endpoint = os.environ["SEARCH_ENDPOINT"] +key = os.environ["SEARCH_API_KEY"] + +client = SearchClient(endpoint, index_name, AzureKeyCredential(key)) + +result = client.get_document(key="1") + +print("Details for hotel '1' are:") +print(" Name: {}".format(result["HotelName"])) +print(" Rating: {}".format(result["Rating"])) +print(" Category: {}".format(result["Category"])) +``` + + +### Adding documents to your index + +You can `Upload`, `Merge`, `MergeOrUpload`, and `Delete` multiple documents from +an index in a single batched request. There are +[a few special rules for merging](https://docs.microsoft.com/rest/api/searchservice/addupdate-or-delete-documents#document-actions) +to be aware of. ```python +import os from azure.core.credentials import AzureKeyCredential from azure.search.documents import SearchClient -client = SearchClient("", "", AzureKeyCredential("")) + +index_name = "hotels" +endpoint = os.environ["SEARCH_ENDPOINT"] +key = os.environ["SEARCH_API_KEY"] DOCUMENT = { 'Category': 'Hotel', @@ -232,6 +324,7 @@ result = client.upload_documents(documents=[DOCUMENT]) print("Upload of new document succeeded: {}".format(result[0].succeeded)) ``` + ## Troubleshooting ### General @@ -273,17 +366,26 @@ result = client.search(search_text="spa", logging_enable=True) ## Next steps -### Additional documentation - -For more extensive documentation on Cognitive Search, see the [Azure Cognitive Search documentation](https://docs.microsoft.com/en-us/azure/search/) on docs.microsoft.com. +* [Go further with Azure.Search.Documents and our samples](samples) +* [Watch a demo or deep dive video](https://azure.microsoft.com/resources/videos/index/?services=search) +* [Read more about the Azure Cognitive Search service](https://docs.microsoft.com/azure/search/search-what-is-azure-search) ## Contributing -This project welcomes contributions and suggestions. Most contributions require you to agree to a Contributor License Agreement (CLA) declaring that you have the right to, and actually do, grant us the rights to use your contribution. For details, visit [cla.microsoft.com][cla]. +See our [Search CONTRIBUTING.md][search_contrib] for details on building, +testing, and contributing to this library. + +This project welcomes contributions and suggestions. Most contributions require +you to agree to a Contributor License Agreement (CLA) declaring that you have +the right to, and actually do, grant us the rights to use your contribution. For +details, visit [cla.microsoft.com][cla]. -When you submit a pull request, a CLA-bot will automatically determine whether you need to provide a CLA and decorate the PR appropriately (e.g., label, comment). Simply follow the instructions provided by the bot. You will only need to do this once across all repos using our CLA. +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. -This project has adopted the [Microsoft Open Source Code of Conduct][code_of_conduct]. For more information see the [Code of Conduct FAQ][coc_faq] or contact [opencode@microsoft.com][coc_contact] with any additional questions or comments. +![Impressions](https://azure-sdk-impressions.azurewebsites.net/api/impressions/azure-sdk-for-net%2Fsdk%2Fsearch%2FAzure.Search.Documents%2FREADME.png) ## Related projects diff --git a/sdk/search/azure-search-documents/azure/search/documents/indexes/_internal/_utils.py b/sdk/search/azure-search-documents/azure/search/documents/indexes/_internal/_utils.py index 98acd82634c9..3ac0d9ee8260 100644 --- a/sdk/search/azure-search-documents/azure/search/documents/indexes/_internal/_utils.py +++ b/sdk/search/azure-search-documents/azure/search/documents/indexes/_internal/_utils.py @@ -423,11 +423,12 @@ def pack_search_field(search_field): ) fields = [pack_search_field(x) for x in search_field.fields] \ if search_field.fields else None + retrievable = not search_field.hidden if search_field.hidden is not None else None return _SearchField( name=search_field.name, type=search_field.type, key=search_field.key, - retrievable=not search_field.hidden, + retrievable=retrievable, searchable=search_field.searchable, filterable=search_field.filterable, sortable=search_field.sortable, @@ -446,11 +447,12 @@ def unpack_search_field(search_field): return None fields = [unpack_search_field(x) for x in search_field.fields] \ if search_field.fields else None + hidden = not search_field.retrievable if search_field.retrievable is not None else None return _SearchField( name=search_field.name, type=search_field.type, key=search_field.key, - hidden=not search_field.retrievable, + hidden=hidden, searchable=search_field.searchable, filterable=search_field.filterable, sortable=search_field.sortable, diff --git a/sdk/search/azure-search-documents/samples/async_samples/sample_data_source_operations_async.py b/sdk/search/azure-search-documents/samples/async_samples/sample_data_source_operations_async.py index 7caac9897357..1afb437220a1 100644 --- a/sdk/search/azure-search-documents/samples/async_samples/sample_data_source_operations_async.py +++ b/sdk/search/azure-search-documents/samples/async_samples/sample_data_source_operations_async.py @@ -64,7 +64,7 @@ async def get_data_source_connection(): async def delete_data_source_connection(): # [START delete_data_source_connection_async] async with client: - client.delete_datasource("async-sample-data-source-connection") + client.delete_data_source_connection("async-sample-data-source-connection") print("Data Source Connection 'async-sample-data-source-connection' successfully deleted") # [END delete_data_source_connection_async] diff --git a/sdk/search/azure-search-documents/samples/async_samples/sample_index_crud_operations_async.py b/sdk/search/azure-search-documents/samples/async_samples/sample_index_crud_operations_async.py index 4a9dbea14d06..9ffc957fcf5f 100644 --- a/sdk/search/azure-search-documents/samples/async_samples/sample_index_crud_operations_async.py +++ b/sdk/search/azure-search-documents/samples/async_samples/sample_index_crud_operations_async.py @@ -96,7 +96,7 @@ async def update_index(): scoring_profiles=scoring_profiles, cors_options=cors_options) - result = await client.create_or_update_index(index_name=index.name, index=index) + result = await client.create_or_update_index(index=index) # [END update_index_async] async def delete_index(): diff --git a/sdk/search/azure-search-documents/samples/async_samples/sample_indexers_operations_async.py b/sdk/search/azure-search-documents/samples/async_samples/sample_indexers_operations_async.py index 4752e1fc3d63..ad3c028fd4fd 100644 --- a/sdk/search/azure-search-documents/samples/async_samples/sample_indexers_operations_async.py +++ b/sdk/search/azure-search-documents/samples/async_samples/sample_indexers_operations_async.py @@ -27,7 +27,12 @@ from azure.core.credentials import AzureKeyCredential from azure.search.documents.indexes.models import ( - SearchIndexerDataContainer, SearchIndex, SearchIndexer, SimpleField, SearchFieldDataType + SearchIndexerDataContainer, + SearchIndexerDataSourceConnection, + SearchIndex, + SearchIndexer, + SimpleField, + SearchFieldDataType ) from azure.search.documents.indexes.aio import SearchIndexerClient, SearchIndexClient @@ -41,23 +46,28 @@ async def create_indexer(): SimpleField(name="baseRate", type=SearchFieldDataType.Double) ] index = SearchIndex(name=index_name, fields=fields) - ind_client = SearchIndexClient(service_endpoint, AzureKeyCredential(key)) + ind_client = SearchIndexerClient(service_endpoint, AzureKeyCredential(key)) async with ind_client: await ind_client.create_index(index) # [START create_indexer_async] # create a datasource container = SearchIndexerDataContainer(name='searchcontainer') + data_source_connection = SearchIndexerDataSourceConnection( + name="indexer-datasource", + type="azureblob", + connection_string=connection_string, + container=container + ) async with ind_client: - data_source = await ind_client.create_datasource( - name="async-indexer-datasource", - type="azureblob", - connection_string=connection_string, - container=container - ) + data_source = await ind_client.create_data_source_connection(data_source_connection) # create an indexer - indexer = SearchIndexer(name="async-sample-indexer", data_source_name="async-indexer-datasource", target_index_name="hotels") + indexer = SearchIndexer( + name="async-sample-indexer", + data_source_name="async-indexer-datasource", + target_index_name="indexer-hotels" + ) async with indexers_client: result = await indexers_client.create_indexer(indexer) print("Create new Indexer - async-sample-indexer") diff --git a/sdk/search/azure-search-documents/samples/async_samples/sample_synonym_map_operations_async.py b/sdk/search/azure-search-documents/samples/async_samples/sample_synonym_map_operations_async.py index 1fa553e19d88..9ce962b1fdf1 100644 --- a/sdk/search/azure-search-documents/samples/async_samples/sample_synonym_map_operations_async.py +++ b/sdk/search/azure-search-documents/samples/async_samples/sample_synonym_map_operations_async.py @@ -44,7 +44,7 @@ async def create_synonym_map(): async def get_synonym_maps(): # [START get_synonym_maps_async] result = await client.get_synonym_maps() - names = [x["name"] for x in result] + names = [x.name for x in result] print("Found {} Synonym Maps in the service: {}".format(len(result), ", ".join(names))) # [END get_synonym_maps_async] @@ -52,7 +52,7 @@ async def get_synonym_map(): # [START get_synonym_map_async] result = await client.get_synonym_map("test-syn-map") print("Retrived Synonym Map 'test-syn-map' with synonyms") - for syn in result["synonyms"]: + for syn in result.synonyms: print(" {}".format(syn)) # [END get_synonym_map_async] diff --git a/sdk/search/azure-search-documents/samples/sample_index_crud_operations.py b/sdk/search/azure-search-documents/samples/sample_index_crud_operations.py index 4a34809d672e..b3d8d2d85684 100644 --- a/sdk/search/azure-search-documents/samples/sample_index_crud_operations.py +++ b/sdk/search/azure-search-documents/samples/sample_index_crud_operations.py @@ -87,13 +87,13 @@ def update_index(): ) scoring_profiles = [] scoring_profiles.append(scoring_profile) - index = Index( + index = SearchIndex( name=name, fields=fields, scoring_profiles=scoring_profiles, cors_options=cors_options) - result = client.create_or_update_index(index_name=index.name, index=index) + result = client.create_or_update_index(index=index) # [END update_index] def delete_index(): diff --git a/sdk/search/azure-search-documents/samples/sample_indexers_operations.py b/sdk/search/azure-search-documents/samples/sample_indexers_operations.py index 668a7c622297..c437fb9cbae2 100644 --- a/sdk/search/azure-search-documents/samples/sample_indexers_operations.py +++ b/sdk/search/azure-search-documents/samples/sample_indexers_operations.py @@ -26,7 +26,12 @@ from azure.core.credentials import AzureKeyCredential from azure.search.documents.indexes.models import ( - SearchIndexerDataContainer, SearchIndex, SearchIndexer, SimpleField, SearchFieldDataType + SearchIndexerDataContainer, + SearchIndexerDataSourceConnection, + SearchIndex, + SearchIndexer, + SimpleField, + SearchFieldDataType ) from azure.search.documents.indexes import SearchIndexClient, SearchIndexerClient @@ -46,15 +51,20 @@ def create_indexer(): # [START create_indexer] # create a datasource container = SearchIndexerDataContainer(name='searchcontainer') - data_source = indexers_client.create_datasource( + data_source_connection = SearchIndexerDataSourceConnection( name="indexer-datasource", type="azureblob", connection_string=connection_string, container=container ) + data_source = indexers_client.create_data_source_connection(data_source_connection) # create an indexer - indexer = SearchIndexer(name="sample-indexer", data_source_name="indexer-datasource", target_index_name="hotels") + indexer = SearchIndexer( + name="sample-indexer", + data_source_name="indexer-datasource", + target_index_name="indexer-hotels" + ) result = indexers_client.create_indexer(indexer) print("Create new Indexer - sample-indexer") # [END create_indexer] diff --git a/sdk/search/azure-search-documents/samples/sample_synonym_map_operations.py b/sdk/search/azure-search-documents/samples/sample_synonym_map_operations.py index a9f828690c53..607d56dadfe9 100644 --- a/sdk/search/azure-search-documents/samples/sample_synonym_map_operations.py +++ b/sdk/search/azure-search-documents/samples/sample_synonym_map_operations.py @@ -43,7 +43,7 @@ def create_synonym_map(): def get_synonym_maps(): # [START get_synonym_maps] result = client.get_synonym_maps() - names = [x["name"] for x in result] + names = [x.name for x in result] print("Found {} Synonym Maps in the service: {}".format(len(result), ", ".join(names))) # [END get_synonym_maps] @@ -51,7 +51,7 @@ def get_synonym_map(): # [START get_synonym_map] result = client.get_synonym_map("test-syn-map") print("Retrived Synonym Map 'test-syn-map' with synonyms") - for syn in result["synonyms"]: + for syn in result.synonyms: print(" {}".format(syn)) # [END get_synonym_map] From c05481c0d7e6d3e4c87f10892f2abf22c49e6b18 Mon Sep 17 00:00:00 2001 From: Rakshith Bhyravabhotla Date: Wed, 17 Jun 2020 12:17:49 -0700 Subject: [PATCH 20/21] changes in samples tests (#12090) * changes in samples * oops --- .../tests/test_samples.py | 21 ++++++++++--------- .../tests/test_samples_async.py | 21 ++++++++++--------- 2 files changed, 22 insertions(+), 20 deletions(-) diff --git a/sdk/formrecognizer/azure-ai-formrecognizer/tests/test_samples.py b/sdk/formrecognizer/azure-ai-formrecognizer/tests/test_samples.py index 7e30f070c346..2c282f1976cd 100644 --- a/sdk/formrecognizer/azure-ai-formrecognizer/tests/test_samples.py +++ b/sdk/formrecognizer/azure-ai-formrecognizer/tests/test_samples.py @@ -25,8 +25,6 @@ from azure.ai.formrecognizer import FormTrainingClient from testcase import FormRecognizerTest, GlobalFormRecognizerAccountPreparer -def _setenv(key, val): - os.environ[key] = os.getenv(val) or os.getenv(key) def run(cmd, my_env): os.environ['PYTHONUNBUFFERED'] = "1" @@ -45,21 +43,24 @@ def _test_file(file_name, account, key, root_dir='./samples'): my_env = dict(os.environ) if sys.version_info < (3, 5): my_env = {key: str(val) for key, val in my_env.items()} - code, _, err = run([sys.executable, root_dir + '/' + file_name], my_env=my_env) - assert code == 0 - assert err is None + code, out, err = run([sys.executable, root_dir + '/' + file_name], my_env=my_env) + try: + assert code == 0 + assert err is None + except AssertionError as e: + e.args += (out, ) + raise AssertionError(e) class TestSamples(FormRecognizerTest): @pytest.mark.live_test_only @GlobalFormRecognizerAccountPreparer() def test_sample_authentication(self, resource_group, location, form_recognizer_account, form_recognizer_account_key): - _setenv('AZURE_FORM_RECOGNIZER_AAD_ENDPOINT', 'AZURE_FORM_RECOGNIZER_AAD_ENDPOINT') _test_file('sample_authentication.py', form_recognizer_account, form_recognizer_account_key) @pytest.mark.live_test_only @GlobalFormRecognizerAccountPreparer() def test_sample_get_bounding_boxes(self, resource_group, location, form_recognizer_account, form_recognizer_account_key): - _setenv('CONTAINER_SAS_URL', 'AZURE_FORM_RECOGNIZER_STORAGE_CONTAINER_SAS_URL') + os.environ['CONTAINER_SAS_URL'] = self.get_settings_value("FORM_RECOGNIZER_STORAGE_CONTAINER_SAS_URL") ftc = FormTrainingClient(form_recognizer_account, AzureKeyCredential(form_recognizer_account_key)) container_sas_url = os.environ['CONTAINER_SAS_URL'] poller = ftc.begin_training(container_sas_url, use_training_labels=False) @@ -80,7 +81,7 @@ def test_sample_recognize_content(self, resource_group, location, form_recognize @pytest.mark.live_test_only @GlobalFormRecognizerAccountPreparer() def test_sample_recognize_custom_forms(self, resource_group, location, form_recognizer_account, form_recognizer_account_key): - _setenv('CONTAINER_SAS_URL', 'AZURE_FORM_RECOGNIZER_STORAGE_CONTAINER_SAS_URL') + os.environ['CONTAINER_SAS_URL'] = self.get_settings_value("FORM_RECOGNIZER_STORAGE_CONTAINER_SAS_URL") ftc = FormTrainingClient(form_recognizer_account, AzureKeyCredential(form_recognizer_account_key)) container_sas_url = os.environ['CONTAINER_SAS_URL'] poller = ftc.begin_training(container_sas_url, use_training_labels=False) @@ -101,11 +102,11 @@ def test_sample_recognize_receipts(self, resource_group, location, form_recogniz @pytest.mark.live_test_only @GlobalFormRecognizerAccountPreparer() def test_sample_train_model_with_labels(self, resource_group, location, form_recognizer_account, form_recognizer_account_key): - _setenv('CONTAINER_SAS_URL', 'AZURE_FORM_RECOGNIZER_STORAGE_CONTAINER_SAS_URL') + os.environ['CONTAINER_SAS_URL'] = self.get_settings_value("FORM_RECOGNIZER_STORAGE_CONTAINER_SAS_URL") _test_file('sample_train_model_with_labels.py', form_recognizer_account, form_recognizer_account_key) @pytest.mark.live_test_only @GlobalFormRecognizerAccountPreparer() def test_sample_train_model_without_labels(self, resource_group, location, form_recognizer_account, form_recognizer_account_key): - _setenv('CONTAINER_SAS_URL', 'AZURE_FORM_RECOGNIZER_STORAGE_CONTAINER_SAS_URL') + os.environ['CONTAINER_SAS_URL'] = self.get_settings_value("FORM_RECOGNIZER_STORAGE_CONTAINER_SAS_URL") _test_file('sample_train_model_without_labels.py', form_recognizer_account, form_recognizer_account_key) diff --git a/sdk/formrecognizer/azure-ai-formrecognizer/tests/test_samples_async.py b/sdk/formrecognizer/azure-ai-formrecognizer/tests/test_samples_async.py index d1db2ca048b4..88c74dfef491 100644 --- a/sdk/formrecognizer/azure-ai-formrecognizer/tests/test_samples_async.py +++ b/sdk/formrecognizer/azure-ai-formrecognizer/tests/test_samples_async.py @@ -24,9 +24,6 @@ from azure.ai.formrecognizer.aio import FormTrainingClient from testcase import FormRecognizerTest, GlobalFormRecognizerAccountPreparer -def _setenv(key, val): - os.environ[key] = os.getenv(val) or os.getenv(key) - def run(cmd, my_env): os.environ['PYTHONUNBUFFERED'] = "1" proc = subprocess.Popen(cmd, @@ -41,9 +38,13 @@ def run(cmd, my_env): def _test_file(file_name, account, key, root_dir='./samples/async_samples'): os.environ['AZURE_FORM_RECOGNIZER_ENDPOINT'] = account os.environ['AZURE_FORM_RECOGNIZER_KEY'] = key - code, _, err = run([sys.executable, root_dir + '/' + file_name], my_env=dict(os.environ)) - assert code == 0 - assert err is None + code, out, err = run([sys.executable, root_dir + '/' + file_name], my_env=dict(os.environ)) + try: + assert code == 0 + assert err is None + except AssertionError as e: + e.args += (out, ) + raise AssertionError(e) class TestSamplesAsync(FormRecognizerTest): @@ -56,7 +57,7 @@ def test_sample_authentication_async(self, resource_group, location, form_recogn @pytest.mark.live_test_only @GlobalFormRecognizerAccountPreparer() async def test_sample_get_bounding_boxes_async(self, resource_group, location, form_recognizer_account, form_recognizer_account_key): - _setenv('CONTAINER_SAS_URL', 'AZURE_FORM_RECOGNIZER_STORAGE_CONTAINER_SAS_URL') + os.environ['CONTAINER_SAS_URL'] = self.get_settings_value("FORM_RECOGNIZER_STORAGE_CONTAINER_SAS_URL") ftc = FormTrainingClient(form_recognizer_account, AzureKeyCredential(form_recognizer_account_key)) container_sas_url = os.environ['CONTAINER_SAS_URL'] poller = await ftc.begin_training(container_sas_url, use_training_labels=False) @@ -77,7 +78,7 @@ def test_sample_recognize_content_async(self, resource_group, location, form_rec @pytest.mark.live_test_only @GlobalFormRecognizerAccountPreparer() async def test_sample_recognize_custom_forms_async(self, resource_group, location, form_recognizer_account, form_recognizer_account_key): - _setenv('CONTAINER_SAS_URL', 'AZURE_FORM_RECOGNIZER_STORAGE_CONTAINER_SAS_URL') + os.environ['CONTAINER_SAS_URL'] = self.get_settings_value("FORM_RECOGNIZER_STORAGE_CONTAINER_SAS_URL") ftc = FormTrainingClient(form_recognizer_account, AzureKeyCredential(form_recognizer_account_key)) container_sas_url = os.environ['CONTAINER_SAS_URL'] poller = await ftc.begin_training(container_sas_url, use_training_labels=False) @@ -98,12 +99,12 @@ def test_sample_recognize_receipts_async(self, resource_group, location, form_re @pytest.mark.live_test_only @GlobalFormRecognizerAccountPreparer() def test_sample_train_model_with_labels_async(self, resource_group, location, form_recognizer_account, form_recognizer_account_key): - _setenv('CONTAINER_SAS_URL', 'AZURE_FORM_RECOGNIZER_STORAGE_CONTAINER_SAS_URL') + os.environ['CONTAINER_SAS_URL'] = self.get_settings_value("FORM_RECOGNIZER_STORAGE_CONTAINER_SAS_URL") _test_file('sample_train_model_with_labels_async.py', form_recognizer_account, form_recognizer_account_key) @pytest.mark.live_test_only @GlobalFormRecognizerAccountPreparer() def test_sample_train_model_without_labels_async(self, resource_group, location, form_recognizer_account, form_recognizer_account_key): - _setenv('CONTAINER_SAS_URL', 'AZURE_FORM_RECOGNIZER_STORAGE_CONTAINER_SAS_URL') + os.environ['CONTAINER_SAS_URL'] = self.get_settings_value("FORM_RECOGNIZER_STORAGE_CONTAINER_SAS_URL") _test_file('sample_train_model_without_labels_async.py', form_recognizer_account, form_recognizer_account_key) From 4c8dcd3d1b4e84870c16b1932640ada7c842f294 Mon Sep 17 00:00:00 2001 From: Krista Pratico Date: Wed, 17 Jun 2020 12:48:05 -0700 Subject: [PATCH 21/21] [formrecognizer] update formrecognizer links to new aka.ms naming (#12079) * update formrecognizer links to new aka.ms naming * update --- sdk/formrecognizer/azure-ai-formrecognizer/README.md | 6 +++--- .../azure-ai-formrecognizer/samples/README.md | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/sdk/formrecognizer/azure-ai-formrecognizer/README.md b/sdk/formrecognizer/azure-ai-formrecognizer/README.md index 9096cb5ba62e..5d511fa6002b 100644 --- a/sdk/formrecognizer/azure-ai-formrecognizer/README.md +++ b/sdk/formrecognizer/azure-ai-formrecognizer/README.md @@ -403,7 +403,7 @@ This project has adopted the [Microsoft Open Source Code of Conduct][code_of_con [python-fr-src]: https://github.com/Azure/azure-sdk-for-python/tree/master/sdk/formrecognizer/azure-ai-formrecognizer/azure/ai/formrecognizer [python-fr-pypi]: https://pypi.org/project/azure-ai-formrecognizer/ [python-fr-product-docs]: https://docs.microsoft.com/azure/cognitive-services/form-recognizer/overview -[python-fr-ref-docs]: https://aka.ms/azsdk-python-formrecognizer-ref-docs +[python-fr-ref-docs]: https://aka.ms/azsdk/python/formrecognizer/docs [python-fr-samples]: https://github.com/Azure/azure-sdk-for-python/tree/master/sdk/formrecognizer/azure-ai-formrecognizer/samples @@ -413,13 +413,13 @@ This project has adopted the [Microsoft Open Source Code of Conduct][code_of_con [pip]: https://pypi.org/project/pip/ [azure_portal_create_FR_resource]: https://ms.portal.azure.com/#create/Microsoft.CognitiveServicesFormRecognizer [azure_cli_create_FR_resource]: https://docs.microsoft.com/azure/cognitive-services/cognitive-services-apis-create-account-cli?tabs=windows -[azure-key-credential]: https://aka.ms/azsdk-python-core-azurekeycredential +[azure-key-credential]: https://aka.ms/azsdk/python/core/azurekeycredential [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 [azure_core]: https://github.com/Azure/azure-sdk-for-python/blob/master/sdk/core/azure-core/README.md -[azure_core_ref_docs]: https://aka.ms/azsdk-python-azure-core +[azure_core_ref_docs]: https://aka.ms/azsdk/python/core/docs [python_logging]: https://docs.python.org/3/library/logging.html [multi_and_single_service]: https://docs.microsoft.com/azure/cognitive-services/cognitive-services-apis-create-account?tabs=multiservice%2Cwindows [azure_cli_endpoint_lookup]: https://docs.microsoft.com/cli/azure/cognitiveservices/account?view=azure-cli-latest#az-cognitiveservices-account-show diff --git a/sdk/formrecognizer/azure-ai-formrecognizer/samples/README.md b/sdk/formrecognizer/azure-ai-formrecognizer/samples/README.md index 566299916445..d156fa3c7327 100644 --- a/sdk/formrecognizer/azure-ai-formrecognizer/samples/README.md +++ b/sdk/formrecognizer/azure-ai-formrecognizer/samples/README.md @@ -69,7 +69,7 @@ what you can do with the Azure Form Recognizer client library. [azure_subscription]: https://azure.microsoft.com/free/ [azure_form_recognizer_account]: https://docs.microsoft.com/azure/cognitive-services/cognitive-services-apis-create-account?tabs=singleservice%2Cwindows [azure_identity_pip]: https://pypi.org/project/azure-identity/ -[python-fr-ref-docs]: https://aka.ms/azsdk-python-formrecognizer-ref-docs +[python-fr-ref-docs]: https://aka.ms/azsdk/python/formrecognizer/docs [get-endpoint-instructions]: https://github.com/Azure/azure-sdk-for-python/blob/master/sdk/formrecognizer/azure-ai-formrecognizer/README.md#looking-up-the-endpoint [get-key-instructions]: https://github.com/Azure/azure-sdk-for-python/blob/master/sdk/formrecognizer/azure-ai-formrecognizer/README.md#types-of-credentials