Skip to content

Commit 6db6bd9

Browse files
authored
[SchemaRegistry] handle unknown content types (Azure#37166)
* [SchemaRegistry] handle unknown content types * remove extra space in Accept headers * fix license in tests * fix spacing in accept header async * add tests with mocked unknown content type * mypy/lint * cspell
1 parent f17312e commit 6db6bd9

File tree

9 files changed

+522
-41
lines changed

9 files changed

+522
-41
lines changed

sdk/schemaregistry/azure-schemaregistry/CHANGELOG.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,15 +6,16 @@ This version and all future versions will require Python 3.8+. Python 3.7 is no
66

77
### Features Added
88

9-
- `V2022_10` has been added to `ApiVersion` and set as the default API version.
10-
- `Json` and `Custom` have been added to supported formats in `SchemaFormat`.
119
- Sync and async `JsonSchemaEncoder` have been added under `azure.schemaregistry.encoder.jsonencoder`.
1210
- `InvalidContentError` have been added under `azure.schemaregistry.encoder.jsonencoder` for use with the `JsonSchemaEncoder`.
1311
- `MessageContent`, `OutboundMessageContent`,`InboundMessageContent`, and `SchemaContentValidate` have been added under `azure.schemaregistry` as protocols for use with the `JsonSchemaEncoder` and/or future encoder implementations.
12+
- `Json` and `Custom` have been added to supported formats in `SchemaFormat`.
13+
- `V2022_10` has been added to `ApiVersion` and set as the default API version.
1414

1515
### Bugs Fixed
1616

1717
- Fixed a bug in sync/async `register_schema` and `get_schema_properties` that did not accept case insensitive strings as an argument to the `format` parameter.
18+
- Fixed a bug where unknown content type strings from the service raised a client error, rather than being returned as a string in the SchemaProperties `format` property.
1819

1920
### Other Changes
2021

sdk/schemaregistry/azure-schemaregistry/assets.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,5 @@
22
"AssetsRepo": "Azure/azure-sdk-assets",
33
"AssetsRepoPrefixPath": "python",
44
"TagPrefix": "python/schemaregistry/azure-schemaregistry",
5-
"Tag": "python/schemaregistry/azure-schemaregistry_a1c9d18bfd"
5+
"Tag": "python/schemaregistry/azure-schemaregistry_0a27c7561c"
66
}

sdk/schemaregistry/azure-schemaregistry/azure/schemaregistry/_patch.py

Lines changed: 89 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -22,12 +22,13 @@
2222
overload,
2323
IO,
2424
)
25+
from enum import Enum
2526
from typing_extensions import Protocol, TypedDict, Self
26-
27+
from azure.core import CaseInsensitiveEnumMeta
2728
from azure.core.tracing.decorator import distributed_trace
2829

2930
from ._client import SchemaRegistryClient as GeneratedServiceClient
30-
from .models._patch import SchemaFormat
31+
from .models._patch import SchemaFormat, NormalizedSchemaContentTypes
3132

3233
if TYPE_CHECKING:
3334
from azure.core.credentials import TokenCredential
@@ -57,22 +58,24 @@ def _parse_schema_properties_dict(response_headers: Mapping[str, Union[str, int]
5758
"version": int(response_headers["Schema-Version"]),
5859
}
5960

61+
def _normalize_content_type(content_type: str) -> str:
62+
return content_type.replace(" ", "").lower()
6063

61-
def _get_format(content_type: str) -> SchemaFormat:
64+
def _get_format(content_type: str) -> Union[SchemaFormat, str]:
6265
# pylint:disable=redefined-builtin
6366
# Exception cases may be due to forward compatibility.
6467
# i.e. Getting a schema with a content type from a future API version.
65-
# In this case, we default to CUSTOM format.
66-
try:
67-
format = content_type.split("serialization=")[1]
68-
try:
69-
return SchemaFormat(format.capitalize())
70-
except ValueError:
71-
pass
72-
except IndexError:
73-
pass
74-
return SchemaFormat.CUSTOM
75-
68+
# In this case, we default to returning the content type string.
69+
70+
# remove whitespace and case from string
71+
normalized_content_type = _normalize_content_type(content_type)
72+
if normalized_content_type == NormalizedSchemaContentTypes.AVRO.value:
73+
return SchemaFormat.AVRO
74+
if normalized_content_type == NormalizedSchemaContentTypes.JSON.value:
75+
return SchemaFormat.JSON
76+
if normalized_content_type == NormalizedSchemaContentTypes.CUSTOM.value:
77+
return SchemaFormat.CUSTOM
78+
return content_type
7679

7780
def prepare_schema_properties_result( # pylint:disable=unused-argument,redefined-builtin
7881
format: str,
@@ -220,10 +223,62 @@ def register_schema( # pylint:disable=arguments-differ
220223
return SchemaProperties(**properties)
221224

222225
@overload
223-
def get_schema(self, schema_id: str, **kwargs: Any) -> Schema: ...
226+
def get_schema(self, schema_id: str, **kwargs: Any) -> Schema:
227+
"""Gets a registered schema.
228+
229+
To get a registered schema by its unique ID, pass the `schema_id` parameter and any optional
230+
keyword arguments. Azure Schema Registry guarantees that ID is unique within a namespace.
231+
232+
WARNING: If retrieving a schema format that is unsupported by this client version, upgrade to a client
233+
version that supports the schema format. Otherwise, the content MIME type string will be returned as
234+
the `format` value in the `properties` of the returned Schema.
235+
236+
:param str schema_id: References specific schema in registry namespace. Required if `group_name`,
237+
`name`, and `version` are not provided.
238+
:return: The schema stored in the registry associated with the provided arguments.
239+
:rtype: ~azure.schemaregistry.Schema
240+
:raises: :class:`~azure.core.exceptions.HttpResponseError`
241+
242+
.. admonition:: Example:
243+
244+
.. literalinclude:: ../samples/sync_samples/sample_code_schemaregistry.py
245+
:start-after: [START get_schema_sync]
246+
:end-before: [END get_schema_sync]
247+
:language: python
248+
:dedent: 4
249+
:caption: Get schema by id.
250+
251+
"""
252+
...
224253

225254
@overload
226-
def get_schema(self, *, group_name: str, name: str, version: int, **kwargs: Any) -> Schema: ...
255+
def get_schema(self, *, group_name: str, name: str, version: int, **kwargs: Any) -> Schema:
256+
"""Gets a registered schema.
257+
258+
To get a specific version of a schema within the specified schema group, pass in the required
259+
keyword arguments `group_name`, `name`, and `version` and any optional keyword arguments.
260+
261+
WARNING: If retrieving a schema format that is unsupported by this client version, upgrade to a client
262+
version that supports the schema format. Otherwise, the content MIME type string will be returned as
263+
the `format` value in the `properties` of the returned Schema.
264+
265+
:keyword str group_name: Name of schema group that contains the registered schema.
266+
:keyword str name: Name of schema which should be retrieved.
267+
:keyword int version: Version of schema which should be retrieved.
268+
:return: The schema stored in the registry associated with the provided arguments.
269+
:rtype: ~azure.schemaregistry.Schema
270+
:raises: :class:`~azure.core.exceptions.HttpResponseError`
271+
272+
.. admonition:: Example:
273+
274+
.. literalinclude:: ../samples/sync_samples/sample_code_schemaregistry.py
275+
:start-after: [START get_schema_by_version_sync]
276+
:end-before: [END get_schema_by_version_sync]
277+
:language: python
278+
:dedent: 4
279+
:caption: Get schema by version.
280+
"""
281+
...
227282

228283
@distributed_trace
229284
def get_schema( # pylint: disable=docstring-missing-param,docstring-should-be-keyword
@@ -237,6 +292,10 @@ def get_schema( # pylint: disable=docstring-missing-param,docstring-should-be-k
237292
2) To get a specific version of a schema within the specified schema group, pass in the required
238293
keyword arguments `group_name`, `name`, and `version` and any optional keyword arguments.
239294
295+
WARNING: If retrieving a schema format that is unsupported by this client version, upgrade to a client
296+
version that supports the schema format. Otherwise, the content MIME type string will be returned as
297+
the `format` value in the `properties` of the returned Schema.
298+
240299
:param str schema_id: References specific schema in registry namespace. Required if `group_name`,
241300
`name`, and `version` are not provided.
242301
:keyword str group_name: Name of schema group that contains the registered schema.
@@ -281,8 +340,8 @@ def get_schema( # pylint: disable=docstring-missing-param,docstring-should-be-k
281340
id=schema_id,
282341
cls=prepare_schema_result,
283342
headers={ # TODO: remove when multiple content types in response are supported
284-
"Accept": """application/json; serialization=Avro, application/json; \
285-
serialization=json, text/plain; charset=utf-8"""
343+
"Accept": """application/json; serialization=Avro, application/json; """
344+
"""serialization=json, text/plain; charset=utf-8"""
286345
},
287346
stream=True,
288347
**http_request_kwargs,
@@ -305,8 +364,8 @@ def get_schema( # pylint: disable=docstring-missing-param,docstring-should-be-k
305364
schema_version=version,
306365
cls=prepare_schema_result,
307366
headers={ # TODO: remove when multiple content types in response are supported
308-
"Accept": """application/json; serialization=Avro, application/json; \
309-
serialization=json, text/plain; charset=utf-8"""
367+
"Accept": """application/json; serialization=Avro, application/json; """
368+
"""serialization=json, text/plain; charset=utf-8"""
310369
},
311370
stream=True,
312371
**http_request_kwargs,
@@ -414,6 +473,16 @@ def __init__(self, **kwargs: Any) -> None:
414473
def __repr__(self) -> str:
415474
return f"Schema(definition={self.definition}, properties={self.properties})"[:1024]
416475

476+
# ApiVersion was added to a previously GA'd version. However, newer libraries should not
477+
# accept ApiVersion enums and only take strings. Leaving this here for backwards compatibility.
478+
class ApiVersion(str, Enum, metaclass=CaseInsensitiveEnumMeta):
479+
"""
480+
Represents the Schema Registry API version to use for requests.
481+
"""
482+
483+
V2021_10 = "2021-10"
484+
V2022_10 = "2022-10"
485+
"""This is the default version."""
417486

418487
###### Encoder Protocols ######
419488

sdk/schemaregistry/azure-schemaregistry/azure/schemaregistry/aio/_patch.py

Lines changed: 62 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -143,10 +143,62 @@ async def register_schema(
143143
return SchemaProperties(**properties)
144144

145145
@overload
146-
async def get_schema(self, schema_id: str, **kwargs: Any) -> Schema: ...
146+
async def get_schema(self, schema_id: str, **kwargs: Any) -> Schema:
147+
"""Gets a registered schema.
148+
149+
To get a registered schema by its unique ID, pass the `schema_id` parameter and any optional
150+
keyword arguments. Azure Schema Registry guarantees that ID is unique within a namespace.
151+
152+
WARNING: If retrieving a schema format that is unsupported by this client version, upgrade to a client
153+
version that supports the schema format. Otherwise, the content MIME type string will be returned as
154+
the `format` value in the `properties` of the returned Schema.
155+
156+
:param str schema_id: References specific schema in registry namespace. Required if `group_name`,
157+
`name`, and `version` are not provided.
158+
:return: The schema stored in the registry associated with the provided arguments.
159+
:rtype: ~azure.schemaregistry.Schema
160+
:raises: :class:`~azure.core.exceptions.HttpResponseError`
161+
162+
.. admonition:: Example:
163+
164+
.. literalinclude:: ../samples/async_samples/sample_code_schemaregistry_async.py
165+
:start-after: [START get_schema_async]
166+
:end-before: [END get_schema_async]
167+
:language: python
168+
:dedent: 4
169+
:caption: Get schema by id.
170+
171+
"""
172+
...
147173

148174
@overload
149-
async def get_schema(self, *, group_name: str, name: str, version: int, **kwargs: Any) -> Schema: ...
175+
async def get_schema(self, *, group_name: str, name: str, version: int, **kwargs: Any) -> Schema:
176+
"""Gets a registered schema.
177+
178+
To get a specific version of a schema within the specified schema group, pass in the required
179+
keyword arguments `group_name`, `name`, and `version` and any optional keyword arguments.
180+
181+
WARNING: If retrieving a schema format that is unsupported by this client version, upgrade to a client
182+
version that supports the schema format. Otherwise, the content MIME type string will be returned as
183+
the `format` value in the `properties` of the returned Schema.
184+
185+
:keyword str group_name: Name of schema group that contains the registered schema.
186+
:keyword str name: Name of schema which should be retrieved.
187+
:keyword int version: Version of schema which should be retrieved.
188+
:return: The schema stored in the registry associated with the provided arguments.
189+
:rtype: ~azure.schemaregistry.Schema
190+
:raises: :class:`~azure.core.exceptions.HttpResponseError`
191+
192+
.. admonition:: Example:
193+
194+
.. literalinclude:: ../samples/async_samples/sample_code_schemaregistry_async.py
195+
:start-after: [START get_schema_by_version_async]
196+
:end-before: [END get_schema_by_version_async]
197+
:language: python
198+
:dedent: 4
199+
:caption: Get schema by version.
200+
"""
201+
...
150202

151203
@distributed_trace_async
152204
async def get_schema( # pylint: disable=docstring-missing-param,docstring-should-be-keyword
@@ -160,6 +212,10 @@ async def get_schema( # pylint: disable=docstring-missing-param,docstring-shoul
160212
2) To get a specific version of a schema within the specified schema group, pass in the required
161213
keyword arguments `group_name`, `name`, and `version` and any optional keyword arguments.
162214
215+
WARNING: If retrieving a schema format that is unsupported by this client version, upgrade to a client
216+
version that supports the schema format. Otherwise, the content MIME type string will be returned as
217+
the `format` value in the `properties` of the returned Schema.
218+
163219
:param str schema_id: References specific schema in registry namespace. Required if `group_name`,
164220
`name`, and `version` are not provided.
165221
:keyword str group_name: Name of schema group that contains the registered schema.
@@ -204,8 +260,8 @@ async def get_schema( # pylint: disable=docstring-missing-param,docstring-shoul
204260
id=schema_id,
205261
cls=prepare_schema_result,
206262
headers={ # TODO: remove when multiple content types are supported
207-
"Accept": """application/json; serialization=Avro, application/json; \
208-
serialization=json, text/plain; charset=utf-8"""
263+
"Accept": """application/json; serialization=Avro, application/json; """
264+
"""serialization=json, text/plain; charset=utf-8"""
209265
},
210266
stream=True,
211267
**http_request_kwargs,
@@ -229,8 +285,8 @@ async def get_schema( # pylint: disable=docstring-missing-param,docstring-shoul
229285
schema_version=version,
230286
cls=prepare_schema_result,
231287
headers={ # TODO: remove when multiple content types are supported
232-
"Accept": """application/json; serialization=Avro, application/json; \
233-
serialization=json, text/plain; charset=utf-8"""
288+
"Accept": """application/json; serialization=Avro, application/json; """
289+
"""serialization=json, text/plain; charset=utf-8"""
234290
},
235291
stream=True,
236292
**http_request_kwargs,

sdk/schemaregistry/azure-schemaregistry/azure/schemaregistry/models/_patch.py

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,9 @@
99
from typing import List
1010

1111
from enum import Enum
12+
1213
from azure.core import CaseInsensitiveEnumMeta
14+
from ._enums import SchemaContentTypeValues
1315

1416

1517
class SchemaFormat(str, Enum, metaclass=CaseInsensitiveEnumMeta):
@@ -22,18 +24,16 @@ class SchemaFormat(str, Enum, metaclass=CaseInsensitiveEnumMeta):
2224
CUSTOM = "Custom"
2325
"""Represents a custom schema format."""
2426

27+
# Normalizing the schema content type strings for whitespace and case insensitive comparison.
28+
class NormalizedSchemaContentTypes(str, Enum, metaclass=CaseInsensitiveEnumMeta):
29+
"""Describes closed list of normalized schema content type values."""
2530

26-
class ApiVersion(str, Enum, metaclass=CaseInsensitiveEnumMeta):
27-
"""
28-
Represents the Schema Registry API version to use for requests.
29-
"""
30-
31-
V2021_10 = "2021-10"
32-
V2022_10 = "2022-10"
33-
"""This is the default version."""
34-
35-
36-
DEFAULT_VERSION = ApiVersion.V2022_10
31+
AVRO = SchemaContentTypeValues.AVRO.value.replace(" ", "").lower()
32+
"""Avro encoding."""
33+
JSON = SchemaContentTypeValues.JSON.value.replace(" ", "").lower()
34+
"""JSON encoding"""
35+
CUSTOM = SchemaContentTypeValues.CUSTOM.value.replace(" ", "").lower()
36+
"""Plain text custom encoding."""
3737

3838

3939
__all__: List[str] = [] # Add all objects you want publicly available to users at this package level

0 commit comments

Comments
 (0)