diff --git a/eng/ignore-links.txt b/eng/ignore-links.txt index bd605f16d16a..806cc64a3fe8 100644 --- a/eng/ignore-links.txt +++ b/eng/ignore-links.txt @@ -1 +1,10 @@ -https://docs.microsoft.com/python/api/overview/azure/{{package_doc_id}} \ No newline at end of file +https://docs.microsoft.com/python/api/overview/azure/{{package_doc_id}} +https://pypi.org/project/azure-schemaregistry +https://azuresdkdocs.blob.core.windows.net/$web/python/azure-schemaregistry/latest/index.html +https://github.com/Azure/azure-sdk-for-python/tree/master/sdk/schemaregistry/azure-schemaregistry/samples/sync_samples/schema_registry.py +https://github.com/Azure/azure-sdk-for-python/tree/master/sdk/schemaregistry/azure-schemaregistry/samples/async_samples/schema_registry_async.py +https://pypi.org/project/azure-schemaregistry-avroserializer +https://github.com/Azure/azure-sdk-for-python/tree/master/sdk/schemaregistry/azure-schemaregistry-avroserializer/CHANGELOG.md +https://azuresdkdocs.blob.core.windows.net/$web/python/azure-schemaregistry-avroserializer/latest/index.html +https://github.com/Azure/azure-sdk-for-python/tree/master/sdk/schemaregistry/azure-schemaregistry-avroserializer/samples +https://github.com/Azure/azure-sdk-for-python/tree/master/sdk/schemaregistry/azure-schemaregistry-avroserializer/samples/avro_serializer.py \ No newline at end of file diff --git a/sdk/schemaregistry/azure-schemaregistry-avroserializer/CHANGELOG.md b/sdk/schemaregistry/azure-schemaregistry-avroserializer/CHANGELOG.md new file mode 100644 index 000000000000..d132099f47a4 --- /dev/null +++ b/sdk/schemaregistry/azure-schemaregistry-avroserializer/CHANGELOG.md @@ -0,0 +1,11 @@ +# Release History + +## 1.0.0b1 (2020-09-08) + +Version 1.0.0b1 is the first preview of our efforts to create a user-friendly and Pythonic client library for Azure Schema Registry Avro Serializer. + +**New features** + +- `SchemaRegistryAvroSerializer` is the top-level client class that provides the functionality to encode and decode avro data utilizing the avro library. It will automatically register schema and retrieve schema from Azure Schema Registry Service. It provides two methods: + - `serialize`: Serialize dict data into bytes according to the given schema and register schema if needed. + - `deserialize`: Deserialize bytes data into dict data by automatically retrieving schema from the service. diff --git a/sdk/schemaregistry/azure-schemaregistry-avroserializer/MANIFEST.in b/sdk/schemaregistry/azure-schemaregistry-avroserializer/MANIFEST.in new file mode 100644 index 000000000000..90e5d6eb2bd7 --- /dev/null +++ b/sdk/schemaregistry/azure-schemaregistry-avroserializer/MANIFEST.in @@ -0,0 +1,6 @@ +include *.md +include azure/__init__.py +include azure/schemaregistry/__init__.py +include azure/schemaregistry/serializer/__init__.py +recursive-include tests *.py +recursive-include samples *.py \ No newline at end of file diff --git a/sdk/schemaregistry/azure-schemaregistry-avroserializer/README.md b/sdk/schemaregistry/azure-schemaregistry-avroserializer/README.md new file mode 100644 index 000000000000..c4ef1006c343 --- /dev/null +++ b/sdk/schemaregistry/azure-schemaregistry-avroserializer/README.md @@ -0,0 +1,174 @@ +# Azure Schema Registry Avro Serializer client library for Python + +Azure Schema Registry Avro Serializer provides the ability to serialize and deserialize data according +to the given avro schema. It is integrated with Azure Schema Registry SDK and will automatically register and get schema. + +[Source code][source_code] | [Package (PyPi)][pypi] | [API reference documentation][api_docs] | [Samples][sr_avro_samples] | [Changelog][change_log] + +## Getting started + +### Install the package + +Install the Azure Schema Registry Avro Serializer client library and Azure Identity client library for Python with [pip][pip]: + +```Bash +pip install azure-schemaregistry-avroserializer azure-identity +``` + +### Prerequisites: +To use this package, you must have: +* Azure subscription - [Create a free account][azure_sub] +* Azure Schema Registry +* Python 2.7, 3.5 or later - [Install Python][python] + +### Authenticate the client +Interaction with Schema Registry Avro Serializer starts with an instance of SchemaRegistryAvroSerializer class. You need the endpoint, AAD credential and schema group name to instantiate the client object. + +**Create client using the azure-identity library:** + +```python +from azure.schemaregistry import SchemaRegistryClient +from azure.schemaregistry.serializer.avroserializer import SchemaRegistryAvroSerializer +from azure.identity import DefaultAzureCredential + +credential = DefaultAzureCredential() +endpoint = '<< ENDPOINT OF THE SCHEMA REGISTRY >>' +schema_group = '<< GROUP NAME OF THE SCHEMA >>' +schema_registry_client = SchemaRegistryClient(endpoint, credential) +serializer = SchemaRegistryAvroSerializer(schema_registry_client, schema_group) +``` + +## Key concepts + +- Avro: Apache Avro™ is a data serialization system. + +## Examples + +The following sections provide several code snippets covering some of the most common Schema Registry tasks, including: + +- [Serialization](#serialization) +- [Deserialization](#deserialization) + +### Serialization + +```python +import os +from azure.schemaregistry import SchemaRegistryClient +from azure.schemaregistry.serializer.avroserializer import SchemaRegistryAvroSerializer +from azure.identity import DefaultAzureCredential + +token_credential = DefaultAzureCredential() +endpoint = os.environ['SCHEMA_REGISTRY_ENDPOINT'] +schema_group = "" + +schema_registry_client = SchemaRegistryClient(endpoint, token_credential) +serializer = SchemaRegistryAvroSerializer(schema_registry_client, schema_group) + +schema_string = """ +{"namespace": "example.avro", + "type": "record", + "name": "User", + "fields": [ + {"name": "name", "type": "string"}, + {"name": "favorite_number", "type": ["int", "null"]}, + {"name": "favorite_color", "type": ["string", "null"]} + ] +}""" + +with serializer: + dict_data = {"name": "Ben", "favorite_number": 7, "favorite_color": "red"} + encoded_bytes = serializer.serialize(dict_data, schema_string) +``` + +### Deserialization + +```python +import os +from azure.schemaregistry import SchemaRegistryClient +from azure.schemaregistry.serializer.avroserializer import SchemaRegistryAvroSerializer +from azure.identity import DefaultAzureCredential + +token_credential = DefaultAzureCredential() +endpoint = os.environ['SCHEMA_REGISTRY_ENDPOINT'] +schema_group = "" + +schema_registry_client = SchemaRegistryClient(endpoint, token_credential) +serializer = SchemaRegistryAvroSerializer(schema_registry_client, schema_group) + +with serializer: + encoded_bytes = b'' + decoded_data = serializer.deserialize(encoded_bytes) +``` + +## Troubleshooting + +### General + +Azure Schema Registry Avro Serializer raise exceptions defined in [Azure Core][azure_core]. + +### Logging +This library uses the standard +[logging][python_logging] library for logging. +Basic information about HTTP sessions (URLs, headers, etc.) is logged at INFO +level. + +Detailed DEBUG level logging, including request/response bodies and unredacted +headers, can be enabled on a client with the `logging_enable` argument: +```python +import sys +import logging +from azure.schemaregistry import SchemaRegistryClient +from azure.schemaregistry.serializer.avroserializer import SchemaRegistryAvroSerializer +from azure.identity import DefaultAzureCredential + +# Create a logger for the SDK +logger = logging.getLogger('azure.schemaregistry') +logger.setLevel(logging.DEBUG) + +# Configure a console output +handler = logging.StreamHandler(stream=sys.stdout) +logger.addHandler(handler) + +credential = DefaultAzureCredential() +schema_registry_client = SchemaRegistryClient("", credential) +# This client will log detailed information about its HTTP sessions, at DEBUG level +serializer = SchemaRegistryAvroSerializer(schema_registry_client, "", logging_enable=True) +``` + +Similarly, `logging_enable` can enable detailed logging for a single operation, +even when it isn't enabled for the client: +```py +serializer.serialie(dict_data, schema_content, logging_enable=True) +``` + +## Next steps + +### More sample code + +Please find further examples in the [samples][sr_avro_samples] directory demonstrating common Azure Schema Registry Avro Serializer scenarios. + +## 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 https://cla.microsoft.com. + +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](https://opensource.microsoft.com/codeofconduct/). +For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or +contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments. + + +[pip]: https://pypi.org/project/pip/ +[pypi]: https://pypi.org/project/azure-schemaregistry-avroserializer +[python]: https://www.python.org/downloads/ +[azure_core]: https://github.com/Azure/azure-sdk-for-python/blob/master/sdk/core/azure-core/README.md +[azure_sub]: https://azure.microsoft.com/free/ +[python_logging]: https://docs.python.org/3/library/logging.html +[sr_avro_samples]: https://github.com/Azure/azure-sdk-for-python/tree/master/sdk/schemaregistry/azure-schemaregistry-avroserializer/samples +[api_reference]: https://azuresdkdocs.blob.core.windows.net/$web/python/azure-schemaregistry-avroserializer/latest/index.html +[source_code]: https://github.com/Azure/azure-sdk-for-python/tree/master/sdk/schemaregistry/azure-schemaregistry-avroserializer +[change_log]: https://github.com/Azure/azure-sdk-for-python/tree/master/sdk/schemaregistry/azure-schemaregistry-avroserializer/CHANGELOG.md \ No newline at end of file diff --git a/sdk/schemaregistry/azure-schemaregistry-avroserializer/azure/__init__.py b/sdk/schemaregistry/azure-schemaregistry-avroserializer/azure/__init__.py new file mode 100644 index 000000000000..80f86cb969ec --- /dev/null +++ b/sdk/schemaregistry/azure-schemaregistry-avroserializer/azure/__init__.py @@ -0,0 +1,26 @@ +# -------------------------------------------------------------------------- +# +# Copyright (c) Microsoft Corporation. All rights reserved. +# +# The MIT License (MIT) +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the ""Software""), to +# deal in the Software without restriction, including without limitation the +# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +# sell copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +# IN THE SOFTWARE. +# +# -------------------------------------------------------------------------- +__path__ = __import__("pkgutil").extend_path(__path__, __name__) # type: ignore diff --git a/sdk/schemaregistry/azure-schemaregistry-avroserializer/azure/schemaregistry/__init__.py b/sdk/schemaregistry/azure-schemaregistry-avroserializer/azure/schemaregistry/__init__.py new file mode 100644 index 000000000000..c36aaed14908 --- /dev/null +++ b/sdk/schemaregistry/azure-schemaregistry-avroserializer/azure/schemaregistry/__init__.py @@ -0,0 +1,25 @@ +# -------------------------------------------------------------------------- +# +# Copyright (c) Microsoft Corporation. All rights reserved. +# +# The MIT License (MIT) +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the ""Software""), to +# deal in the Software without restriction, including without limitation the +# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +# sell copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +# IN THE SOFTWARE. +# +# -------------------------------------------------------------------------- diff --git a/sdk/schemaregistry/azure-schemaregistry-avroserializer/azure/schemaregistry/serializer/__init__.py b/sdk/schemaregistry/azure-schemaregistry-avroserializer/azure/schemaregistry/serializer/__init__.py new file mode 100644 index 000000000000..c36aaed14908 --- /dev/null +++ b/sdk/schemaregistry/azure-schemaregistry-avroserializer/azure/schemaregistry/serializer/__init__.py @@ -0,0 +1,25 @@ +# -------------------------------------------------------------------------- +# +# Copyright (c) Microsoft Corporation. All rights reserved. +# +# The MIT License (MIT) +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the ""Software""), to +# deal in the Software without restriction, including without limitation the +# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +# sell copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +# IN THE SOFTWARE. +# +# -------------------------------------------------------------------------- diff --git a/sdk/schemaregistry/azure-schemaregistry-avroserializer/azure/schemaregistry/serializer/avroserializer/__init__.py b/sdk/schemaregistry/azure-schemaregistry-avroserializer/azure/schemaregistry/serializer/avroserializer/__init__.py new file mode 100644 index 000000000000..fe999769d03e --- /dev/null +++ b/sdk/schemaregistry/azure-schemaregistry-avroserializer/azure/schemaregistry/serializer/avroserializer/__init__.py @@ -0,0 +1,34 @@ +# -------------------------------------------------------------------------- +# +# Copyright (c) Microsoft Corporation. All rights reserved. +# +# The MIT License (MIT) +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the ""Software""), to +# deal in the Software without restriction, including without limitation the +# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +# sell copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +# IN THE SOFTWARE. +# +# -------------------------------------------------------------------------- +from ._version import VERSION + +__version__ = VERSION + +from ._schema_registry_avro_serializer import SchemaRegistryAvroSerializer + +__all__ = [ + "SchemaRegistryAvroSerializer" +] diff --git a/sdk/schemaregistry/azure-schemaregistry-avroserializer/azure/schemaregistry/serializer/avroserializer/_avro_serializer.py b/sdk/schemaregistry/azure-schemaregistry-avroserializer/azure/schemaregistry/serializer/avroserializer/_avro_serializer.py new file mode 100644 index 000000000000..a190bffe5467 --- /dev/null +++ b/sdk/schemaregistry/azure-schemaregistry-avroserializer/azure/schemaregistry/serializer/avroserializer/_avro_serializer.py @@ -0,0 +1,109 @@ +# -------------------------------------------------------------------------- +# +# Copyright (c) Microsoft Corporation. All rights reserved. +# +# The MIT License (MIT) +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the ""Software""), to +# deal in the Software without restriction, including without limitation the +# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +# sell copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +# IN THE SOFTWARE. +# +# -------------------------------------------------------------------------- +from typing import BinaryIO, Union, TypeVar, Dict +from io import BytesIO +import avro +from avro.io import DatumWriter, DatumReader, BinaryDecoder, BinaryEncoder + +ObjectType = TypeVar("ObjectType") + + +class AvroObjectSerializer(object): + + def __init__(self, codec=None): + """A Avro serializer using avro lib from Apache. + :param str codec: The writer codec. If None, let the avro library decides. + """ + self._writer_codec = codec + self._schema_writer_cache = {} # type: Dict[str, DatumWriter] + self._schema_reader_cache = {} # type: Dict[str, DatumReader] + + def serialize( + self, + data, # type: ObjectType + schema, # type: Union[str, bytes, avro.schema.Schema] + ): + # type: (ObjectType, Union[str, bytes, avro.schema.Schema]) -> bytes + """Convert the provided value to it's binary representation and write it to the stream. + Schema must be a Avro RecordSchema: + https://avro.apache.org/docs/1.10.0/gettingstartedpython.html#Defining+a+schema + :param data: An object to serialize + :type data: ObjectType + :param schema: An Avro RecordSchema + :type schema: Union[str, bytes, avro.schema.Schema] + :returns: Encoded bytes + :rtype: bytes + """ + if not schema: + raise ValueError("Schema is required in Avro serializer.") + + if not isinstance(schema, avro.schema.Schema): + schema = avro.schema.parse(schema) + + try: + writer = self._schema_writer_cache[str(schema)] + except KeyError: + writer = DatumWriter(schema) + self._schema_writer_cache[str(schema)] = writer + + stream = BytesIO() + with stream: + writer.write(data, BinaryEncoder(stream)) + encoded_data = stream.getvalue() + return encoded_data + + def deserialize( + self, + data, # type: Union[bytes, BinaryIO] + schema, # type: Union[str, bytes, avro.schema.Schema] + ): + # type: (Union[bytes, BinaryIO], Union[str, bytes, avro.schema.Schema]) -> ObjectType + """Read the binary representation into a specific type. + Return type will be ignored, since the schema is deduced from the provided bytes. + :param data: A stream of bytes or bytes directly + :type data: BinaryIO or bytes + :param schema: An Avro RecordSchema + :type schema: Union[str, bytes, avro.schema.Schema] + :returns: An instantiated object + :rtype: ObjectType + """ + if not hasattr(data, 'read'): + data = BytesIO(data) + + if not isinstance(schema, avro.schema.Schema): + schema = avro.schema.parse(schema) + + try: + reader = self._schema_reader_cache[str(schema)] + except KeyError: + reader = DatumReader(writers_schema=schema) + self._schema_reader_cache[str(schema)] = reader + + with data: + bin_decoder = BinaryDecoder(data) + decoded_data = reader.read(bin_decoder) + + return decoded_data diff --git a/sdk/schemaregistry/azure-schemaregistry-avroserializer/azure/schemaregistry/serializer/avroserializer/_constants.py b/sdk/schemaregistry/azure-schemaregistry-avroserializer/azure/schemaregistry/serializer/avroserializer/_constants.py new file mode 100644 index 000000000000..a95ec7dd5088 --- /dev/null +++ b/sdk/schemaregistry/azure-schemaregistry-avroserializer/azure/schemaregistry/serializer/avroserializer/_constants.py @@ -0,0 +1,36 @@ +# -------------------------------------------------------------------------- +# +# Copyright (c) Microsoft Corporation. All rights reserved. +# +# The MIT License (MIT) +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the ""Software""), to +# deal in the Software without restriction, including without limitation the +# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +# sell copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +# IN THE SOFTWARE. +# +# -------------------------------------------------------------------------- + +# encoded payload is consisted of: +# 0~3: 4 bytes denoting record format identifier +# 4~35: 32 bytes denoting schema id +# 36~END: encode data + +RECORD_FORMAT_IDENTIFIER_START_INDEX = 0 +RECORD_FORMAT_IDENTIFIER_LENGTH = 4 +SCHEMA_ID_START_INDEX = 4 +SCHEMA_ID_LENGTH = 32 +DATA_START_INDEX = 36 diff --git a/sdk/schemaregistry/azure-schemaregistry-avroserializer/azure/schemaregistry/serializer/avroserializer/_schema_registry_avro_serializer.py b/sdk/schemaregistry/azure-schemaregistry-avroserializer/azure/schemaregistry/serializer/avroserializer/_schema_registry_avro_serializer.py new file mode 100644 index 000000000000..a09e7e6bd9d0 --- /dev/null +++ b/sdk/schemaregistry/azure-schemaregistry-avroserializer/azure/schemaregistry/serializer/avroserializer/_schema_registry_avro_serializer.py @@ -0,0 +1,164 @@ +# -------------------------------------------------------------------------- +# +# Copyright (c) Microsoft Corporation. All rights reserved. +# +# The MIT License (MIT) +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the ""Software""), to +# deal in the Software without restriction, including without limitation the +# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +# sell copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +# IN THE SOFTWARE. +# +# -------------------------------------------------------------------------- +from io import BytesIO +from typing import Any, Dict, Union +import avro + +from ._constants import SCHEMA_ID_START_INDEX, SCHEMA_ID_LENGTH, DATA_START_INDEX +from ._avro_serializer import AvroObjectSerializer + + +class SchemaRegistryAvroSerializer(object): + """ + SchemaRegistryAvroSerializer provides the ability to serialize and deserialize data according + to the given avro schema. It would automatically register, get and cache the schema. + + :param schema_registry: The schema registry client + which is used to register schema and retrieve schema from the service. + :type schema_registry: ~azure.schemaregistry.SchemaRegistryClient + :param str schema_group: Schema group under which schema should be registered. + :keyword str codec: The writer codec. If None, let the avro library decides. + + """ + def __init__(self, schema_registry, schema_group, **kwargs): + # type: ("SchemaRegistryClient", str, Any) -> None + self._schema_group = schema_group + self._avro_serializer = AvroObjectSerializer(codec=kwargs.get("codec")) + self._schema_registry_client = schema_registry # type: "SchemaRegistryClient" + self._id_to_schema = {} + self._schema_to_id = {} + self._user_input_schema_cache = {} + + def __enter__(self): + # type: () -> SchemaRegistryAvroSerializer + self._schema_registry_client.__enter__() + return self + + def __exit__(self, *exc_details): + # type: (Any) -> None + self._schema_registry_client.__exit__(*exc_details) + + def close(self): + # type: () -> None + """ This method is to close the sockets opened by the client. + It need not be used when using with a context manager. + """ + self._schema_registry_client.close() + + def _get_schema_id(self, schema_name, schema, **kwargs): + # type: (str, avro.schema.Schema, Any) -> str + """ + Get schema id from local cache with the given schema. + If there is no item in the local cache, get schema id from the service and cache it. + + :param schema_name: Name of the schema + :type schema_name: str + :param schema: Schema object + :type schema: avro.schema.Schema + :return: Schema Id + :rtype: str + """ + schema_str = str(schema) + try: + return self._schema_to_id[schema_str] + except KeyError: + schema_id = self._schema_registry_client.register_schema( + self._schema_group, + schema_name, + "Avro", + schema_str, + **kwargs + ).schema_id + self._schema_to_id[schema_str] = schema_id + self._id_to_schema[schema_id] = schema_str + return schema_id + + def _get_schema(self, schema_id, **kwargs): + # type: (str, Any) -> str + """ + Get schema content from local cache with the given schema id. + If there is no item in the local cache, get schema from the service and cache it. + + :param str schema_id: Schema id + :return: Schema content + """ + try: + return self._id_to_schema[schema_id] + except KeyError: + schema_str = self._schema_registry_client.get_schema(schema_id, **kwargs).schema_content + self._id_to_schema[schema_id] = schema_str + self._schema_to_id[schema_str] = schema_id + return schema_str + + def serialize(self, data, schema, **kwargs): + # type: (Dict[str, Any], Union[str, bytes], Any) -> bytes + """ + Encode dict data with the given schema. The returns bytes are consisted of: The first 4 bytes + denoting record format identifier. The following 32 bytes denoting schema id returned by schema registry + service. The remaining bytes are the real data payload. + + :param data: The dict data to be encoded. + :param schema: The schema used to encode the data. + :type schema: Union[str, bytes] + :return: + """ + raw_input_schema = schema + try: + cached_schema = self._user_input_schema_cache[raw_input_schema] + except KeyError: + parsed_schema = avro.schema.parse(raw_input_schema) + self._user_input_schema_cache[raw_input_schema] = parsed_schema + cached_schema = parsed_schema + + record_format_identifier = b'\0\0\0\0' + schema_id = self._get_schema_id(cached_schema.fullname, cached_schema, **kwargs) + data_bytes = self._avro_serializer.serialize(data, cached_schema) + + stream = BytesIO() + + stream.write(record_format_identifier) + stream.write(schema_id.encode('utf-8')) + stream.write(data_bytes) + stream.flush() + + payload = stream.getvalue() + stream.close() + return payload + + def deserialize(self, data, **kwargs): + # type: (bytes, Any) -> Dict[str, Any] + """ + Decode bytes data. + + :param bytes data: The bytes data needs to be decoded. + :rtype: Dict[str, Any] + """ + # record_format_identifier = data[0:4] # The first 4 bytes are retained for future record format identifier. + schema_id = data[SCHEMA_ID_START_INDEX:(SCHEMA_ID_START_INDEX + SCHEMA_ID_LENGTH)].decode('utf-8') + schema_content = self._get_schema(schema_id, **kwargs) + + dict_data = self._avro_serializer.deserialize(data[DATA_START_INDEX:], schema_content) + return dict_data diff --git a/sdk/schemaregistry/azure-schemaregistry-avroserializer/azure/schemaregistry/serializer/avroserializer/_version.py b/sdk/schemaregistry/azure-schemaregistry-avroserializer/azure/schemaregistry/serializer/avroserializer/_version.py new file mode 100644 index 000000000000..d316addb36cb --- /dev/null +++ b/sdk/schemaregistry/azure-schemaregistry-avroserializer/azure/schemaregistry/serializer/avroserializer/_version.py @@ -0,0 +1,27 @@ +# -------------------------------------------------------------------------- +# +# Copyright (c) Microsoft Corporation. All rights reserved. +# +# The MIT License (MIT) +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the ""Software""), to +# deal in the Software without restriction, including without limitation the +# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +# sell copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +# IN THE SOFTWARE. +# +# -------------------------------------------------------------------------- + +VERSION = "1.0.0b1" diff --git a/sdk/schemaregistry/azure-schemaregistry-avroserializer/dev_requirements.txt b/sdk/schemaregistry/azure-schemaregistry-avroserializer/dev_requirements.txt new file mode 100644 index 000000000000..c2d5098aa720 --- /dev/null +++ b/sdk/schemaregistry/azure-schemaregistry-avroserializer/dev_requirements.txt @@ -0,0 +1,4 @@ +-e ../../../tools/azure-devtools +-e ../../../tools/azure-sdk-tools +-e ../../identity/azure-identity +../azure-schemaregistry \ No newline at end of file diff --git a/sdk/schemaregistry/azure-schemaregistry-avroserializer/mypy.ini b/sdk/schemaregistry/azure-schemaregistry-avroserializer/mypy.ini new file mode 100644 index 000000000000..c5adef3f8a6d --- /dev/null +++ b/sdk/schemaregistry/azure-schemaregistry-avroserializer/mypy.ini @@ -0,0 +1,6 @@ +[mypy] +python_version = 3.7 +warn_unused_configs = True +ignore_missing_imports = True + +# Per-module options: diff --git a/sdk/schemaregistry/azure-schemaregistry-avroserializer/samples/README.md b/sdk/schemaregistry/azure-schemaregistry-avroserializer/samples/README.md new file mode 100644 index 000000000000..5c235b8bccba --- /dev/null +++ b/sdk/schemaregistry/azure-schemaregistry-avroserializer/samples/README.md @@ -0,0 +1,50 @@ +--- +page_type: sample +languages: + - python +products: + - azure + - azure-schema-registry-avro-serializer +urlFragment: schemaregistry-avro-serializer-samples +--- + +# Azure Schema Registry Avro Serializer library for Python Samples + +These are code samples that show common scenario operations with the Schema Registry Avro Serializer library. + +Several Schema Registry Avro Serializer Python SDK samples are available to you in the SDK's GitHub repository. These samples provide example code for additional scenarios commonly encountered while working with Schema Registry Avro Serializer: + +* [avro_serializer.py][avro_serializer_sample] - Examples for common Schema Registry Avro Serializer tasks: + * Serialize data according to the given schema + * Deserialize data + +## Prerequisites +- Python 2.7, 3.5 or later. +- **Microsoft Azure Subscription:** To use Azure services, including Azure Schema Registry, you'll need a subscription. +If you do not have an existing Azure account, you may sign up for a free trial or use your MSDN subscriber benefits when you [create an account](https://account.windowsazure.com/Home/Index). + +## Setup + +1. Install the Azure Schema Registry Avro Serializer client library and Azure Identity client library for Python with [pip](https://pypi.org/project/pip/): + +```bash +pip install azure-schemaregistry-avroserializer azure-identity +``` + +2. Clone or download this sample repository +3. Open the sample folder in Visual Studio Code or your IDE of choice. + +## Running the samples + +1. Open a terminal window and `cd` to the directory that the samples are saved in. +2. Set the environment variables specified in the sample file you wish to run. +3. Follow the usage described in the file, e.g. `python avro_serializer.py` + +## Next steps + +Check out the [API reference documentation][api_reference] to learn more about +what you can do with the Azure Schema Registry Avro Serializer library. + + +[avro_serializer_sample]: https://github.com/Azure/azure-sdk-for-python/tree/master/sdk/schemaregistry/azure-schemaregistry-avroserializer/samples/avro_serializer.py +[api_reference]: https://azuresdkdocs.blob.core.windows.net/$web/python/azure-schemaregistry-avroserializer/latest/index.html diff --git a/sdk/schemaregistry/azure-schemaregistry-avroserializer/samples/avro_serializer.py b/sdk/schemaregistry/azure-schemaregistry-avroserializer/samples/avro_serializer.py new file mode 100644 index 000000000000..031ce09c7aa6 --- /dev/null +++ b/sdk/schemaregistry/azure-schemaregistry-avroserializer/samples/avro_serializer.py @@ -0,0 +1,87 @@ +# -------------------------------------------------------------------------- +# +# Copyright (c) Microsoft Corporation. All rights reserved. +# +# The MIT License (MIT) +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the ""Software""), to +# deal in the Software without restriction, including without limitation the +# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +# sell copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +# IN THE SOFTWARE. +# +# -------------------------------------------------------------------------- +import os + +from azure.identity import ClientSecretCredential +from azure.schemaregistry import SchemaRegistryClient +from azure.schemaregistry.serializer.avroserializer import SchemaRegistryAvroSerializer + +TENANT_ID=os.environ['SCHEMA_REGISTRY_AZURE_TENANT_ID'] +CLIENT_ID=os.environ['SCHEMA_REGISTRY_AZURE_CLIENT_ID'] +CLIENT_SECRET=os.environ['SCHEMA_REGISTRY_AZURE_CLIENT_SECRET'] + +SCHEMA_REGISTRY_ENDPOINT=os.environ['SCHEMA_REGISTRY_ENDPOINT'] +SCHEMA_GROUP=os.environ['SCHEMA_REGISTRY_GROUP'] +SCHEMA_STRING = """ +{"namespace": "example.avro", + "type": "record", + "name": "User", + "fields": [ + {"name": "name", "type": "string"}, + {"name": "favorite_number", "type": ["int", "null"]}, + {"name": "favorite_color", "type": ["string", "null"]} + ] +}""" + + +token_credential = ClientSecretCredential( + tenant_id=TENANT_ID, + client_id=CLIENT_ID, + client_secret=CLIENT_SECRET +) + + +def serialize(serializer): + dict_data_ben = {"name": u"Ben", "favorite_number": 7, "favorite_color": u"red"} + dict_data_alice = {"name": u"Alice", "favorite_number": 15, "favorite_color": u"green"} + + # Schema would be automatically registered into Schema Registry and cached locally. + payload_ben = serializer.serialize(dict_data_ben, SCHEMA_STRING) + # The second call won't trigger a service call. + payload_alice = serializer.serialize(dict_data_alice, SCHEMA_STRING) + + print('Encoded bytes are: ', payload_ben) + print('Encoded bytes are: ', payload_alice) + return [payload_ben, payload_alice] + + +def deserialize(serializer, bytes_payload): + # serializer.deserialize would extract the schema id from the payload, + # retrieve schema from Schema Registry and cache the schema locally. + # If the schema id is the local cache, the call won't trigger a service call. + dict_data = serializer.deserialize(bytes_payload) + + print('Deserialized data is: ', dict_data) + return dict_data + + +if __name__ == '__main__': + schema_registry = SchemaRegistryClient(endpoint=SCHEMA_REGISTRY_ENDPOINT, credential=token_credential) + serializer = SchemaRegistryAvroSerializer(schema_registry, SCHEMA_GROUP) + bytes_data_ben, bytes_data_alice = serialize(serializer) + dict_data_ben = deserialize(serializer, bytes_data_ben) + dict_data_alice = deserialize(serializer, bytes_data_alice) + serializer.close() diff --git a/sdk/schemaregistry/azure-schemaregistry-avroserializer/sdk_packaging.toml b/sdk/schemaregistry/azure-schemaregistry-avroserializer/sdk_packaging.toml new file mode 100644 index 000000000000..e7687fdae93b --- /dev/null +++ b/sdk/schemaregistry/azure-schemaregistry-avroserializer/sdk_packaging.toml @@ -0,0 +1,2 @@ +[packaging] +auto_update = false \ No newline at end of file diff --git a/sdk/schemaregistry/azure-schemaregistry-avroserializer/setup.cfg b/sdk/schemaregistry/azure-schemaregistry-avroserializer/setup.cfg new file mode 100644 index 000000000000..3c6e79cf31da --- /dev/null +++ b/sdk/schemaregistry/azure-schemaregistry-avroserializer/setup.cfg @@ -0,0 +1,2 @@ +[bdist_wheel] +universal=1 diff --git a/sdk/schemaregistry/azure-schemaregistry-avroserializer/setup.py b/sdk/schemaregistry/azure-schemaregistry-avroserializer/setup.py new file mode 100644 index 000000000000..c97916e14530 --- /dev/null +++ b/sdk/schemaregistry/azure-schemaregistry-avroserializer/setup.py @@ -0,0 +1,71 @@ +#!/usr/bin/env python + +# ------------------------------------------------------------------------ +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for +# license information. +# ------------------------------------------------------------------------- + +import re +import os.path +from io import open +from setuptools import find_packages, setup + +# Change the PACKAGE_NAME only to change folder and different name +PACKAGE_NAME = "azure-schemaregistry-avroserializer" +PACKAGE_PPRINT_NAME = "Schema Registry Avro Serializer" + +package_folder_path = "azure/schemaregistry/serializer/avroserializer" +namespace_name = "azure.schemaregistry.serializer.avroserializer" + +# Version extraction inspired from 'requests' +with open(os.path.join(package_folder_path, '_version.py'), 'r') as fd: + version = re.search(r'^VERSION\s*=\s*[\'"]([^\'"]*)[\'"]', + fd.read(), re.MULTILINE).group(1) + +if not version: + raise RuntimeError('Cannot find version information') + +with open('README.md', encoding='utf-8') as f: + readme = f.read() +with open('CHANGELOG.md', encoding='utf-8') as f: + changelog = f.read() + +exclude_packages = [ + 'tests', + 'samples', + # Exclude packages that will be covered by PEP420 or nspkg + 'azure', + 'azure.schemaregistry', + 'azure.schemaregistry.serializer' + ] + +setup( + name=PACKAGE_NAME, + version=version, + description='Microsoft Azure {} Client Library for Python'.format(PACKAGE_PPRINT_NAME), + long_description=readme + '\n\n' + changelog, + long_description_content_type='text/markdown', + license='MIT License', + author='Microsoft Corporation', + author_email='azpysdkhelp@microsoft.com', + url='https://github.com/Azure/azure-sdk-for-python', + classifiers=[ + "Development Status :: 4 - Beta", + 'Programming Language :: Python', + 'Programming Language :: Python :: 2', + 'Programming Language :: Python :: 2.7', + 'Programming Language :: Python :: 3', + '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, + packages=find_packages(exclude=exclude_packages), + install_requires=[ + 'azure-schemaregistry<2.0.0,>=1.0.0b1', + 'avro<2.0.0,>=1.10.0' + ] +) diff --git a/sdk/schemaregistry/azure-schemaregistry-avroserializer/tests/recordings/test_avro_serializer.test_basic_sr_avro_serializer.yaml b/sdk/schemaregistry/azure-schemaregistry-avroserializer/tests/recordings/test_avro_serializer.test_basic_sr_avro_serializer.yaml new file mode 100644 index 000000000000..c23e6224d483 --- /dev/null +++ b/sdk/schemaregistry/azure-schemaregistry-avroserializer/tests/recordings/test_avro_serializer.test_basic_sr_avro_serializer.yaml @@ -0,0 +1,104 @@ +interactions: +- request: + body: '"{\"type\": \"record\", \"name\": \"User\", \"namespace\": \"example.avro\", + \"fields\": [{\"type\": \"string\", \"name\": \"name\"}, {\"type\": [\"int\", + \"null\"], \"name\": \"favorite_number\"}, {\"type\": [\"string\", \"null\"], + \"name\": \"favorite_color\"}]}"' + headers: + Accept: + - application/json + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + Content-Length: + - '265' + Content-Type: + - application/json + User-Agent: + - azsdk-python-azureschemaregistry/1.0.0b1 Python/3.7.7 (Windows-10-10.0.19041-SP0) + X-Schema-Type: + - Avro + method: PUT + uri: https://sr-playground.servicebus.windows.net/$schemagroups/azsdk_python_test_group/schemas/example.avro.User?api-version=2017-04 + response: + body: + string: '{"id":"ec9977eee8d84c178db2bcb9f9685ac8"}' + headers: + content-type: + - application/json + date: + - Fri, 04 Sep 2020 22:09:09 GMT + location: + - https://sr-playground.servicebus.windows.net:443/$schemagroups/azsdk_python_test_group/schemas/example.avro.User/versions/1?api-version=2017-04 + server: + - Microsoft-HTTPAPI/2.0 + strict-transport-security: + - max-age=31536000 + transfer-encoding: + - chunked + x-schema-id: + - ec9977eee8d84c178db2bcb9f9685ac8 + x-schema-id-location: + - https://sr-playground.servicebus.windows.net:443/$schemagroups/getschemabyid/ec9977eee8d84c178db2bcb9f9685ac8?api-version=2017-04 + x-schema-type: + - Avro + x-schema-version: + - '1' + x-schema-versions-location: + - https://sr-playground.servicebus.windows.net:443/$schemagroups/azsdk_python_test_group/schemas/example.avro.User/versions?api-version=2017-04 + status: + code: 200 + message: OK +- request: + body: '"{\"type\": \"record\", \"name\": \"User\", \"namespace\": \"example.avro\", + \"fields\": [{\"type\": \"string\", \"name\": \"name\"}, {\"type\": [\"int\", + \"null\"], \"name\": \"favorite_number\"}, {\"type\": [\"string\", \"null\"], + \"name\": \"favorite_color\"}]}"' + headers: + Accept: + - application/json + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + Content-Length: + - '265' + Content-Type: + - application/json + User-Agent: + - azsdk-python-azureschemaregistry/1.0.0b1 Python/3.7.7 (Windows-10-10.0.19041-SP0) + X-Schema-Type: + - Avro + method: POST + uri: https://sr-playground.servicebus.windows.net/$schemagroups/azsdk_python_test_group/schemas/example.avro.User?api-version=2017-04 + response: + body: + string: '{"id":"ec9977eee8d84c178db2bcb9f9685ac8"}' + headers: + content-type: + - application/json + date: + - Fri, 04 Sep 2020 22:09:09 GMT + location: + - https://sr-playground.servicebus.windows.net:443/$schemagroups/azsdk_python_test_group/schemas/example.avro.User/versions/1?api-version=2017-04 + server: + - Microsoft-HTTPAPI/2.0 + strict-transport-security: + - max-age=31536000 + transfer-encoding: + - chunked + x-schema-id: + - ec9977eee8d84c178db2bcb9f9685ac8 + x-schema-id-location: + - https://sr-playground.servicebus.windows.net:443/$schemagroups/getschemabyid/ec9977eee8d84c178db2bcb9f9685ac8?api-version=2017-04 + x-schema-type: + - Avro + x-schema-version: + - '1' + x-schema-versions-location: + - https://sr-playground.servicebus.windows.net:443/$schemagroups/azsdk_python_test_group/schemas/example.avro.User/versions?api-version=2017-04 + status: + code: 200 + message: OK +version: 1 diff --git a/sdk/schemaregistry/azure-schemaregistry-avroserializer/tests/schemaregistry_preparer.py b/sdk/schemaregistry/azure-schemaregistry-avroserializer/tests/schemaregistry_preparer.py new file mode 100644 index 000000000000..f1f78608d0d0 --- /dev/null +++ b/sdk/schemaregistry/azure-schemaregistry-avroserializer/tests/schemaregistry_preparer.py @@ -0,0 +1,73 @@ +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the ""Software""), to +# deal in the Software without restriction, including without limitation the +# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +# sell copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +# IN THE SOFTWARE. +# +# -------------------------------------------------------------------------- + +import functools +import hashlib +import os +from collections import namedtuple + +from azure.identity import ClientSecretCredential +from azure_devtools.scenario_tests.exceptions import AzureTestError +from devtools_testutils import ( + ResourceGroupPreparer, AzureMgmtPreparer, FakeResource +) + + +SCHEMA_REGISTRY_ENDPOINT_PARAM = "schemaregistry_endpoint" +SCHEMA_REGISTRY_GROUP_PARAM = "schemaregistry_group" +SCHEMA_REGISTRY_ENDPOINT_ENV_KEY_NAME = 'SCHEMA_REGISTRY_ENDPOINT' +SCHEMA_REGISTRY_GROUP_ENV_KEY_NAME = 'SCHEMA_REGISTRY_GROUP' + + +class SchemaRegistryNamespacePreparer(AzureMgmtPreparer): + # TODO: SR doesn't have mgmt package + def __init__(self): + pass + + def create_resource(self, name, **kwargs): + pass + + def remove_resource(self, name, **kwargs): + pass + + +class SchemaRegistryPreparer(AzureMgmtPreparer): + def __init__( + self, + name_prefix='' + ): + super(SchemaRegistryPreparer, self).__init__(name_prefix, 24) + + def create_resource(self, name, **kwargs): + # TODO: right now the endpoint/group is fixed, as there is no way to create/delete resources using api, in the future we should be able to dynamically create and remove resources + if self.is_live: + return { + SCHEMA_REGISTRY_ENDPOINT_PARAM: os.environ[SCHEMA_REGISTRY_ENDPOINT_ENV_KEY_NAME], + SCHEMA_REGISTRY_GROUP_PARAM: os.environ[SCHEMA_REGISTRY_GROUP_ENV_KEY_NAME] + } + + else: + return { + SCHEMA_REGISTRY_ENDPOINT_PARAM: "sr-playground.servicebus.windows.net", + SCHEMA_REGISTRY_GROUP_PARAM: "azsdk_python_test_group" + } + + def remove_resource(self, name, **kwargs): + pass diff --git a/sdk/schemaregistry/azure-schemaregistry-avroserializer/tests/test_avro_serializer.py b/sdk/schemaregistry/azure-schemaregistry-avroserializer/tests/test_avro_serializer.py new file mode 100644 index 000000000000..31ceb1933385 --- /dev/null +++ b/sdk/schemaregistry/azure-schemaregistry-avroserializer/tests/test_avro_serializer.py @@ -0,0 +1,101 @@ +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the ""Software""), to +# deal in the Software without restriction, including without limitation the +# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +# sell copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +# IN THE SOFTWARE. +# +# -------------------------------------------------------------------------- +import pytest +import uuid +import avro +import avro.io +from io import BytesIO + +from azure.schemaregistry import SchemaRegistryClient +from azure.schemaregistry.serializer.avroserializer import SchemaRegistryAvroSerializer +from azure.schemaregistry.serializer.avroserializer._avro_serializer import AvroObjectSerializer +from azure.identity import ClientSecretCredential +from azure.core.exceptions import ClientAuthenticationError, ServiceRequestError, HttpResponseError + +from schemaregistry_preparer import SchemaRegistryPreparer +from devtools_testutils import AzureTestCase + + +class SchemaRegistryAvroSerializerTests(AzureTestCase): + + def test_raw_avro_serializer(self): + schema_str = """{"namespace":"example.avro","type":"record","name":"User","fields":[{"name":"name","type":"string"},{"name":"favorite_number","type":["int","null"]},{"name":"favorite_color","type":["string","null"]}]}""" + schema = avro.schema.parse(schema_str) + dict_data = {"name": u"Ben", "favorite_number": 7, "favorite_color": u"red"} + + raw_avro_object_serializer = AvroObjectSerializer() + + # encoding part + encoded_payload = raw_avro_object_serializer.serialize(dict_data, schema) + + # decoding part + decoded_data = raw_avro_object_serializer.deserialize(encoded_payload, schema) + + assert decoded_data["name"] == u"Ben" + assert decoded_data["favorite_number"] == 7 + assert decoded_data["favorite_color"] == u"red" + + dict_data_missing_optional_fields = {"name": u"Alice"} + encoded_payload = raw_avro_object_serializer.serialize(dict_data_missing_optional_fields, schema) + decoded_data = raw_avro_object_serializer.deserialize(encoded_payload, schema) + + assert decoded_data["name"] == u"Alice" + assert not decoded_data["favorite_number"] + assert not decoded_data["favorite_color"] + + def test_raw_avro_serializer_negative(self): + schema_str = """{"namespace":"example.avro","type":"record","name":"User","fields":[{"name":"name","type":"string"},{"name":"favorite_number","type":["int","null"]},{"name":"favorite_color","type":["string","null"]}]}""" + schema = avro.schema.parse(schema_str) + + raw_avro_object_serializer = AvroObjectSerializer() + dict_data_wrong_type = {"name": u"Ben", "favorite_number": u"something", "favorite_color": u"red"} + with pytest.raises(avro.io.AvroTypeException): + raw_avro_object_serializer.serialize(dict_data_wrong_type, schema) + + dict_data_missing_required_field = {"favorite_number": 7, "favorite_color": u"red"} + with pytest.raises(avro.io.AvroTypeException): + raw_avro_object_serializer.serialize(dict_data_missing_required_field, schema) + + @SchemaRegistryPreparer() + def test_basic_sr_avro_serializer(self, schemaregistry_endpoint, schemaregistry_group, **kwargs): + sr_client = self.create_basic_client(SchemaRegistryClient, endpoint=schemaregistry_endpoint) + sr_avro_serializer = SchemaRegistryAvroSerializer(sr_client, schemaregistry_group) + + schema_str = """{"namespace":"example.avro","type":"record","name":"User","fields":[{"name":"name","type":"string"},{"name":"favorite_number","type":["int","null"]},{"name":"favorite_color","type":["string","null"]}]}""" + schema = avro.schema.parse(schema_str) + + dict_data = {"name": u"Ben", "favorite_number": 7, "favorite_color": u"red"} + encoded_data = sr_avro_serializer.serialize(dict_data, schema_str) + + assert schema_str in sr_avro_serializer._user_input_schema_cache + assert str(avro.schema.parse(schema_str)) in sr_avro_serializer._schema_to_id + + assert encoded_data[0:4] == b'\0\0\0\0' + schema_id = sr_client.get_schema_id(schemaregistry_group, schema.fullname, "Avro", str(schema)).schema_id + assert encoded_data[4:36] == schema_id.encode("utf-8") + + assert schema_id in sr_avro_serializer._id_to_schema + + decoded_data = sr_avro_serializer.deserialize(encoded_data) + assert decoded_data["name"] == u"Ben" + assert decoded_data["favorite_number"] == 7 + assert decoded_data["favorite_color"] == u"red" + + sr_avro_serializer.close() diff --git a/sdk/schemaregistry/azure-schemaregistry/CHANGELOG.md b/sdk/schemaregistry/azure-schemaregistry/CHANGELOG.md index 332564950c28..22deb5fe313e 100644 --- a/sdk/schemaregistry/azure-schemaregistry/CHANGELOG.md +++ b/sdk/schemaregistry/azure-schemaregistry/CHANGELOG.md @@ -1,3 +1,12 @@ # Release History -## 1.0.0b1 (Unreleased) +## 1.0.0b1 (2020-09-08) + +Version 1.0.0b1 is the first preview of our efforts to create a user-friendly and Pythonic client library for Azure Schema Registry. + +**New features** + +- `SchemaRegistryClient` is the top-level client class interacting with the Azure Schema Registry Service. It provides three methods: + - `register_schema`: Store schema into the service. + - `get_schema`: Get schema content and its properties by schema id. + - `get_schema_id`: Get schema id and its properties by schema group, schema name, serialization type and schema content. diff --git a/sdk/schemaregistry/azure-schemaregistry/README.md b/sdk/schemaregistry/azure-schemaregistry/README.md index 412d4a03b90e..8ef6b8790650 100644 --- a/sdk/schemaregistry/azure-schemaregistry/README.md +++ b/sdk/schemaregistry/azure-schemaregistry/README.md @@ -1,18 +1,177 @@ # Azure Schema Registry client library for Python -Azure Schema Registry is a ... -<> +Azure Schema Registry is a service that provides the ability to store and retrieve different types of schemas such as +Avro, Json, etc. + +[Source code][source_code] | [Package (PyPi)][pypi] | [API reference documentation][api_reference] | [Samples][sr_samples] | [Changelog][change_log] ## Getting started +### Install the package + +Install the Azure Schema Registry client library and Azure Identity client library for Python with [pip][pip]: + +```Bash +pip install azure-schemaregistry azure-identity +``` + +### Prerequisites: +To use this package, you must have: +* Azure subscription - [Create a free account][azure_sub] +* Azure Schema Registry +* Python 2.7, 3.5 or later - [Install Python][python] + +### Authenticate the client +Interaction with Schema Registry starts with an instance of SchemaRegistryClient class. You need the endpoint and AAD credential to instantiate the client object. + +**Create client using the azure-identity library:** + +```python +from azure.schemaregistry import SchemaRegistryClient +from azure.identity import DefaultAzureCredential + +credential = DefaultAzureCredential() +endpoint = '<< ENDPOINT OF THE SCHEMA REGISTRY >>' +schema_registry_client = SchemaRegistryClient(endpoint, credential) +``` + ## Key concepts +- Schema: Schema is the organization or structure for data. + ## Examples +The following sections provide several code snippets covering some of the most common Schema Registry tasks, including: + +- [Register a schema](#register-a-schema) +- [Get the schema by id](#get-the-schema-by-id) +- [Get the id of a schema](#get-the-id-of-a-schema) + +### Register a schema + +```python +import os + +from azure.identity import DefaultAzureCredential +from azure.schemaregistry import SchemaRegistryClient + +token_credential = DefaultAzureCredential() +endpoint = os.environ['SCHEMA_REGISTRY_ENDPOINT'] +schema_group = "" +schema_name = "" +serialization_type = "Avro" +schema_content = """ +{"namespace": "example.avro", + "type": "record", + "name": "User", + "fields": [ + {"name": "name", "type": "string"}, + {"name": "favorite_number", "type": ["int", "null"]}, + {"name": "favorite_color", "type": ["string", "null"]} + ] +} +""" + +schema_registry_client = SchemaRegistryClient(endpoint=endpoint, credential=token_credential) +with schema_registry_client: + schema_properties = schema_registry_client.register_schema(schema_group, schema_name, serialization_type, schema_content) + schema_id = schema_properties.schema_id +``` + +### Get the schema by id + +```python +import os + +from azure.identity import DefaultAzureCredential +from azure.schemaregistry import SchemaRegistryClient + +token_credential = DefaultAzureCredential() +endpoint = os.environ['SCHEMA_REGISTRY_ENDPOINT'] +schema_id = '' + +schema_registry_client = SchemaRegistryClient(endpoint=endpoint, credential=token_credential) +with schema_registry_client: + schema = schema_registry_client.get_schema(schema_id) + schema_content = schema.schema_content +``` + +### Get the id of a schema + +```python +import os + +from azure.identity import DefaultAzureCredential +from azure.schemaregistry import SchemaRegistryClient + +token_credential = DefaultAzureCredential() +endpoint = os.environ['SCHEMA_REGISTRY_ENDPOINT'] +schema_group = "" +schema_name = "" +serialization_type = "Avro" +schema_content = """ +{"namespace": "example.avro", + "type": "record", + "name": "User", + "fields": [ + {"name": "name", "type": "string"}, + {"name": "favorite_number", "type": ["int", "null"]}, + {"name": "favorite_color", "type": ["string", "null"]} + ] +} +""" + +schema_registry_client = SchemaRegistryClient(endpoint=endpoint, credential=token_credential) +with schema_registry_client: + schema_properties = schema_registry_client.get_schema_id(schema_group, schema_name, serialization_type, schema_content) + schema_id = schema_properties.schema_id +``` + ## Troubleshooting +### General + +Schema Registry clients raise exceptions defined in [Azure Core][azure_core]. + +### Logging +This library uses the standard +[logging][python_logging] library for logging. +Basic information about HTTP sessions (URLs, headers, etc.) is logged at INFO +level. + +Detailed DEBUG level logging, including request/response bodies and unredacted +headers, can be enabled on a client with the `logging_enable` argument: +```python +import sys +import logging +from azure.schemaregistry import SchemaRegistryClient +from azure.identity import DefaultAzureCredential + +# Create a logger for the SDK +logger = logging.getLogger('azure.schemaregistry') +logger.setLevel(logging.DEBUG) + +# Configure a console output +handler = logging.StreamHandler(stream=sys.stdout) +logger.addHandler(handler) + +credential = DefaultAzureCredential() +# This client will log detailed information about its HTTP sessions, at DEBUG level +schema_registry_client = SchemaRegistryClient("you_end_point", credential, logging_enable=True) +``` + +Similarly, `logging_enable` can enable detailed logging for a single operation, +even when it isn't enabled for the client: +```py +schema_registry_client.get_schema(schema_id, logging_enable=True) +``` + ## Next steps +### More sample code + +Please take a look at the [samples][sr_samples] directory for detailed examples of how to use this library to register and retrieve schema to/from Schema Registry. + ## Contributing This project welcomes contributions and suggestions. Most contributions require you to agree to a @@ -28,3 +187,13 @@ For more information see the [Code of Conduct FAQ](https://opensource.microsoft. contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments. +[pip]: https://pypi.org/project/pip/ +[pypi]: https://pypi.org/project/azure-schemaregistry +[python]: https://www.python.org/downloads/ +[azure_core]: https://github.com/Azure/azure-sdk-for-python/blob/master/sdk/core/azure-core/README.md +[azure_sub]: https://azure.microsoft.com/free/ +[python_logging]: https://docs.python.org/3/library/logging.html +[sr_samples]: https://github.com/Azure/azure-sdk-for-python/tree/master/sdk/schemaregistry/azure-schemaregistry/samples +[api_reference]: https://azuresdkdocs.blob.core.windows.net/$web/python/azure-schemaregistry/latest/index.html +[source_code]: https://github.com/Azure/azure-sdk-for-python/tree/master/sdk/schemaregistry/azure-schemaregistry +[change_log]: https://github.com/Azure/azure-sdk-for-python/tree/master/sdk/schemaregistry/azure-schemaregistry/CHANGELOG.md \ No newline at end of file diff --git a/sdk/schemaregistry/azure-schemaregistry/azure/__init__.py b/sdk/schemaregistry/azure-schemaregistry/azure/__init__.py index e604558a8bed..6f1f89cde8df 100644 --- a/sdk/schemaregistry/azure-schemaregistry/azure/__init__.py +++ b/sdk/schemaregistry/azure-schemaregistry/azure/__init__.py @@ -1 +1,26 @@ -__path__ = __import__('pkgutil').extend_path(__path__, __name__) # type: ignore +# -------------------------------------------------------------------------- +# +# Copyright (c) Microsoft Corporation. All rights reserved. +# +# The MIT License (MIT) +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the ""Software""), to +# deal in the Software without restriction, including without limitation the +# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +# sell copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +# IN THE SOFTWARE. +# +# -------------------------------------------------------------------------- +__path__ = __import__('pkgutil').extend_path(__path__, __name__) # type: ignore diff --git a/sdk/schemaregistry/azure-schemaregistry/azure/schemaregistry/__init__.py b/sdk/schemaregistry/azure-schemaregistry/azure/schemaregistry/__init__.py index a933188e69d4..6c68f01b1092 100644 --- a/sdk/schemaregistry/azure-schemaregistry/azure/schemaregistry/__init__.py +++ b/sdk/schemaregistry/azure-schemaregistry/azure/schemaregistry/__init__.py @@ -1,8 +1,40 @@ -# ------------------------------------------------------------------------ +# -------------------------------------------------------------------------- +# # Copyright (c) Microsoft Corporation. All rights reserved. -# Licensed under the MIT License. See License.txt in the project root for -# license information. -# ------------------------------------------------------------------------- +# +# The MIT License (MIT) +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the ""Software""), to +# deal in the Software without restriction, including without limitation the +# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +# sell copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +# IN THE SOFTWARE. +# +# -------------------------------------------------------------------------- +__path__ = __import__("pkgutil").extend_path(__path__, __name__) # type: ignore from ._version import VERSION __version__ = VERSION + +from ._schema_registry_client import SchemaRegistryClient +from ._common._constants import SerializationType +from ._common._schema import Schema, SchemaProperties + +__all__ = [ + "SchemaRegistryClient", + "SerializationType", + "Schema", + "SchemaProperties" +] diff --git a/sdk/schemaregistry/azure-schemaregistry/azure/schemaregistry/_common/__init__.py b/sdk/schemaregistry/azure-schemaregistry/azure/schemaregistry/_common/__init__.py new file mode 100644 index 000000000000..c36aaed14908 --- /dev/null +++ b/sdk/schemaregistry/azure-schemaregistry/azure/schemaregistry/_common/__init__.py @@ -0,0 +1,25 @@ +# -------------------------------------------------------------------------- +# +# Copyright (c) Microsoft Corporation. All rights reserved. +# +# The MIT License (MIT) +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the ""Software""), to +# deal in the Software without restriction, including without limitation the +# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +# sell copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +# IN THE SOFTWARE. +# +# -------------------------------------------------------------------------- diff --git a/sdk/schemaregistry/azure-schemaregistry/azure/schemaregistry/_common/_constants.py b/sdk/schemaregistry/azure-schemaregistry/azure/schemaregistry/_common/_constants.py new file mode 100644 index 000000000000..a22d118f4b84 --- /dev/null +++ b/sdk/schemaregistry/azure-schemaregistry/azure/schemaregistry/_common/_constants.py @@ -0,0 +1,30 @@ +# -------------------------------------------------------------------------- +# +# Copyright (c) Microsoft Corporation. All rights reserved. +# +# The MIT License (MIT) +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the ""Software""), to +# deal in the Software without restriction, including without limitation the +# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +# sell copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +# IN THE SOFTWARE. +# +# -------------------------------------------------------------------------- +from enum import Enum + + +class SerializationType(str, Enum): + AVRO = "avro" diff --git a/sdk/schemaregistry/azure-schemaregistry/azure/schemaregistry/_common/_response_handlers.py b/sdk/schemaregistry/azure-schemaregistry/azure/schemaregistry/_common/_response_handlers.py new file mode 100644 index 000000000000..e1ce9f4424c6 --- /dev/null +++ b/sdk/schemaregistry/azure-schemaregistry/azure/schemaregistry/_common/_response_handlers.py @@ -0,0 +1,56 @@ +# -------------------------------------------------------------------------- +# +# Copyright (c) Microsoft Corporation. All rights reserved. +# +# The MIT License (MIT) +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the ""Software""), to +# deal in the Software without restriction, including without limitation the +# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +# sell copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +# IN THE SOFTWARE. +# +# -------------------------------------------------------------------------- +from typing import Any, Dict + +from azure.core.pipeline import PipelineResponse + +from .._generated.models import SchemaId as InternalSchemaId +from ._schema import SchemaProperties, Schema + + +def _parse_response_schema_id(pipeline_response, deserialized, response_headers): # pylint: disable=unused-argument + # type: (PipelineResponse, InternalSchemaId, Dict[str, Any]) -> SchemaProperties + """ + + :param pipeline_response: + :param deserialized: + :param response_headers: + :return: + """ + return SchemaProperties(schema_id=deserialized.id, **response_headers) + + +def _parse_response_schema(pipeline_response, deserialized, response_headers): # pylint: disable=unused-argument + # type: (PipelineResponse, str, Dict[str, Any]) -> Schema + """ + + :param pipeline_response: + :param deserialized: + :param response_headers: + :return: + """ + + return Schema(schema_content=deserialized, schema_properties=SchemaProperties(**response_headers)) diff --git a/sdk/schemaregistry/azure-schemaregistry/azure/schemaregistry/_common/_schema.py b/sdk/schemaregistry/azure-schemaregistry/azure/schemaregistry/_common/_schema.py new file mode 100644 index 000000000000..6864db3ae5e3 --- /dev/null +++ b/sdk/schemaregistry/azure-schemaregistry/azure/schemaregistry/_common/_schema.py @@ -0,0 +1,93 @@ +# -------------------------------------------------------------------------- +# +# Copyright (c) Microsoft Corporation. All rights reserved. +# +# The MIT License (MIT) +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the ""Software""), to +# deal in the Software without restriction, including without limitation the +# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +# sell copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +# IN THE SOFTWARE. +# +# -------------------------------------------------------------------------- +from typing import Any, Optional + + +class SchemaProperties(object): + """ + Meta properties of a schema. + + :ivar schema_id: References specific schema in registry namespace. + :type schema_id: str + :ivar location: URL location of schema, identified by schema group, schema name, and version. + :type location: str + :ivar location_by_id: URL location of schema, identified by schema ID. + :type location_by_id: str + :ivar serialization_type: Serialization type for the schema being stored. + :type serialization_type: str + :ivar version: Version of the returned schema. + :type version: int + + .. admonition:: Example: + + .. literalinclude:: ../samples/sync_samples/sample_code_schemaregistry.py + :start-after: [START print_schema_properties] + :end-before: [END print_schema_properties] + :language: python + :dedent: 4 + :caption: SchemaId object. + + """ + def __init__( + self, + schema_id=None, + **kwargs + ): + # type: (Optional[str], Any) -> None + self.location = kwargs.get('Location') + self.schema_id = schema_id or kwargs.get("X-Schema-Id") + self.location_by_id = kwargs.get('X-Schema-Id-Location') + self.serialization_type = kwargs.get('X-Schema-Type') + self.version = kwargs.get('X-Schema-Version') + + +class Schema(object): + """ + The schema content of a schema, along with id and meta properties. + + :ivar schema_content: The content of the schema. + :type schema_content: str + :ivar schema_properties: The properties of the schema. + :type schema_properties: SchemaProperties + + .. admonition:: Example: + + .. literalinclude:: ../samples/sync_samples/sample_code_schemaregistry.py + :start-after: [START print_schema] + :end-before: [END print_schema] + :language: python + :dedent: 4 + :caption: Schema object. + + """ + def __init__( + self, + schema_content, + schema_properties, + ): + # type: (str, SchemaProperties) -> None + self.schema_content = schema_content + self.schema_properties = schema_properties diff --git a/sdk/schemaregistry/azure-schemaregistry/azure/schemaregistry/_generated/__init__.py b/sdk/schemaregistry/azure-schemaregistry/azure/schemaregistry/_generated/__init__.py index 85fc8393f162..f8bdf57c1911 100644 --- a/sdk/schemaregistry/azure-schemaregistry/azure/schemaregistry/_generated/__init__.py +++ b/sdk/schemaregistry/azure-schemaregistry/azure/schemaregistry/_generated/__init__.py @@ -13,7 +13,7 @@ __all__ = ['AzureSchemaRegistry'] try: - from ._patch import patch_sdk + from ._patch import patch_sdk # type: ignore patch_sdk() except ImportError: pass diff --git a/sdk/schemaregistry/azure-schemaregistry/azure/schemaregistry/_generated/_azure_schema_registry.py b/sdk/schemaregistry/azure-schemaregistry/azure/schemaregistry/_generated/_azure_schema_registry.py index 7c4e41e03505..c4f180986a47 100644 --- a/sdk/schemaregistry/azure-schemaregistry/azure/schemaregistry/_generated/_azure_schema_registry.py +++ b/sdk/schemaregistry/azure-schemaregistry/azure/schemaregistry/_generated/_azure_schema_registry.py @@ -15,29 +15,33 @@ # pylint: disable=unused-import,ungrouped-imports from typing import Any + from azure.core.credentials import TokenCredential + from ._configuration import AzureSchemaRegistryConfiguration from .operations import SchemaOperations from . import models class AzureSchemaRegistry(object): - """AzureSchemaRegistry. + """Azure Schema Registry is as a central schema repository for enterprise-level data infrastructure, complete with support for versioning and management. :ivar schema: SchemaOperations operations :vartype schema: azure.schemaregistry._generated.operations.SchemaOperations + :param credential: Credential needed for the client to connect to Azure. + :type credential: ~azure.core.credentials.TokenCredential :param endpoint: The Schema Registry service endpoint, for example my-namespace.servicebus.windows.net. :type endpoint: str - :keyword int polling_interval: Default waiting time between two polls for LRO operations if no Retry-After header is present. """ def __init__( self, + credential, # type: "TokenCredential" endpoint, # type: str **kwargs # type: Any ): # type: (...) -> None - base_url = 'https://{endpoint}/$schemagroups' - self._config = AzureSchemaRegistryConfiguration(endpoint, **kwargs) + base_url = 'https://{endpoint}' + self._config = AzureSchemaRegistryConfiguration(credential, endpoint, **kwargs) self._client = PipelineClient(base_url=base_url, config=self._config, **kwargs) client_models = {k: v for k, v in models.__dict__.items() if isinstance(v, type)} diff --git a/sdk/schemaregistry/azure-schemaregistry/azure/schemaregistry/_generated/_configuration.py b/sdk/schemaregistry/azure-schemaregistry/azure/schemaregistry/_generated/_configuration.py index 7b9ea58dc046..02e2eed29c10 100644 --- a/sdk/schemaregistry/azure-schemaregistry/azure/schemaregistry/_generated/_configuration.py +++ b/sdk/schemaregistry/azure-schemaregistry/azure/schemaregistry/_generated/_configuration.py @@ -17,6 +17,8 @@ # pylint: disable=unused-import,ungrouped-imports from typing import Any + from azure.core.credentials import TokenCredential + class AzureSchemaRegistryConfiguration(Configuration): """Configuration for AzureSchemaRegistry. @@ -24,21 +26,29 @@ class AzureSchemaRegistryConfiguration(Configuration): Note that all parameters used to create this instance are saved as instance attributes. + :param credential: Credential needed for the client to connect to Azure. + :type credential: ~azure.core.credentials.TokenCredential :param endpoint: The Schema Registry service endpoint, for example my-namespace.servicebus.windows.net. :type endpoint: str """ def __init__( self, + credential, # type: "TokenCredential" endpoint, # type: str **kwargs # type: Any ): # type: (...) -> None + if credential is None: + raise ValueError("Parameter 'credential' must not be None.") if endpoint is None: raise ValueError("Parameter 'endpoint' must not be None.") super(AzureSchemaRegistryConfiguration, self).__init__(**kwargs) + self.credential = credential self.endpoint = endpoint + self.api_version = "2017-04" + self.credential_scopes = kwargs.pop('credential_scopes', ['https://eventhubs.azure.net/.default']) kwargs.setdefault('sdk_moniker', 'azureschemaregistry/{}'.format(VERSION)) self._configure(**kwargs) @@ -51,7 +61,10 @@ def _configure( self.headers_policy = kwargs.get('headers_policy') or policies.HeadersPolicy(**kwargs) self.proxy_policy = kwargs.get('proxy_policy') or policies.ProxyPolicy(**kwargs) self.logging_policy = kwargs.get('logging_policy') or policies.NetworkTraceLoggingPolicy(**kwargs) + self.http_logging_policy = kwargs.get('http_logging_policy') or policies.HttpLoggingPolicy(**kwargs) self.retry_policy = kwargs.get('retry_policy') or policies.RetryPolicy(**kwargs) self.custom_hook_policy = kwargs.get('custom_hook_policy') or policies.CustomHookPolicy(**kwargs) self.redirect_policy = kwargs.get('redirect_policy') or policies.RedirectPolicy(**kwargs) self.authentication_policy = kwargs.get('authentication_policy') + if self.credential and not self.authentication_policy: + self.authentication_policy = policies.BearerTokenCredentialPolicy(self.credential, *self.credential_scopes, **kwargs) diff --git a/sdk/schemaregistry/azure-schemaregistry/azure/schemaregistry/_generated/aio/__init__.py b/sdk/schemaregistry/azure-schemaregistry/azure/schemaregistry/_generated/aio/__init__.py index 6319c29918f1..ae36d95dbeb0 100644 --- a/sdk/schemaregistry/azure-schemaregistry/azure/schemaregistry/_generated/aio/__init__.py +++ b/sdk/schemaregistry/azure-schemaregistry/azure/schemaregistry/_generated/aio/__init__.py @@ -6,5 +6,5 @@ # Changes may cause incorrect behavior and will be lost if the code is regenerated. # -------------------------------------------------------------------------- -from ._azure_schema_registry_async import AzureSchemaRegistry +from ._azure_schema_registry import AzureSchemaRegistry __all__ = ['AzureSchemaRegistry'] diff --git a/sdk/schemaregistry/azure-schemaregistry/azure/schemaregistry/_generated/aio/_azure_schema_registry_async.py b/sdk/schemaregistry/azure-schemaregistry/azure/schemaregistry/_generated/aio/_azure_schema_registry.py similarity index 68% rename from sdk/schemaregistry/azure-schemaregistry/azure/schemaregistry/_generated/aio/_azure_schema_registry_async.py rename to sdk/schemaregistry/azure-schemaregistry/azure/schemaregistry/_generated/aio/_azure_schema_registry.py index 97def68d031b..dd14b4dac46a 100644 --- a/sdk/schemaregistry/azure-schemaregistry/azure/schemaregistry/_generated/aio/_azure_schema_registry_async.py +++ b/sdk/schemaregistry/azure-schemaregistry/azure/schemaregistry/_generated/aio/_azure_schema_registry.py @@ -6,33 +6,39 @@ # Changes may cause incorrect behavior and will be lost if the code is regenerated. # -------------------------------------------------------------------------- -from typing import Any +from typing import Any, TYPE_CHECKING from azure.core import AsyncPipelineClient from msrest import Deserializer, Serializer -from ._configuration_async import AzureSchemaRegistryConfiguration -from .operations_async import SchemaOperations +if TYPE_CHECKING: + # pylint: disable=unused-import,ungrouped-imports + from azure.core.credentials_async import AsyncTokenCredential + +from ._configuration import AzureSchemaRegistryConfiguration +from .operations import SchemaOperations from .. import models class AzureSchemaRegistry(object): - """AzureSchemaRegistry. + """Azure Schema Registry is as a central schema repository for enterprise-level data infrastructure, complete with support for versioning and management. :ivar schema: SchemaOperations operations - :vartype schema: azure.schemaregistry._generated.aio.operations_async.SchemaOperations + :vartype schema: azure.schemaregistry._generated.aio.operations.SchemaOperations + :param credential: Credential needed for the client to connect to Azure. + :type credential: ~azure.core.credentials_async.AsyncTokenCredential :param endpoint: The Schema Registry service endpoint, for example my-namespace.servicebus.windows.net. :type endpoint: str - :keyword int polling_interval: Default waiting time between two polls for LRO operations if no Retry-After header is present. """ def __init__( self, + credential: "AsyncTokenCredential", endpoint: str, **kwargs: Any ) -> None: - base_url = 'https://{endpoint}/$schemagroups' - self._config = AzureSchemaRegistryConfiguration(endpoint, **kwargs) + base_url = 'https://{endpoint}' + self._config = AzureSchemaRegistryConfiguration(credential, endpoint, **kwargs) self._client = AsyncPipelineClient(base_url=base_url, config=self._config, **kwargs) client_models = {k: v for k, v in models.__dict__.items() if isinstance(v, type)} diff --git a/sdk/schemaregistry/azure-schemaregistry/azure/schemaregistry/_generated/aio/_configuration_async.py b/sdk/schemaregistry/azure-schemaregistry/azure/schemaregistry/_generated/aio/_configuration.py similarity index 69% rename from sdk/schemaregistry/azure-schemaregistry/azure/schemaregistry/_generated/aio/_configuration_async.py rename to sdk/schemaregistry/azure-schemaregistry/azure/schemaregistry/_generated/aio/_configuration.py index c4d00a2ef815..146ad563107c 100644 --- a/sdk/schemaregistry/azure-schemaregistry/azure/schemaregistry/_generated/aio/_configuration_async.py +++ b/sdk/schemaregistry/azure-schemaregistry/azure/schemaregistry/_generated/aio/_configuration.py @@ -6,13 +6,17 @@ # Changes may cause incorrect behavior and will be lost if the code is regenerated. # -------------------------------------------------------------------------- -from typing import Any +from typing import Any, TYPE_CHECKING from azure.core.configuration import Configuration from azure.core.pipeline import policies from .._version import VERSION +if TYPE_CHECKING: + # pylint: disable=unused-import,ungrouped-imports + from azure.core.credentials_async import AsyncTokenCredential + class AzureSchemaRegistryConfiguration(Configuration): """Configuration for AzureSchemaRegistry. @@ -20,20 +24,28 @@ class AzureSchemaRegistryConfiguration(Configuration): Note that all parameters used to create this instance are saved as instance attributes. + :param credential: Credential needed for the client to connect to Azure. + :type credential: ~azure.core.credentials_async.AsyncTokenCredential :param endpoint: The Schema Registry service endpoint, for example my-namespace.servicebus.windows.net. :type endpoint: str """ def __init__( self, + credential: "AsyncTokenCredential", endpoint: str, **kwargs: Any ) -> None: + if credential is None: + raise ValueError("Parameter 'credential' must not be None.") if endpoint is None: raise ValueError("Parameter 'endpoint' must not be None.") super(AzureSchemaRegistryConfiguration, self).__init__(**kwargs) + self.credential = credential self.endpoint = endpoint + self.api_version = "2017-04" + self.credential_scopes = kwargs.pop('credential_scopes', ['https://eventhubs.azure.net/.default']) kwargs.setdefault('sdk_moniker', 'azureschemaregistry/{}'.format(VERSION)) self._configure(**kwargs) @@ -45,7 +57,10 @@ def _configure( self.headers_policy = kwargs.get('headers_policy') or policies.HeadersPolicy(**kwargs) self.proxy_policy = kwargs.get('proxy_policy') or policies.ProxyPolicy(**kwargs) self.logging_policy = kwargs.get('logging_policy') or policies.NetworkTraceLoggingPolicy(**kwargs) + self.http_logging_policy = kwargs.get('http_logging_policy') or policies.HttpLoggingPolicy(**kwargs) self.retry_policy = kwargs.get('retry_policy') or policies.AsyncRetryPolicy(**kwargs) self.custom_hook_policy = kwargs.get('custom_hook_policy') or policies.CustomHookPolicy(**kwargs) self.redirect_policy = kwargs.get('redirect_policy') or policies.AsyncRedirectPolicy(**kwargs) self.authentication_policy = kwargs.get('authentication_policy') + if self.credential and not self.authentication_policy: + self.authentication_policy = policies.AsyncBearerTokenCredentialPolicy(self.credential, *self.credential_scopes, **kwargs) diff --git a/sdk/schemaregistry/azure-schemaregistry/azure/schemaregistry/_generated/aio/operations_async/__init__.py b/sdk/schemaregistry/azure-schemaregistry/azure/schemaregistry/_generated/aio/operations/__init__.py similarity index 90% rename from sdk/schemaregistry/azure-schemaregistry/azure/schemaregistry/_generated/aio/operations_async/__init__.py rename to sdk/schemaregistry/azure-schemaregistry/azure/schemaregistry/_generated/aio/operations/__init__.py index 29642724a481..61b30e467156 100644 --- a/sdk/schemaregistry/azure-schemaregistry/azure/schemaregistry/_generated/aio/operations_async/__init__.py +++ b/sdk/schemaregistry/azure-schemaregistry/azure/schemaregistry/_generated/aio/operations/__init__.py @@ -6,7 +6,7 @@ # Changes may cause incorrect behavior and will be lost if the code is regenerated. # -------------------------------------------------------------------------- -from ._schema_operations_async import SchemaOperations +from ._schema_operations import SchemaOperations __all__ = [ 'SchemaOperations', diff --git a/sdk/schemaregistry/azure-schemaregistry/azure/schemaregistry/_generated/aio/operations_async/_schema_operations_async.py b/sdk/schemaregistry/azure-schemaregistry/azure/schemaregistry/_generated/aio/operations/_schema_operations.py similarity index 78% rename from sdk/schemaregistry/azure-schemaregistry/azure/schemaregistry/_generated/aio/operations_async/_schema_operations_async.py rename to sdk/schemaregistry/azure-schemaregistry/azure/schemaregistry/_generated/aio/operations/_schema_operations.py index e77a32253fcf..9dd2aedd6d6d 100644 --- a/sdk/schemaregistry/azure-schemaregistry/azure/schemaregistry/_generated/aio/operations_async/_schema_operations_async.py +++ b/sdk/schemaregistry/azure-schemaregistry/azure/schemaregistry/_generated/aio/operations/_schema_operations.py @@ -5,7 +5,7 @@ # Code generated by Microsoft (R) AutoRest Code Generator. # Changes may cause incorrect behavior and will be lost if the code is regenerated. # -------------------------------------------------------------------------- -from typing import Any, Callable, Dict, Generic, Optional, TypeVar +from typing import Any, Callable, Dict, Generic, Optional, TypeVar, Union import warnings from azure.core.exceptions import HttpResponseError, ResourceExistsError, ResourceNotFoundError, map_error @@ -44,9 +44,10 @@ async def get_by_id( schema_id: str, **kwargs ) -> str: - """Gets a registered schema by its unique ID. Azure Schema Registry guarantees that ID is unique within a namespace. + """Get a registered schema by its unique ID reference. - Get a registered schema by its unique ID reference. + Gets a registered schema by its unique ID. Azure Schema Registry guarantees that ID is unique + within a namespace. :param schema_id: References specific schema in registry namespace. :type schema_id: str @@ -58,6 +59,8 @@ async def get_by_id( cls = kwargs.pop('cls', None) # type: ClsType[str] error_map = {404: ResourceNotFoundError, 409: ResourceExistsError} error_map.update(kwargs.pop('error_map', {})) + api_version = "2017-04" + accept = "application/json" # Construct URL url = self.get_by_id.metadata['url'] # type: ignore @@ -69,12 +72,12 @@ async def get_by_id( # Construct parameters query_parameters = {} # type: Dict[str, Any] + query_parameters['api-version'] = self._serialize.query("api_version", api_version, 'str') # Construct headers header_parameters = {} # type: Dict[str, Any] - header_parameters['Accept'] = 'application/json' + header_parameters['Accept'] = self._serialize.header("accept", accept, 'str') - # Construct and send request request = self._client.get(url, query_parameters, header_parameters) pipeline_response = await self._client._pipeline.run(request, stream=False, **kwargs) response = pipeline_response.http_response @@ -86,7 +89,7 @@ async def get_by_id( response_headers = {} response_headers['Location']=self._deserialize('str', response.headers.get('Location')) - response_headers['X-Serialization']=self._deserialize('str', response.headers.get('X-Serialization')) + response_headers['X-Schema-Type']=self._deserialize('str', response.headers.get('X-Schema-Type')) response_headers['X-Schema-Id']=self._deserialize('str', response.headers.get('X-Schema-Id')) response_headers['X-Schema-Id-Location']=self._deserialize('str', response.headers.get('X-Schema-Id-Location')) response_headers['X-Schema-Version']=self._deserialize('int', response.headers.get('X-Schema-Version')) @@ -96,28 +99,28 @@ async def get_by_id( return cls(pipeline_response, deserialized, response_headers) return deserialized - get_by_id.metadata = {'url': '/getSchemaById/{schema-id}'} # type: ignore + get_by_id.metadata = {'url': '/$schemagroups/getSchemaById/{schema-id}'} # type: ignore - async def get_id_by_content( + async def query_id_by_content( self, group_name: str, schema_name: str, - serialization_type: str, + x_schema_type: Union[str, "models.SerializationType"], schema_content: str, **kwargs ) -> "models.SchemaId": - """Gets the ID referencing an existing schema within the specified schema group, as matched by schema content comparison. + """Get ID for existing schema. - Get ID for existing schema. + Gets the ID referencing an existing schema within the specified schema group, as matched by + schema content comparison. :param group_name: Schema group under which schema is registered. Group's serialization type should match the serialization type specified in the request. :type group_name: str :param schema_name: Name of the registered schema. :type schema_name: str - :param serialization_type: Serialization type of the registered schema. Must match - serialization type of the specified schema group. - :type serialization_type: str + :param x_schema_type: Serialization type for the schema being registered. + :type x_schema_type: str or ~azure.schemaregistry._generated.models.SerializationType :param schema_content: String representation of the registered schema. :type schema_content: str :keyword callable cls: A custom type or function that will be passed the direct response @@ -128,10 +131,12 @@ async def get_id_by_content( cls = kwargs.pop('cls', None) # type: ClsType["models.SchemaId"] error_map = {404: ResourceNotFoundError, 409: ResourceExistsError} error_map.update(kwargs.pop('error_map', {})) + api_version = "2017-04" content_type = kwargs.pop("content_type", "application/json") + accept = "application/json" # Construct URL - url = self.get_id_by_content.metadata['url'] # type: ignore + url = self.query_id_by_content.metadata['url'] # type: ignore path_format_arguments = { 'endpoint': self._serialize.url("self._config.endpoint", self._config.endpoint, 'str', skip_quote=True), 'group-name': self._serialize.url("group_name", group_name, 'str'), @@ -141,19 +146,18 @@ async def get_id_by_content( # Construct parameters query_parameters = {} # type: Dict[str, Any] + query_parameters['api-version'] = self._serialize.query("api_version", api_version, 'str') # Construct headers header_parameters = {} # type: Dict[str, Any] - header_parameters['serialization-type'] = self._serialize.header("serialization_type", serialization_type, 'str') + header_parameters['X-Schema-Type'] = self._serialize.header("x_schema_type", x_schema_type, 'str') header_parameters['Content-Type'] = self._serialize.header("content_type", content_type, 'str') - header_parameters['Accept'] = 'application/json' + header_parameters['Accept'] = self._serialize.header("accept", accept, 'str') - # Construct and send request body_content_kwargs = {} # type: Dict[str, Any] body_content = self._serialize.body(schema_content, 'str') body_content_kwargs['content'] = body_content request = self._client.post(url, query_parameters, header_parameters, **body_content_kwargs) - pipeline_response = await self._client._pipeline.run(request, stream=False, **kwargs) response = pipeline_response.http_response @@ -164,7 +168,7 @@ async def get_id_by_content( response_headers = {} response_headers['Location']=self._deserialize('str', response.headers.get('Location')) - response_headers['X-Serialization']=self._deserialize('str', response.headers.get('X-Serialization')) + response_headers['X-Schema-Type']=self._deserialize('str', response.headers.get('X-Schema-Type')) response_headers['X-Schema-Id']=self._deserialize('str', response.headers.get('X-Schema-Id')) response_headers['X-Schema-Id-Location']=self._deserialize('str', response.headers.get('X-Schema-Id-Location')) response_headers['X-Schema-Version']=self._deserialize('int', response.headers.get('X-Schema-Version')) @@ -174,28 +178,29 @@ async def get_id_by_content( return cls(pipeline_response, deserialized, response_headers) return deserialized - get_id_by_content.metadata = {'url': '/{group-name}/schemas/{schema-name}'} # type: ignore + query_id_by_content.metadata = {'url': '/$schemagroups/{group-name}/schemas/{schema-name}'} # type: ignore async def register( self, group_name: str, schema_name: str, - serialization_type: str, + x_schema_type: Union[str, "models.SerializationType"], schema_content: str, **kwargs ) -> "models.SchemaId": - """Register new schema. If schema of specified name does not exist in specified group, schema is created at version 1. If schema of specified name exists already in specified group, schema is created at latest version + 1. + """Register new schema. - Register new schema. + Register new schema. If schema of specified name does not exist in specified group, schema is + created at version 1. If schema of specified name exists already in specified group, schema is + created at latest version + 1. :param group_name: Schema group under which schema should be registered. Group's serialization type should match the serialization type specified in the request. :type group_name: str :param schema_name: Name of schema being registered. :type schema_name: str - :param serialization_type: Serialization type for the schema being registered. Must match - serialization type of the specified schema group. - :type serialization_type: str + :param x_schema_type: Serialization type for the schema being registered. + :type x_schema_type: str or ~azure.schemaregistry._generated.models.SerializationType :param schema_content: String representation of the schema being registered. :type schema_content: str :keyword callable cls: A custom type or function that will be passed the direct response @@ -206,7 +211,9 @@ async def register( cls = kwargs.pop('cls', None) # type: ClsType["models.SchemaId"] error_map = {404: ResourceNotFoundError, 409: ResourceExistsError} error_map.update(kwargs.pop('error_map', {})) + api_version = "2017-04" content_type = kwargs.pop("content_type", "application/json") + accept = "application/json" # Construct URL url = self.register.metadata['url'] # type: ignore @@ -219,19 +226,18 @@ async def register( # Construct parameters query_parameters = {} # type: Dict[str, Any] + query_parameters['api-version'] = self._serialize.query("api_version", api_version, 'str') # Construct headers header_parameters = {} # type: Dict[str, Any] - header_parameters['serialization-type'] = self._serialize.header("serialization_type", serialization_type, 'str') + header_parameters['X-Schema-Type'] = self._serialize.header("x_schema_type", x_schema_type, 'str') header_parameters['Content-Type'] = self._serialize.header("content_type", content_type, 'str') - header_parameters['Accept'] = 'application/json' + header_parameters['Accept'] = self._serialize.header("accept", accept, 'str') - # Construct and send request body_content_kwargs = {} # type: Dict[str, Any] body_content = self._serialize.body(schema_content, 'str') body_content_kwargs['content'] = body_content request = self._client.put(url, query_parameters, header_parameters, **body_content_kwargs) - pipeline_response = await self._client._pipeline.run(request, stream=False, **kwargs) response = pipeline_response.http_response @@ -242,7 +248,7 @@ async def register( response_headers = {} response_headers['Location']=self._deserialize('str', response.headers.get('Location')) - response_headers['X-Serialization']=self._deserialize('str', response.headers.get('X-Serialization')) + response_headers['X-Schema-Type']=self._deserialize('str', response.headers.get('X-Schema-Type')) response_headers['X-Schema-Id']=self._deserialize('str', response.headers.get('X-Schema-Id')) response_headers['X-Schema-Id-Location']=self._deserialize('str', response.headers.get('X-Schema-Id-Location')) response_headers['X-Schema-Version']=self._deserialize('int', response.headers.get('X-Schema-Version')) @@ -252,4 +258,4 @@ async def register( return cls(pipeline_response, deserialized, response_headers) return deserialized - register.metadata = {'url': '/{group-name}/schemas/{schema-name}'} # type: ignore + register.metadata = {'url': '/$schemagroups/{group-name}/schemas/{schema-name}'} # type: ignore diff --git a/sdk/schemaregistry/azure-schemaregistry/azure/schemaregistry/_generated/models/__init__.py b/sdk/schemaregistry/azure-schemaregistry/azure/schemaregistry/_generated/models/__init__.py index acabe98624c0..93cd4fc50d84 100644 --- a/sdk/schemaregistry/azure-schemaregistry/azure/schemaregistry/_generated/models/__init__.py +++ b/sdk/schemaregistry/azure-schemaregistry/azure/schemaregistry/_generated/models/__init__.py @@ -11,6 +11,11 @@ except (SyntaxError, ImportError): from ._models import SchemaId # type: ignore +from ._azure_schema_registry_enums import ( + SerializationType, +) + __all__ = [ 'SchemaId', + 'SerializationType', ] diff --git a/sdk/schemaregistry/azure-schemaregistry/azure/schemaregistry/_generated/models/_azure_schema_registry_enums.py b/sdk/schemaregistry/azure-schemaregistry/azure/schemaregistry/_generated/models/_azure_schema_registry_enums.py new file mode 100644 index 000000000000..a1ae7238a329 --- /dev/null +++ b/sdk/schemaregistry/azure-schemaregistry/azure/schemaregistry/_generated/models/_azure_schema_registry_enums.py @@ -0,0 +1,31 @@ +# 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 enum import Enum, EnumMeta +from six import with_metaclass + +class _CaseInsensitiveEnumMeta(EnumMeta): + def __getitem__(self, name): + return super().__getitem__(name.upper()) + + def __getattr__(cls, name): + """Return the enum member matching `name` + We use __getattr__ instead of descriptors or inserting into the enum + class' __dict__ in order to support `name` and `value` being both + properties for enum members (which live in the class' __dict__) and + enum members themselves. + """ + try: + return cls._member_map_[name.upper()] + except KeyError: + raise AttributeError(name) + + +class SerializationType(with_metaclass(_CaseInsensitiveEnumMeta, str, Enum)): + + AVRO = "avro" #: Avro Serialization schema type. diff --git a/sdk/schemaregistry/azure-schemaregistry/azure/schemaregistry/_generated/operations/_schema_operations.py b/sdk/schemaregistry/azure-schemaregistry/azure/schemaregistry/_generated/operations/_schema_operations.py index c2eb7573a328..8472c0b9f69d 100644 --- a/sdk/schemaregistry/azure-schemaregistry/azure/schemaregistry/_generated/operations/_schema_operations.py +++ b/sdk/schemaregistry/azure-schemaregistry/azure/schemaregistry/_generated/operations/_schema_operations.py @@ -16,7 +16,7 @@ if TYPE_CHECKING: # pylint: disable=unused-import,ungrouped-imports - from typing import Any, Callable, Dict, Generic, Optional, TypeVar + from typing import Any, Callable, Dict, Generic, Optional, TypeVar, Union T = TypeVar('T') ClsType = Optional[Callable[[PipelineResponse[HttpRequest, HttpResponse], T, Dict[str, Any]], Any]] @@ -49,9 +49,10 @@ def get_by_id( **kwargs # type: Any ): # type: (...) -> str - """Gets a registered schema by its unique ID. Azure Schema Registry guarantees that ID is unique within a namespace. + """Get a registered schema by its unique ID reference. - Get a registered schema by its unique ID reference. + Gets a registered schema by its unique ID. Azure Schema Registry guarantees that ID is unique + within a namespace. :param schema_id: References specific schema in registry namespace. :type schema_id: str @@ -63,6 +64,8 @@ def get_by_id( cls = kwargs.pop('cls', None) # type: ClsType[str] error_map = {404: ResourceNotFoundError, 409: ResourceExistsError} error_map.update(kwargs.pop('error_map', {})) + api_version = "2017-04" + accept = "application/json" # Construct URL url = self.get_by_id.metadata['url'] # type: ignore @@ -74,12 +77,12 @@ def get_by_id( # Construct parameters query_parameters = {} # type: Dict[str, Any] + query_parameters['api-version'] = self._serialize.query("api_version", api_version, 'str') # Construct headers header_parameters = {} # type: Dict[str, Any] - header_parameters['Accept'] = 'application/json' + header_parameters['Accept'] = self._serialize.header("accept", accept, 'str') - # Construct and send request request = self._client.get(url, query_parameters, header_parameters) pipeline_response = self._client._pipeline.run(request, stream=False, **kwargs) response = pipeline_response.http_response @@ -91,7 +94,7 @@ def get_by_id( response_headers = {} response_headers['Location']=self._deserialize('str', response.headers.get('Location')) - response_headers['X-Serialization']=self._deserialize('str', response.headers.get('X-Serialization')) + response_headers['X-Schema-Type']=self._deserialize('str', response.headers.get('X-Schema-Type')) response_headers['X-Schema-Id']=self._deserialize('str', response.headers.get('X-Schema-Id')) response_headers['X-Schema-Id-Location']=self._deserialize('str', response.headers.get('X-Schema-Id-Location')) response_headers['X-Schema-Version']=self._deserialize('int', response.headers.get('X-Schema-Version')) @@ -101,29 +104,29 @@ def get_by_id( return cls(pipeline_response, deserialized, response_headers) return deserialized - get_by_id.metadata = {'url': '/getSchemaById/{schema-id}'} # type: ignore + get_by_id.metadata = {'url': '/$schemagroups/getSchemaById/{schema-id}'} # type: ignore - def get_id_by_content( + def query_id_by_content( self, group_name, # type: str schema_name, # type: str - serialization_type, # type: str + x_schema_type, # type: Union[str, "models.SerializationType"] schema_content, # type: str **kwargs # type: Any ): # type: (...) -> "models.SchemaId" - """Gets the ID referencing an existing schema within the specified schema group, as matched by schema content comparison. + """Get ID for existing schema. - Get ID for existing schema. + Gets the ID referencing an existing schema within the specified schema group, as matched by + schema content comparison. :param group_name: Schema group under which schema is registered. Group's serialization type should match the serialization type specified in the request. :type group_name: str :param schema_name: Name of the registered schema. :type schema_name: str - :param serialization_type: Serialization type of the registered schema. Must match - serialization type of the specified schema group. - :type serialization_type: str + :param x_schema_type: Serialization type for the schema being registered. + :type x_schema_type: str or ~azure.schemaregistry._generated.models.SerializationType :param schema_content: String representation of the registered schema. :type schema_content: str :keyword callable cls: A custom type or function that will be passed the direct response @@ -134,10 +137,12 @@ def get_id_by_content( cls = kwargs.pop('cls', None) # type: ClsType["models.SchemaId"] error_map = {404: ResourceNotFoundError, 409: ResourceExistsError} error_map.update(kwargs.pop('error_map', {})) + api_version = "2017-04" content_type = kwargs.pop("content_type", "application/json") + accept = "application/json" # Construct URL - url = self.get_id_by_content.metadata['url'] # type: ignore + url = self.query_id_by_content.metadata['url'] # type: ignore path_format_arguments = { 'endpoint': self._serialize.url("self._config.endpoint", self._config.endpoint, 'str', skip_quote=True), 'group-name': self._serialize.url("group_name", group_name, 'str'), @@ -147,19 +152,18 @@ def get_id_by_content( # Construct parameters query_parameters = {} # type: Dict[str, Any] + query_parameters['api-version'] = self._serialize.query("api_version", api_version, 'str') # Construct headers header_parameters = {} # type: Dict[str, Any] - header_parameters['serialization-type'] = self._serialize.header("serialization_type", serialization_type, 'str') + header_parameters['X-Schema-Type'] = self._serialize.header("x_schema_type", x_schema_type, 'str') header_parameters['Content-Type'] = self._serialize.header("content_type", content_type, 'str') - header_parameters['Accept'] = 'application/json' + header_parameters['Accept'] = self._serialize.header("accept", accept, 'str') - # Construct and send request body_content_kwargs = {} # type: Dict[str, Any] body_content = self._serialize.body(schema_content, 'str') body_content_kwargs['content'] = body_content request = self._client.post(url, query_parameters, header_parameters, **body_content_kwargs) - pipeline_response = self._client._pipeline.run(request, stream=False, **kwargs) response = pipeline_response.http_response @@ -170,7 +174,7 @@ def get_id_by_content( response_headers = {} response_headers['Location']=self._deserialize('str', response.headers.get('Location')) - response_headers['X-Serialization']=self._deserialize('str', response.headers.get('X-Serialization')) + response_headers['X-Schema-Type']=self._deserialize('str', response.headers.get('X-Schema-Type')) response_headers['X-Schema-Id']=self._deserialize('str', response.headers.get('X-Schema-Id')) response_headers['X-Schema-Id-Location']=self._deserialize('str', response.headers.get('X-Schema-Id-Location')) response_headers['X-Schema-Version']=self._deserialize('int', response.headers.get('X-Schema-Version')) @@ -180,29 +184,30 @@ def get_id_by_content( return cls(pipeline_response, deserialized, response_headers) return deserialized - get_id_by_content.metadata = {'url': '/{group-name}/schemas/{schema-name}'} # type: ignore + query_id_by_content.metadata = {'url': '/$schemagroups/{group-name}/schemas/{schema-name}'} # type: ignore def register( self, group_name, # type: str schema_name, # type: str - serialization_type, # type: str + x_schema_type, # type: Union[str, "models.SerializationType"] schema_content, # type: str **kwargs # type: Any ): # type: (...) -> "models.SchemaId" - """Register new schema. If schema of specified name does not exist in specified group, schema is created at version 1. If schema of specified name exists already in specified group, schema is created at latest version + 1. + """Register new schema. - Register new schema. + Register new schema. If schema of specified name does not exist in specified group, schema is + created at version 1. If schema of specified name exists already in specified group, schema is + created at latest version + 1. :param group_name: Schema group under which schema should be registered. Group's serialization type should match the serialization type specified in the request. :type group_name: str :param schema_name: Name of schema being registered. :type schema_name: str - :param serialization_type: Serialization type for the schema being registered. Must match - serialization type of the specified schema group. - :type serialization_type: str + :param x_schema_type: Serialization type for the schema being registered. + :type x_schema_type: str or ~azure.schemaregistry._generated.models.SerializationType :param schema_content: String representation of the schema being registered. :type schema_content: str :keyword callable cls: A custom type or function that will be passed the direct response @@ -213,7 +218,9 @@ def register( cls = kwargs.pop('cls', None) # type: ClsType["models.SchemaId"] error_map = {404: ResourceNotFoundError, 409: ResourceExistsError} error_map.update(kwargs.pop('error_map', {})) + api_version = "2017-04" content_type = kwargs.pop("content_type", "application/json") + accept = "application/json" # Construct URL url = self.register.metadata['url'] # type: ignore @@ -226,19 +233,18 @@ def register( # Construct parameters query_parameters = {} # type: Dict[str, Any] + query_parameters['api-version'] = self._serialize.query("api_version", api_version, 'str') # Construct headers header_parameters = {} # type: Dict[str, Any] - header_parameters['serialization-type'] = self._serialize.header("serialization_type", serialization_type, 'str') + header_parameters['X-Schema-Type'] = self._serialize.header("x_schema_type", x_schema_type, 'str') header_parameters['Content-Type'] = self._serialize.header("content_type", content_type, 'str') - header_parameters['Accept'] = 'application/json' + header_parameters['Accept'] = self._serialize.header("accept", accept, 'str') - # Construct and send request body_content_kwargs = {} # type: Dict[str, Any] body_content = self._serialize.body(schema_content, 'str') body_content_kwargs['content'] = body_content request = self._client.put(url, query_parameters, header_parameters, **body_content_kwargs) - pipeline_response = self._client._pipeline.run(request, stream=False, **kwargs) response = pipeline_response.http_response @@ -249,7 +255,7 @@ def register( response_headers = {} response_headers['Location']=self._deserialize('str', response.headers.get('Location')) - response_headers['X-Serialization']=self._deserialize('str', response.headers.get('X-Serialization')) + response_headers['X-Schema-Type']=self._deserialize('str', response.headers.get('X-Schema-Type')) response_headers['X-Schema-Id']=self._deserialize('str', response.headers.get('X-Schema-Id')) response_headers['X-Schema-Id-Location']=self._deserialize('str', response.headers.get('X-Schema-Id-Location')) response_headers['X-Schema-Version']=self._deserialize('int', response.headers.get('X-Schema-Version')) @@ -259,4 +265,4 @@ def register( return cls(pipeline_response, deserialized, response_headers) return deserialized - register.metadata = {'url': '/{group-name}/schemas/{schema-name}'} # type: ignore + register.metadata = {'url': '/$schemagroups/{group-name}/schemas/{schema-name}'} # type: ignore diff --git a/sdk/schemaregistry/azure-schemaregistry/azure/schemaregistry/_schema_registry_client.py b/sdk/schemaregistry/azure-schemaregistry/azure/schemaregistry/_schema_registry_client.py new file mode 100644 index 000000000000..524e5b81e636 --- /dev/null +++ b/sdk/schemaregistry/azure-schemaregistry/azure/schemaregistry/_schema_registry_client.py @@ -0,0 +1,181 @@ +# -------------------------------------------------------------------------- +# +# Copyright (c) Microsoft Corporation. All rights reserved. +# +# The MIT License (MIT) +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the ""Software""), to +# deal in the Software without restriction, including without limitation the +# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +# sell copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +# IN THE SOFTWARE. +# +# -------------------------------------------------------------------------- +from typing import Any, TYPE_CHECKING, Union + +from ._common._constants import SerializationType +from ._common._schema import Schema, SchemaProperties +from ._common._response_handlers import _parse_response_schema, _parse_response_schema_id +from ._generated._azure_schema_registry import AzureSchemaRegistry + +if TYPE_CHECKING: + from azure.core.credentials import TokenCredential + + +class SchemaRegistryClient(object): + """ + SchemaRegistryClient is as a central schema repository for enterprise-level data infrastructure, + complete with support for versioning and management. + + :param str endpoint: The Schema Registry service endpoint, for example my-namespace.servicebus.windows.net. + :param credential: To authenticate to manage the entities of the SchemaRegistry namespace. + :type credential: TokenCredential + + .. admonition:: Example: + + .. literalinclude:: ../samples/sync_samples/sample_code_schemaregistry.py + :start-after: [START create_sr_client_sync] + :end-before: [END create_sr_client_sync] + :language: python + :dedent: 4 + :caption: Create a new instance of the SchemaRegistryClient. + + """ + def __init__( + self, + endpoint, + credential, + **kwargs + ): + # type: (str, TokenCredential, Any) -> None + self._generated_client = AzureSchemaRegistry(credential=credential, endpoint=endpoint, **kwargs) + + def __enter__(self): + # type: () -> SchemaRegistryClient + self._generated_client.__enter__() + return self + + def __exit__(self, *exc_details): + # type: (Any) -> None + self._generated_client.__exit__(*exc_details) + + def close(self): + # type: () -> None + """ This method is to close the sockets opened by the client. + It need not be used when using with a context manager. + """ + self._generated_client.close() + + def register_schema(self, schema_group, schema_name, serialization_type, schema_content, **kwargs): + # type: (str, str, Union[str, SerializationType], str, Any) -> SchemaProperties + """ + Register new schema. If schema of specified name does not exist in specified group, + schema is created at version 1. If schema of specified name exists already in specified group, + schema is created at latest version + 1. + + :param str schema_group: Schema group under which schema should be registered. + :param str schema_name: Name of schema being registered. + :param serialization_type: Serialization type for the schema being registered. + For now Avro is the only supported serialization type by the service. + :type serialization_type: Union[str, SerializationType] + :param str schema_content: String representation of the schema being registered. + :rtype: SchemaProperties + + .. admonition:: Example: + + .. literalinclude:: ../samples/sync_samples/sample_code_schemaregistry.py + :start-after: [START register_schema_sync] + :end-before: [END register_schema_sync] + :language: python + :dedent: 4 + :caption: Register a new schema. + + """ + try: + serialization_type = serialization_type.value + except AttributeError: + pass + + return self._generated_client.schema.register( + group_name=schema_group, + schema_name=schema_name, + schema_content=schema_content, + x_schema_type=serialization_type, + cls=_parse_response_schema_id, + **kwargs + ) + + def get_schema(self, schema_id, **kwargs): + # type: (str, Any) -> Schema + """ + Gets a registered schema by its unique ID. + Azure Schema Registry guarantees that ID is unique within a namespace. + + :param str schema_id: References specific schema in registry namespace. + :rtype: Schema + + .. admonition:: Example: + + .. literalinclude:: ../samples/sync_samples/sample_code_schemaregistry.py + :start-after: [START get_schema_sync] + :end-before: [END get_schema_sync] + :language: python + :dedent: 4 + :caption: Get schema by id. + + """ + return self._generated_client.schema.get_by_id( + schema_id, + cls=_parse_response_schema, + **kwargs + ) + + def get_schema_id(self, schema_group, schema_name, serialization_type, schema_content, **kwargs): + # type: (str, str, Union[str, SerializationType], str, Any) -> SchemaProperties + """ + Gets the ID referencing an existing schema within the specified schema group, + as matched by schema content comparison. + + :param str schema_group: Schema group under which schema should be registered. + :param str schema_name: Name of schema being registered. + :param serialization_type: Serialization type for the schema being registered. + The + :type serialization_type: Union[str, SerializationType] + :param str schema_content: String representation of the schema being registered. + :rtype: SchemaProperties + + .. admonition:: Example: + + .. literalinclude:: ../samples/sync_samples/sample_code_schemaregistry.py + :start-after: [START get_schema_id_sync] + :end-before: [END get_schema_id_sync] + :language: python + :dedent: 4 + :caption: Get schema id. + + """ + try: + serialization_type = serialization_type.value + except AttributeError: + pass + + return self._generated_client.schema.query_id_by_content( + group_name=schema_group, + schema_name=schema_name, + schema_content=schema_content, + x_schema_type=serialization_type, + cls=_parse_response_schema_id, + **kwargs + ) diff --git a/sdk/schemaregistry/azure-schemaregistry/azure/schemaregistry/_version.py b/sdk/schemaregistry/azure-schemaregistry/azure/schemaregistry/_version.py index ac9f392f513e..d316addb36cb 100644 --- a/sdk/schemaregistry/azure-schemaregistry/azure/schemaregistry/_version.py +++ b/sdk/schemaregistry/azure-schemaregistry/azure/schemaregistry/_version.py @@ -1,6 +1,27 @@ -# ------------------------------------ -# Copyright (c) Microsoft Corporation. -# Licensed under the MIT License. -# ------------------------------------ +# -------------------------------------------------------------------------- +# +# Copyright (c) Microsoft Corporation. All rights reserved. +# +# The MIT License (MIT) +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the ""Software""), to +# deal in the Software without restriction, including without limitation the +# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +# sell copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +# IN THE SOFTWARE. +# +# -------------------------------------------------------------------------- VERSION = "1.0.0b1" diff --git a/sdk/schemaregistry/azure-schemaregistry/azure/schemaregistry/aio/__init__.py b/sdk/schemaregistry/azure-schemaregistry/azure/schemaregistry/aio/__init__.py new file mode 100644 index 000000000000..07b86b655bcd --- /dev/null +++ b/sdk/schemaregistry/azure-schemaregistry/azure/schemaregistry/aio/__init__.py @@ -0,0 +1,30 @@ +# -------------------------------------------------------------------------- +# +# Copyright (c) Microsoft Corporation. All rights reserved. +# +# The MIT License (MIT) +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the ""Software""), to +# deal in the Software without restriction, including without limitation the +# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +# sell copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +# IN THE SOFTWARE. +# +# -------------------------------------------------------------------------- +from ._schema_registry_client_async import SchemaRegistryClient + +__all__ = [ + "SchemaRegistryClient" +] diff --git a/sdk/schemaregistry/azure-schemaregistry/azure/schemaregistry/aio/_schema_registry_client_async.py b/sdk/schemaregistry/azure-schemaregistry/azure/schemaregistry/aio/_schema_registry_client_async.py new file mode 100644 index 000000000000..6c3835a94d98 --- /dev/null +++ b/sdk/schemaregistry/azure-schemaregistry/azure/schemaregistry/aio/_schema_registry_client_async.py @@ -0,0 +1,191 @@ +# -------------------------------------------------------------------------- +# +# Copyright (c) Microsoft Corporation. All rights reserved. +# +# The MIT License (MIT) +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the ""Software""), to +# deal in the Software without restriction, including without limitation the +# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +# sell copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +# IN THE SOFTWARE. +# +# -------------------------------------------------------------------------- +from typing import Any, TYPE_CHECKING, Union + +from .._common._constants import SerializationType +from .._common._response_handlers import _parse_response_schema_id, _parse_response_schema +from .._common._schema import SchemaProperties, Schema +from .._generated.aio._azure_schema_registry import AzureSchemaRegistry + +if TYPE_CHECKING: + from azure.core.credentials_async import AsyncTokenCredential + + +class SchemaRegistryClient(object): + """ + SchemaRegistryClient is as a central schema repository for enterprise-level data infrastructure, + complete with support for versioning and management. + + :param str endpoint: The Schema Registry service endpoint, for example my-namespace.servicebus.windows.net. + :param credential: To authenticate to manage the entities of the SchemaRegistry namespace. + :type credential: AsyncTokenCredential + + .. admonition:: Example: + + .. literalinclude:: ../samples/async_samples/sample_code_schemaregistry_async.py + :start-after: [START create_sr_client_async] + :end-before: [END create_sr_client_async] + :language: python + :dedent: 4 + :caption: Create a new instance of the SchemaRegistryClient. + + """ + def __init__( + self, + endpoint: str, + credential: "AsyncTokenCredential", + **kwargs: Any + ) -> None: + self._generated_client = AzureSchemaRegistry(credential, endpoint, **kwargs) + + async def __aenter__(self): + await self._generated_client.__aenter__() + return self + + async def __aexit__(self, *args): + await self._generated_client.__aexit__(*args) + + async def close(self) -> None: + """ This method is to close the sockets opened by the client. + It need not be used when using with a context manager. + """ + await self._generated_client.close() + + async def register_schema( + self, + schema_group: str, + schema_name: str, + serialization_type: Union[str, SerializationType], + schema_content: str, + **kwargs: Any + ) -> SchemaProperties: + """ + Register new schema. If schema of specified name does not exist in specified group, + schema is created at version 1. If schema of specified name exists already in specified group, + schema is created at latest version + 1. + + :param str schema_group: Schema group under which schema should be registered. + :param str schema_name: Name of schema being registered. + :param serialization_type: Serialization type for the schema being registered. + For now Avro is the only supported serialization type by the service. + :type serialization_type: Union[str, SerializationType] + :param str schema_content: String representation of the schema being registered. + :rtype: SchemaProperties + + .. admonition:: Example: + + .. literalinclude:: ../samples/async_samples/sample_code_schemaregistry.py + :start-after: [START register_schema_async] + :end-before: [END register_schema_async] + :language: python + :dedent: 4 + :caption: Register a new schema. + + """ + try: + serialization_type = serialization_type.value + except AttributeError: + pass + + return await self._generated_client.schema.register( + group_name=schema_group, + schema_name=schema_name, + schema_content=schema_content, + x_schema_type=serialization_type, + cls=_parse_response_schema_id, + **kwargs + ) + + async def get_schema( + self, + schema_id: str, + **kwargs: Any + ) -> Schema: + """ + Gets a registered schema by its unique ID. + Azure Schema Registry guarantees that ID is unique within a namespace. + + :param str schema_id: References specific schema in registry namespace. + :rtype: Schema + + .. admonition:: Example: + + .. literalinclude:: ../samples/async_samples/sample_code_schemaregistry.py + :start-after: [START get_schema_async] + :end-before: [END get_schema_async] + :language: python + :dedent: 4 + :caption: Get schema by id. + + """ + return await self._generated_client.schema.get_by_id( + schema_id=schema_id, + cls=_parse_response_schema, + **kwargs + ) + + async def get_schema_id( + self, + schema_group: str, + schema_name: str, + serialization_type: Union[str, SerializationType], + schema_content: str, + **kwargs: Any + ) -> SchemaProperties: + """ + Gets the ID referencing an existing schema within the specified schema group, + as matched by schema content comparison. + + :param str schema_group: Schema group under which schema should be registered. + :param str schema_name: Name of schema being registered. + :param serialization_type: Serialization type for the schema being registered. + :type serialization_type: Union[str, SerializationType] + :param str schema_content: String representation of the schema being registered. + :rtype: SchemaProperties + + .. admonition:: Example: + + .. literalinclude:: ../samples/async_samples/sample_code_schemaregistry_async.py + :start-after: [START get_schema_id_async] + :end-before: [END get_schema_id_async] + :language: python + :dedent: 4 + :caption:Get schema id. + + """ + try: + serialization_type = serialization_type.value + except AttributeError: + pass + + return await self._generated_client.schema.query_id_by_content( + group_name=schema_group, + schema_name=schema_name, + schema_content=schema_content, + x_schema_type=serialization_type, + cls=_parse_response_schema_id, + **kwargs + ) diff --git a/sdk/schemaregistry/azure-schemaregistry/azure/schemaregistry/serializer/__init__.py b/sdk/schemaregistry/azure-schemaregistry/azure/schemaregistry/serializer/__init__.py new file mode 100644 index 000000000000..c36aaed14908 --- /dev/null +++ b/sdk/schemaregistry/azure-schemaregistry/azure/schemaregistry/serializer/__init__.py @@ -0,0 +1,25 @@ +# -------------------------------------------------------------------------- +# +# Copyright (c) Microsoft Corporation. All rights reserved. +# +# The MIT License (MIT) +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the ""Software""), to +# deal in the Software without restriction, including without limitation the +# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +# sell copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +# IN THE SOFTWARE. +# +# -------------------------------------------------------------------------- diff --git a/sdk/schemaregistry/azure-schemaregistry/samples/sample_placeholder.py b/sdk/schemaregistry/azure-schemaregistry/conftest.py similarity index 54% rename from sdk/schemaregistry/azure-schemaregistry/samples/sample_placeholder.py rename to sdk/schemaregistry/azure-schemaregistry/conftest.py index e779593d1124..4e4d47319fb6 100644 --- a/sdk/schemaregistry/azure-schemaregistry/samples/sample_placeholder.py +++ b/sdk/schemaregistry/azure-schemaregistry/conftest.py @@ -1,7 +1,17 @@ -# ------------------------------------------------------------------------- +# ------------------------------------------------------------------------ # Copyright (c) Microsoft Corporation. All rights reserved. # Licensed under the MIT License. See License.txt in the project root for # license information. -# -------------------------------------------------------------------------- +# ------------------------------------------------------------------------- + +import os +import sys +import uuid + +import pytest -# Samples will be added later +# Ignore async tests for Python < 3.5 +collect_ignore = [] +if sys.version_info < (3, 5): + collect_ignore.append("tests/async_tests") + collect_ignore.append("samples/async_samples") diff --git a/sdk/schemaregistry/azure-schemaregistry/dev_requirements.txt b/sdk/schemaregistry/azure-schemaregistry/dev_requirements.txt index e69de29bb2d1..f15ae1a63a98 100644 --- a/sdk/schemaregistry/azure-schemaregistry/dev_requirements.txt +++ b/sdk/schemaregistry/azure-schemaregistry/dev_requirements.txt @@ -0,0 +1,5 @@ +-e ../../../tools/azure-devtools +-e ../../../tools/azure-sdk-tools +-e ../../core/azure-core +-e ../../identity/azure-identity +aiohttp>=3.0; python_version >= '3.5' \ No newline at end of file diff --git a/sdk/schemaregistry/azure-schemaregistry/samples/README.md b/sdk/schemaregistry/azure-schemaregistry/samples/README.md index e69de29bb2d1..a8eddb04a14e 100644 --- a/sdk/schemaregistry/azure-schemaregistry/samples/README.md +++ b/sdk/schemaregistry/azure-schemaregistry/samples/README.md @@ -0,0 +1,54 @@ +--- +page_type: sample +languages: + - python +products: + - azure + - azure-schema-registry +urlFragment: schemaregistry-samples +--- + +# Azure Schema Registry client library for Python Samples + +These are code samples that show common scenario operations with the Schema Registry client library. +The async versions of the samples (the python sample files appended with `_async`) show asynchronous operations, +and require Python 3.5 or later. + +Several Schema Registry Python SDK samples are available to you in the SDK's GitHub repository. These samples provide example code for additional scenarios commonly encountered while working with Schema Registry: + +* [schema_registry.py][schema_registry_sample] ([async version][schema_registry_async_sample]) - Examples for common Schema Registry tasks: + * Register a schema + * Get schema by id + * Get schema id + +## Prerequisites +- Python 2.7, 3.5 or later. +- **Microsoft Azure Subscription:** To use Azure services, including Azure Schema Registry, you'll need a subscription. +If you do not have an existing Azure account, you may sign up for a free trial or use your MSDN subscriber benefits when you [create an account](https://account.windowsazure.com/Home/Index). + +## Setup + +1. Install the Azure Schema Registry client library and Azure Identity client library for Python with [pip](https://pypi.org/project/pip/): + +```bash +pip install azure-schemaregistry azure-identity +``` + +2. Clone or download this sample repository +3. Open the sample folder in Visual Studio Code or your IDE of choice. + +## Running the samples + +1. Open a terminal window and `cd` to the directory that the samples are saved in. +2. Set the environment variables specified in the sample file you wish to run. +3. Follow the usage described in the file, e.g. `python schema_registry.py` + +## Next steps + +Check out the [API reference documentation][api_reference] to learn more about +what you can do with the Azure Schema Registry client library. + + +[schema_registry_sample]: https://github.com/Azure/azure-sdk-for-python/tree/master/sdk/schemaregistry/azure-schemaregistry/samples/sync_samples/schema_registry.py +[schema_registry_async_sample]: https://github.com/Azure/azure-sdk-for-python/tree/master/sdk/schemaregistry/azure-schemaregistry/samples/async_samples/schema_registry_async.py +[api_reference]: https://azuresdkdocs.blob.core.windows.net/$web/python/azure-schemaregistry/latest/index.html diff --git a/sdk/schemaregistry/azure-schemaregistry/samples/async_samples/sample_schema_registry_code_async.py b/sdk/schemaregistry/azure-schemaregistry/samples/async_samples/sample_schema_registry_code_async.py new file mode 100644 index 000000000000..75788da911b7 --- /dev/null +++ b/sdk/schemaregistry/azure-schemaregistry/samples/async_samples/sample_schema_registry_code_async.py @@ -0,0 +1,108 @@ +# -------------------------------------------------------------------------- +# +# Copyright (c) Microsoft Corporation. All rights reserved. +# +# The MIT License (MIT) +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the ""Software""), to +# deal in the Software without restriction, including without limitation the +# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +# sell copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +# IN THE SOFTWARE. +# +# -------------------------------------------------------------------------- +import os +import asyncio + +from azure.schemaregistry.aio import SchemaRegistryClient +from azure.schemaregistry import SerializationType +from azure.identity.aio import ClientSecretCredential, DefaultAzureCredential + + +def create_client(): + # [START create_sr_client_async] + SCHEMA_REGISTRY_ENDPOINT = os.environ['SCHEMA_REGISTRY_ENDPOINT'] + token_credential = DefaultAzureCredential() + schema_registry_client = SchemaRegistryClient(endpoint=SCHEMA_REGISTRY_ENDPOINT, credential=token_credential) + # [END create_sr_client_async] + TENANT_ID = os.environ['SCHEMA_REGISTRY_AZURE_TENANT_ID'] + CLIENT_ID = os.environ['SCHEMA_REGISTRY_AZURE_CLIENT_ID'] + CLIENT_SECRET = os.environ['SCHEMA_REGISTRY_AZURE_CLIENT_SECRET'] + token_credential = ClientSecretCredential(TENANT_ID, CLIENT_ID, CLIENT_SECRET) + schema_registry_client = SchemaRegistryClient(endpoint=SCHEMA_REGISTRY_ENDPOINT, credential=token_credential) + return schema_registry_client, token_credential + + +async def register_scehma(schema_registry_client): + # [START register_schema_async] + SCHEMA_GROUP = os.environ['SCHEMA_REGISTRY_GROUP'] + SCHEMA_NAME = 'your-schema-name' + SERIALIZATION_TYPE = SerializationType.AVRO + SCHEMA_CONTENT = """ + {"namespace": "example.avro", + "type": "record", + "name": "User", + "fields": [ + {"name": "name", "type": "string"}, + {"name": "favorite_number", "type": ["int", "null"]}, + {"name": "favorite_color", "type": ["string", "null"]} + ] + }""" + schema_properties = await schema_registry_client.register_schema(SCHEMA_GROUP, SCHEMA_NAME, SERIALIZATION_TYPE, SCHEMA_CONTENT) + schem_id = schema_properties.schema_id + # [END register_schema_async] + return schem_id + + +async def get_schema(schema_registry_client, schema_id): + # [START get_schema_async] + schema = await schema_registry_client.get_schema(schema_id) + schema_content = schema.schema_content + # [END get_schema_async] + + +async def get_schema_id(schema_registry_client): + schema_group = os.environ['SCHEMA_REGISTRY_GROUP'] + schema_name = 'your-schema-name' + serialization_type = SerializationType.AVRO + schema_content = """ + {"namespace": "example.avro", + "type": "record", + "name": "User", + "fields": [ + {"name": "name", "type": "string"}, + {"name": "favorite_number", "type": ["int", "null"]}, + {"name": "favorite_color", "type": ["string", "null"]} + ] + }""" + + # [START get_schema_id_async] + schema_properties = await schema_registry_client.get_schema_id(schema_group, schema_name, serialization_type, schema_content) + schem_id = schema_properties.schema_id + # [END get_schema_id_async] + + +async def main(): + client, credential = create_client() + async with client, credential: + id = await register_scehma(client) + schema = await get_schema(client, id) + id = await get_schema_id(client) + + +if __name__ == '__main__': + loop = asyncio.get_event_loop() + loop.run_until_complete(main()) + diff --git a/sdk/schemaregistry/azure-schemaregistry/samples/async_samples/schema_registry_async.py b/sdk/schemaregistry/azure-schemaregistry/samples/async_samples/schema_registry_async.py new file mode 100644 index 000000000000..2efadb028dca --- /dev/null +++ b/sdk/schemaregistry/azure-schemaregistry/samples/async_samples/schema_registry_async.py @@ -0,0 +1,79 @@ +# ------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for +# license information. +# -------------------------------------------------------------------------- + +""" +Example to show basic usage of schema registry asynchronously: + - register a schema + - get schema by id + - get schema id +""" + +import asyncio +import os + +from azure.identity.aio import ClientSecretCredential +from azure.schemaregistry.aio import SchemaRegistryClient +from azure.schemaregistry import SerializationType + +TENANT_ID = os.environ['SCHEMA_REGISTRY_AZURE_TENANT_ID'] +CLIENT_ID = os.environ['SCHEMA_REGISTRY_AZURE_CLIENT_ID'] +CLIENT_SECRET = os.environ['SCHEMA_REGISTRY_AZURE_CLIENT_SECRET'] + +SCHEMA_REGISTRY_ENDPOINT = os.environ['SCHEMA_REGISTRY_ENDPOINT'] +SCHEMA_GROUP = os.environ['SCHEMA_REGISTRY_GROUP'] +SCHEMA_NAME = 'your-schema-name' +SERIALIZATION_TYPE = SerializationType.AVRO +SCHEMA_STRING = """ +{"namespace": "example.avro", + "type": "record", + "name": "User", + "fields": [ + {"name": "name", "type": "string"}, + {"name": "favorite_number", "type": ["int", "null"]}, + {"name": "favorite_color", "type": ["string", "null"]} + ] +}""" + + +async def register_schema(client, schema_group, schema_name, serialization_type, schema_string): + print("Registering schema...") + schema_properties = await client.register_schema(schema_group, schema_name, serialization_type, schema_string) + print("Schema registered, returned schema id is {}".format(schema_properties.schema_id)) + print("Schema properties are {}".format(schema_properties)) + return schema_properties.schema_id + + +async def get_schema_by_id(client, schema_id): + print("Getting schema by id...") + schema = await client.get_schema(schema_id) + print("The schema string of schema id: {} string is {}".format(schema_id, schema.schema_content)) + print("Schema properties are {}".format(schema_id)) + return schema.schema_content + + +async def get_schema_id(client, schema_group, schema_name, serialization_type, schema_string): + print("Getting schema id...") + schema_properties = await client.get_schema_id(schema_group, schema_name, serialization_type, schema_string) + print("The schema id is: {}".format(schema_properties.schema_id)) + print("Schema properties are {}".format(schema_properties)) + return schema_properties.schema_id + + +async def main(): + token_credential = ClientSecretCredential( + tenant_id=TENANT_ID, + client_id=CLIENT_ID, + client_secret=CLIENT_SECRET + ) + schema_registry_client = SchemaRegistryClient(endpoint=SCHEMA_REGISTRY_ENDPOINT, credential=token_credential) + async with token_credential, schema_registry_client: + schema_id = await register_schema(schema_registry_client, SCHEMA_GROUP, SCHEMA_NAME, SERIALIZATION_TYPE, SCHEMA_STRING) + schema_str = await get_schema_by_id(schema_registry_client, schema_id) + schema_id = await get_schema_id(schema_registry_client, SCHEMA_GROUP, SCHEMA_NAME, SERIALIZATION_TYPE, SCHEMA_STRING) + + +loop = asyncio.get_event_loop() +loop.run_until_complete(main()) diff --git a/sdk/schemaregistry/azure-schemaregistry/samples/sync_samples/sample_schema_registry_code.py b/sdk/schemaregistry/azure-schemaregistry/samples/sync_samples/sample_schema_registry_code.py new file mode 100644 index 000000000000..30c7781f889d --- /dev/null +++ b/sdk/schemaregistry/azure-schemaregistry/samples/sync_samples/sample_schema_registry_code.py @@ -0,0 +1,113 @@ +# -------------------------------------------------------------------------- +# +# Copyright (c) Microsoft Corporation. All rights reserved. +# +# The MIT License (MIT) +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the ""Software""), to +# deal in the Software without restriction, including without limitation the +# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +# sell copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +# IN THE SOFTWARE. +# +# -------------------------------------------------------------------------- +import os + +from azure.schemaregistry import SchemaRegistryClient, SerializationType +from azure.identity import ClientSecretCredential, DefaultAzureCredential + + +def create_client(): + # [START create_sr_client_sync] + SCHEMA_REGISTRY_ENDPOINT = os.environ['SCHEMA_REGISTRY_ENDPOINT'] + token_credential = DefaultAzureCredential() + schema_registry_client = SchemaRegistryClient(endpoint=SCHEMA_REGISTRY_ENDPOINT, credential=token_credential) + # [END create_sr_client_sync] + TENANT_ID = os.environ['SCHEMA_REGISTRY_AZURE_TENANT_ID'] + CLIENT_ID = os.environ['SCHEMA_REGISTRY_AZURE_CLIENT_ID'] + CLIENT_SECRET = os.environ['SCHEMA_REGISTRY_AZURE_CLIENT_SECRET'] + token_credential = ClientSecretCredential(TENANT_ID, CLIENT_ID, CLIENT_SECRET) + schema_registry_client = SchemaRegistryClient(endpoint=SCHEMA_REGISTRY_ENDPOINT, credential=token_credential) + return schema_registry_client + + +def register_scehma(schema_registry_client): + # [START register_schema_sync] + SCHEMA_GROUP = os.environ['SCHEMA_REGISTRY_GROUP'] + SCHEMA_NAME = 'your-schema-name' + SERIALIZATION_TYPE = SerializationType.AVRO + SCHEMA_CONTENT = """ + {"namespace": "example.avro", + "type": "record", + "name": "User", + "fields": [ + {"name": "name", "type": "string"}, + {"name": "favorite_number", "type": ["int", "null"]}, + {"name": "favorite_color", "type": ["string", "null"]} + ] + }""" + schema_properties = schema_registry_client.register_schema(SCHEMA_GROUP, SCHEMA_NAME, SERIALIZATION_TYPE, SCHEMA_CONTENT) + schem_id = schema_properties.schema_id + # [END register_schema_sync] + + # [START print_schema_properties] + print(schema_properties.schema_id) + print(schema_properties.location) + print(schema_properties.location_by_id) + print(schema_properties.serialization_type) + print(schema_properties.version) + # [END print_schema_properties] + + return schem_id + + +def get_schema(schema_registry_client, schema_id): + # [START get_schema_sync] + schema = schema_registry_client.get_schema(schema_id) + schema_content = schema.schema_content + # [END get_schema_sync] + + # [START print_schema] + print(schema.schema_content) + print(schema.schema_properties) + # [END print_schema] + + +def get_schema_id(schema_registry_client): + schema_group = os.environ['SCHEMA_REGISTRY_GROUP'] + schema_name = 'your-schema-name' + serialization_type = SerializationType.AVRO + schema_content = """ + {"namespace": "example.avro", + "type": "record", + "name": "User", + "fields": [ + {"name": "name", "type": "string"}, + {"name": "favorite_number", "type": ["int", "null"]}, + {"name": "favorite_color", "type": ["string", "null"]} + ] + }""" + # [START get_schema_id_sync] + schema_properties = schema_registry_client.get_schema_id(schema_group, schema_name, serialization_type, schema_content) + schem_id = schema_properties.schema_id + # [END get_schema_id_sync] + + +if __name__ == '__main__': + client = create_client() + with client: + id = register_scehma(client) + schema = get_schema(client, id) + id = get_schema_id(client) diff --git a/sdk/schemaregistry/azure-schemaregistry/samples/sync_samples/schema_registry.py b/sdk/schemaregistry/azure-schemaregistry/samples/sync_samples/schema_registry.py new file mode 100644 index 000000000000..e1c1d80e6641 --- /dev/null +++ b/sdk/schemaregistry/azure-schemaregistry/samples/sync_samples/schema_registry.py @@ -0,0 +1,95 @@ +# -------------------------------------------------------------------------- +# +# Copyright (c) Microsoft Corporation. All rights reserved. +# +# The MIT License (MIT) +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the ""Software""), to +# deal in the Software without restriction, including without limitation the +# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +# sell copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +# IN THE SOFTWARE. +# +# -------------------------------------------------------------------------- + +""" +Example to show basic usage of schema registry: + - register a schema + - get schema by id + - get schema id +""" + + +import os + +from azure.identity import ClientSecretCredential +from azure.schemaregistry import SchemaRegistryClient, SerializationType + +TENANT_ID = os.environ['SCHEMA_REGISTRY_AZURE_TENANT_ID'] +CLIENT_ID = os.environ['SCHEMA_REGISTRY_AZURE_CLIENT_ID'] +CLIENT_SECRET = os.environ['SCHEMA_REGISTRY_AZURE_CLIENT_SECRET'] + +SCHEMA_REGISTRY_ENDPOINT = os.environ['SCHEMA_REGISTRY_ENDPOINT'] +SCHEMA_GROUP = os.environ['SCHEMA_REGISTRY_GROUP'] +SCHEMA_NAME = 'your-schema-name' +SERIALIZATION_TYPE = SerializationType.AVRO +SCHEMA_STRING = """ +{"namespace": "example.avro", + "type": "record", + "name": "User", + "fields": [ + {"name": "name", "type": "string"}, + {"name": "favorite_number", "type": ["int", "null"]}, + {"name": "favorite_color", "type": ["string", "null"]} + ] +}""" + + +def register_schema(client, schema_group, schema_name, serialization_type, schema_string): + print("Registering schema...") + schema_properties = client.register_schema(schema_group, schema_name, serialization_type, schema_string) + print("Schema registered, returned schema id is {}".format(schema_properties.schema_id)) + print("Schema properties are {}".format(schema_properties)) + return schema_properties.schema_id + + +def get_schema_by_id(client, schema_id): + print("Getting schema by id...") + schema = client.get_schema(schema_id) + print("The schema string of schema id: {} string is {}".format(schema_id, schema.schema_content)) + print("Schema properties are {}".format(schema_id)) + return schema.schema_content + + +def get_schema_id(client, schema_group, schema_name, serialization_type, schema_string): + print("Getting schema id...") + schema_properties = client.get_schema_id(schema_group, schema_name, serialization_type, schema_string) + print("The schema id is: {}".format(schema_properties.schema_id)) + print("Schema properties are {}".format(schema_properties)) + return schema_properties.schema_id + + +if __name__ == '__main__': + token_credential = ClientSecretCredential( + tenant_id=TENANT_ID, + client_id=CLIENT_ID, + client_secret=CLIENT_SECRET + ) + schema_registry_client = SchemaRegistryClient(endpoint=SCHEMA_REGISTRY_ENDPOINT, credential=token_credential) + with schema_registry_client: + schema_id = register_schema(schema_registry_client, SCHEMA_GROUP, SCHEMA_NAME, SERIALIZATION_TYPE, SCHEMA_STRING) + schema_str = get_schema_by_id(schema_registry_client, schema_id=schema_id) + schema_id = get_schema_id(schema_registry_client, SCHEMA_GROUP, SCHEMA_NAME, SERIALIZATION_TYPE, SCHEMA_STRING) + diff --git a/sdk/schemaregistry/azure-schemaregistry/setup.py b/sdk/schemaregistry/azure-schemaregistry/setup.py index 8f8ca94f9e59..118bd0775d15 100644 --- a/sdk/schemaregistry/azure-schemaregistry/setup.py +++ b/sdk/schemaregistry/azure-schemaregistry/setup.py @@ -20,20 +20,6 @@ # a-b-c => a.b.c namespace_name = PACKAGE_NAME.replace('-', '.') -# azure v0.x is not compatible with this package -# azure v0.x used to have a __version__ attribute (newer versions don't) -try: - import azure - try: - ver = azure.__version__ - raise Exception( - 'This package is incompatible with azure=={}. '.format(ver) + - 'Uninstall it with "pip uninstall azure".' - ) - except AttributeError: - pass -except ImportError: - pass # Version extraction inspired from 'requests' with open(os.path.join(package_folder_path, '_version.py'), 'r') as fd: @@ -48,6 +34,13 @@ with open('CHANGELOG.md', encoding='utf-8') as f: changelog = f.read() +exclude_packages = [ + 'tests', + 'samples', + # Exclude packages that will be covered by PEP420 or nspkg + 'azure', + ] + setup( name=PACKAGE_NAME, version=version, @@ -71,17 +64,12 @@ 'License :: OSI Approved :: MIT License', ], zip_safe=False, - packages=find_packages(exclude=[ - 'tests', - 'samples', - # Exclude packages that will be covered by PEP420 or nspkg - 'azure', - ]), + packages=find_packages(exclude=exclude_packages), install_requires=[ 'msrest>=0.5.0', 'azure-core<2.0.0,>=1.2.2' ], extras_require={ - ":python_version<'3.0'": ['azure-nspkg', 'futures'], + ":python_version<'3.0'": ['azure-nspkg'] } ) diff --git a/sdk/schemaregistry/azure-schemaregistry/swagger/README.md b/sdk/schemaregistry/azure-schemaregistry/swagger/README.md index 6acc525b597f..be8dca675f22 100644 --- a/sdk/schemaregistry/azure-schemaregistry/swagger/README.md +++ b/sdk/schemaregistry/azure-schemaregistry/swagger/README.md @@ -5,7 +5,8 @@ ### Generation ```ps cd C:\Work\SchemaRegistry\ -autorest --v3 --python --use=@autorest/python@5.0.0-preview.6 +autorest --reset +autorest --v3 --python ``` ### Settings ``` yaml @@ -16,5 +17,7 @@ no-namespace-folders: true license-header: MICROSOFT_MIT_NO_VERSION clear-output-folder: true python: true +add-credential: true +credential-scopes: "https://eventhubs.azure.net/.default" package-version: "1.0.0b1" ``` diff --git a/sdk/schemaregistry/azure-schemaregistry/tests/async_tests/recordings/test_schema_registry_async.test_schema_basic_async.yaml b/sdk/schemaregistry/azure-schemaregistry/tests/async_tests/recordings/test_schema_registry_async.test_schema_basic_async.yaml new file mode 100644 index 000000000000..c78f2730ba15 --- /dev/null +++ b/sdk/schemaregistry/azure-schemaregistry/tests/async_tests/recordings/test_schema_registry_async.test_schema_basic_async.yaml @@ -0,0 +1,98 @@ +interactions: +- request: + body: '"{\"namespace\":\"example.avro\",\"type\":\"record\",\"name\":\"User\",\"fields\":[{\"name\":\"name\",\"type\":\"string\"},{\"name\":\"favorite_number\",\"type\":[\"int\",\"null\"]},{\"name\":\"favorite_color\",\"type\":[\"string\",\"null\"]}]}"' + headers: + Accept: + - application/json + Content-Length: + - '245' + Content-Type: + - application/json + User-Agent: + - azsdk-python-azureschemaregistry/1.0.0b1 Python/3.7.7 (Windows-10-10.0.19041-SP0) + X-Schema-Type: + - Avro + method: PUT + uri: https://sr-playground.servicebus.windows.net/$schemagroups/azsdk_python_test_group/schemas/test-schema-basic-asynce5e1482?api-version=2017-04 + response: + body: + string: '{"id":"b8503db5451844488672458ba137c27b"}' + headers: + content-type: application/json + date: Fri, 04 Sep 2020 22:07:30 GMT + location: https://sr-playground.servicebus.windows.net:443/$schemagroups/azsdk_python_test_group/schemas/test-schema-basic-asynce5e1482/versions/1?api-version=2017-04 + server: Microsoft-HTTPAPI/2.0 + strict-transport-security: max-age=31536000 + transfer-encoding: chunked + x-schema-id: b8503db5451844488672458ba137c27b + x-schema-id-location: https://sr-playground.servicebus.windows.net:443/$schemagroups/getschemabyid/b8503db5451844488672458ba137c27b?api-version=2017-04 + x-schema-type: Avro + x-schema-version: '1' + x-schema-versions-location: https://sr-playground.servicebus.windows.net:443/$schemagroups/azsdk_python_test_group/schemas/test-schema-basic-asynce5e1482/versions?api-version=2017-04 + status: + code: 200 + message: OK + url: https://sr-playground.servicebus.windows.net/$schemagroups/azsdk_python_test_group/schemas/test-schema-basic-asynce5e1482?api-version=2017-04 +- request: + body: null + headers: + Accept: + - application/json + User-Agent: + - azsdk-python-azureschemaregistry/1.0.0b1 Python/3.7.7 (Windows-10-10.0.19041-SP0) + method: GET + uri: https://sr-playground.servicebus.windows.net/$schemagroups/getSchemaById/b8503db5451844488672458ba137c27b?api-version=2017-04 + response: + body: + string: '"{\"namespace\":\"example.avro\",\"type\":\"record\",\"name\":\"User\",\"fields\":[{\"name\":\"name\",\"type\":\"string\"},{\"name\":\"favorite_number\",\"type\":[\"int\",\"null\"]},{\"name\":\"favorite_color\",\"type\":[\"string\",\"null\"]}]}"' + headers: + content-type: application/json + date: Fri, 04 Sep 2020 22:07:30 GMT + location: https://sr-playground.servicebus.windows.net:443/$schemagroups/azsdk_python_test_group/schemas/test-schema-basic-asynce5e1482/versions/1?api-version=2017-04 + server: Microsoft-HTTPAPI/2.0 + strict-transport-security: max-age=31536000 + transfer-encoding: chunked + x-schema-id: b8503db5451844488672458ba137c27b + x-schema-id-location: https://sr-playground.servicebus.windows.net:443/$schemagroups/getschemabyid/b8503db5451844488672458ba137c27b?api-version=2017-04 + x-schema-type: Avro + x-schema-version: '1' + x-schema-versions-location: https://sr-playground.servicebus.windows.net:443/$schemagroups/azsdk_python_test_group/schemas/test-schema-basic-asynce5e1482/versions?api-version=2017-04 + status: + code: 200 + message: OK + url: https://sr-playground.servicebus.windows.net/$schemagroups/getSchemaById/b8503db5451844488672458ba137c27b?api-version=2017-04 +- request: + body: '"{\"namespace\":\"example.avro\",\"type\":\"record\",\"name\":\"User\",\"fields\":[{\"name\":\"name\",\"type\":\"string\"},{\"name\":\"favorite_number\",\"type\":[\"int\",\"null\"]},{\"name\":\"favorite_color\",\"type\":[\"string\",\"null\"]}]}"' + headers: + Accept: + - application/json + Content-Length: + - '245' + Content-Type: + - application/json + User-Agent: + - azsdk-python-azureschemaregistry/1.0.0b1 Python/3.7.7 (Windows-10-10.0.19041-SP0) + X-Schema-Type: + - Avro + method: POST + uri: https://sr-playground.servicebus.windows.net/$schemagroups/azsdk_python_test_group/schemas/test-schema-basic-asynce5e1482?api-version=2017-04 + response: + body: + string: '{"id":"b8503db5451844488672458ba137c27b"}' + headers: + content-type: application/json + date: Fri, 04 Sep 2020 22:07:31 GMT + location: https://sr-playground.servicebus.windows.net:443/$schemagroups/azsdk_python_test_group/schemas/test-schema-basic-asynce5e1482/versions/1?api-version=2017-04 + server: Microsoft-HTTPAPI/2.0 + strict-transport-security: max-age=31536000 + transfer-encoding: chunked + x-schema-id: b8503db5451844488672458ba137c27b + x-schema-id-location: https://sr-playground.servicebus.windows.net:443/$schemagroups/getschemabyid/b8503db5451844488672458ba137c27b?api-version=2017-04 + x-schema-type: Avro + x-schema-version: '1' + x-schema-versions-location: https://sr-playground.servicebus.windows.net:443/$schemagroups/azsdk_python_test_group/schemas/test-schema-basic-asynce5e1482/versions?api-version=2017-04 + status: + code: 200 + message: OK + url: https://sr-playground.servicebus.windows.net/$schemagroups/azsdk_python_test_group/schemas/test-schema-basic-asynce5e1482?api-version=2017-04 +version: 1 diff --git a/sdk/schemaregistry/azure-schemaregistry/tests/async_tests/recordings/test_schema_registry_async.test_schema_negative_no_schema_async.yaml b/sdk/schemaregistry/azure-schemaregistry/tests/async_tests/recordings/test_schema_registry_async.test_schema_negative_no_schema_async.yaml new file mode 100644 index 000000000000..66be60cfac3c --- /dev/null +++ b/sdk/schemaregistry/azure-schemaregistry/tests/async_tests/recordings/test_schema_registry_async.test_schema_negative_no_schema_async.yaml @@ -0,0 +1,51 @@ +interactions: +- request: + body: null + headers: + Accept: + - application/json + User-Agent: + - azsdk-python-azureschemaregistry/1.0.0b1 Python/3.7.7 (Windows-10-10.0.19041-SP0) + method: GET + uri: https://sr-playground.servicebus.windows.net/$schemagroups/getSchemaById/a?api-version=2017-04 + response: + body: + string: 400SubCode=40000, UnknownType:The request + is invalid. [MGResponseHttpError=BadRequest]. TrackingId:67733d61-7b5b-4e51-9b06-72a6afba9bdf_G1, + SystemTracker:sr-playground.servicebus.windows.net:$schemagroups/getSchemaById/a, + Timestamp:2020-09-04T22:07:32 + headers: + content-type: application/xml; charset=utf-8 + date: Fri, 04 Sep 2020 22:07:32 GMT + server: Microsoft-HTTPAPI/2.0 + strict-transport-security: max-age=31536000 + transfer-encoding: chunked + status: + code: 400 + message: Bad Request + url: https://sr-playground.servicebus.windows.net/$schemagroups/getSchemaById/a?api-version=2017-04 +- request: + body: null + headers: + Accept: + - application/json + User-Agent: + - azsdk-python-azureschemaregistry/1.0.0b1 Python/3.7.7 (Windows-10-10.0.19041-SP0) + method: GET + uri: https://sr-playground.servicebus.windows.net/$schemagroups/getSchemaById/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa?api-version=2017-04 + response: + body: + string: 404Schema id aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + does not exist. TrackingId:17234c8c-2982-4d72-b05d-28df0d550d73_G1, SystemTracker:sr-playground.servicebus.windows.net:$schemagroups/getSchemaById/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa, + Timestamp:2020-09-04T22:07:33 + headers: + content-type: application/xml; charset=utf-8 + date: Fri, 04 Sep 2020 22:07:32 GMT + server: Microsoft-HTTPAPI/2.0 + strict-transport-security: max-age=31536000 + transfer-encoding: chunked + status: + code: 404 + message: Not Found + url: https://sr-playground.servicebus.windows.net/$schemagroups/getSchemaById/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa?api-version=2017-04 +version: 1 diff --git a/sdk/schemaregistry/azure-schemaregistry/tests/async_tests/recordings/test_schema_registry_async.test_schema_same_twice_async.yaml b/sdk/schemaregistry/azure-schemaregistry/tests/async_tests/recordings/test_schema_registry_async.test_schema_same_twice_async.yaml new file mode 100644 index 000000000000..b0a1a139e1c5 --- /dev/null +++ b/sdk/schemaregistry/azure-schemaregistry/tests/async_tests/recordings/test_schema_registry_async.test_schema_same_twice_async.yaml @@ -0,0 +1,70 @@ +interactions: +- request: + body: '"{\"namespace\":\"example.avro\",\"type\":\"record\",\"name\":\"User\",\"fields\":[{\"name\":\"name\",\"type\":\"string\"},{\"name\":\"age\",\"type\":[\"int\",\"null\"]},{\"name\":\"city\",\"type\":[\"string\",\"null\"]}]}"' + headers: + Accept: + - application/json + Content-Length: + - '223' + Content-Type: + - application/json + User-Agent: + - azsdk-python-azureschemaregistry/1.0.0b1 Python/3.7.7 (Windows-10-10.0.19041-SP0) + X-Schema-Type: + - Avro + method: PUT + uri: https://sr-playground.servicebus.windows.net/$schemagroups/azsdk_python_test_group/schemas/test-schema-twice-async7bfd16a1?api-version=2017-04 + response: + body: + string: '{"id":"7d0ff6342f0648b4a89829010b152e4f"}' + headers: + content-type: application/json + date: Fri, 04 Sep 2020 22:07:39 GMT + location: https://sr-playground.servicebus.windows.net:443/$schemagroups/azsdk_python_test_group/schemas/test-schema-twice-async7bfd16a1/versions/1?api-version=2017-04 + server: Microsoft-HTTPAPI/2.0 + strict-transport-security: max-age=31536000 + transfer-encoding: chunked + x-schema-id: 7d0ff6342f0648b4a89829010b152e4f + x-schema-id-location: https://sr-playground.servicebus.windows.net:443/$schemagroups/getschemabyid/7d0ff6342f0648b4a89829010b152e4f?api-version=2017-04 + x-schema-type: Avro + x-schema-version: '1' + x-schema-versions-location: https://sr-playground.servicebus.windows.net:443/$schemagroups/azsdk_python_test_group/schemas/test-schema-twice-async7bfd16a1/versions?api-version=2017-04 + status: + code: 200 + message: OK + url: https://sr-playground.servicebus.windows.net/$schemagroups/azsdk_python_test_group/schemas/test-schema-twice-async7bfd16a1?api-version=2017-04 +- request: + body: '"{\"namespace\":\"example.avro\",\"type\":\"record\",\"name\":\"User\",\"fields\":[{\"name\":\"name\",\"type\":\"string\"},{\"name\":\"age\",\"type\":[\"int\",\"null\"]},{\"name\":\"city\",\"type\":[\"string\",\"null\"]}]}"' + headers: + Accept: + - application/json + Content-Length: + - '223' + Content-Type: + - application/json + User-Agent: + - azsdk-python-azureschemaregistry/1.0.0b1 Python/3.7.7 (Windows-10-10.0.19041-SP0) + X-Schema-Type: + - Avro + method: PUT + uri: https://sr-playground.servicebus.windows.net/$schemagroups/azsdk_python_test_group/schemas/test-schema-twice-async7bfd16a1?api-version=2017-04 + response: + body: + string: '{"id":"7d0ff6342f0648b4a89829010b152e4f"}' + headers: + content-type: application/json + date: Fri, 04 Sep 2020 22:07:39 GMT + location: https://sr-playground.servicebus.windows.net:443/$schemagroups/azsdk_python_test_group/schemas/test-schema-twice-async7bfd16a1/versions/1?api-version=2017-04 + server: Microsoft-HTTPAPI/2.0 + strict-transport-security: max-age=31536000 + transfer-encoding: chunked + x-schema-id: 7d0ff6342f0648b4a89829010b152e4f + x-schema-id-location: https://sr-playground.servicebus.windows.net:443/$schemagroups/getschemabyid/7d0ff6342f0648b4a89829010b152e4f?api-version=2017-04 + x-schema-type: Avro + x-schema-version: '1' + x-schema-versions-location: https://sr-playground.servicebus.windows.net:443/$schemagroups/azsdk_python_test_group/schemas/test-schema-twice-async7bfd16a1/versions?api-version=2017-04 + status: + code: 200 + message: OK + url: https://sr-playground.servicebus.windows.net/$schemagroups/azsdk_python_test_group/schemas/test-schema-twice-async7bfd16a1?api-version=2017-04 +version: 1 diff --git a/sdk/schemaregistry/azure-schemaregistry/tests/async_tests/recordings/test_schema_registry_async.test_schema_update_async.yaml b/sdk/schemaregistry/azure-schemaregistry/tests/async_tests/recordings/test_schema_registry_async.test_schema_update_async.yaml new file mode 100644 index 000000000000..316f7c90d7de --- /dev/null +++ b/sdk/schemaregistry/azure-schemaregistry/tests/async_tests/recordings/test_schema_registry_async.test_schema_update_async.yaml @@ -0,0 +1,98 @@ +interactions: +- request: + body: '"{\"namespace\":\"example.avro\",\"type\":\"record\",\"name\":\"User\",\"fields\":[{\"name\":\"name\",\"type\":\"string\"},{\"name\":\"favorite_number\",\"type\":[\"int\",\"null\"]},{\"name\":\"favorite_color\",\"type\":[\"string\",\"null\"]}]}"' + headers: + Accept: + - application/json + Content-Length: + - '245' + Content-Type: + - application/json + User-Agent: + - azsdk-python-azureschemaregistry/1.0.0b1 Python/3.7.7 (Windows-10-10.0.19041-SP0) + X-Schema-Type: + - Avro + method: PUT + uri: https://sr-playground.servicebus.windows.net/$schemagroups/azsdk_python_test_group/schemas/test-schema-update-async24591503?api-version=2017-04 + response: + body: + string: '{"id":"a960c5bf612b4f928b409ab2474de907"}' + headers: + content-type: application/json + date: Fri, 04 Sep 2020 22:07:41 GMT + location: https://sr-playground.servicebus.windows.net:443/$schemagroups/azsdk_python_test_group/schemas/test-schema-update-async24591503/versions/9?api-version=2017-04 + server: Microsoft-HTTPAPI/2.0 + strict-transport-security: max-age=31536000 + transfer-encoding: chunked + x-schema-id: a960c5bf612b4f928b409ab2474de907 + x-schema-id-location: https://sr-playground.servicebus.windows.net:443/$schemagroups/getschemabyid/a960c5bf612b4f928b409ab2474de907?api-version=2017-04 + x-schema-type: Avro + x-schema-version: '9' + x-schema-versions-location: https://sr-playground.servicebus.windows.net:443/$schemagroups/azsdk_python_test_group/schemas/test-schema-update-async24591503/versions?api-version=2017-04 + status: + code: 200 + message: OK + url: https://sr-playground.servicebus.windows.net/$schemagroups/azsdk_python_test_group/schemas/test-schema-update-async24591503?api-version=2017-04 +- request: + body: '"{\"namespace\":\"example.avro\",\"type\":\"record\",\"name\":\"User\",\"fields\":[{\"name\":\"name\",\"type\":\"string\"},{\"name\":\"favorite_number\",\"type\":[\"int\",\"null\"]},{\"name\":\"favorite_food\",\"type\":[\"string\",\"null\"]}]}"' + headers: + Accept: + - application/json + Content-Length: + - '244' + Content-Type: + - application/json + User-Agent: + - azsdk-python-azureschemaregistry/1.0.0b1 Python/3.7.7 (Windows-10-10.0.19041-SP0) + X-Schema-Type: + - Avro + method: PUT + uri: https://sr-playground.servicebus.windows.net/$schemagroups/azsdk_python_test_group/schemas/test-schema-update-async24591503?api-version=2017-04 + response: + body: + string: '{"id":"767b2ebdbe4e4ac2bfa48f58e0fa7365"}' + headers: + content-type: application/json + date: Fri, 04 Sep 2020 22:07:41 GMT + location: https://sr-playground.servicebus.windows.net:443/$schemagroups/azsdk_python_test_group/schemas/test-schema-update-async24591503/versions/10?api-version=2017-04 + server: Microsoft-HTTPAPI/2.0 + strict-transport-security: max-age=31536000 + transfer-encoding: chunked + x-schema-id: 767b2ebdbe4e4ac2bfa48f58e0fa7365 + x-schema-id-location: https://sr-playground.servicebus.windows.net:443/$schemagroups/getschemabyid/767b2ebdbe4e4ac2bfa48f58e0fa7365?api-version=2017-04 + x-schema-type: Avro + x-schema-version: '10' + x-schema-versions-location: https://sr-playground.servicebus.windows.net:443/$schemagroups/azsdk_python_test_group/schemas/test-schema-update-async24591503/versions?api-version=2017-04 + status: + code: 200 + message: OK + url: https://sr-playground.servicebus.windows.net/$schemagroups/azsdk_python_test_group/schemas/test-schema-update-async24591503?api-version=2017-04 +- request: + body: null + headers: + Accept: + - application/json + User-Agent: + - azsdk-python-azureschemaregistry/1.0.0b1 Python/3.7.7 (Windows-10-10.0.19041-SP0) + method: GET + uri: https://sr-playground.servicebus.windows.net/$schemagroups/getSchemaById/767b2ebdbe4e4ac2bfa48f58e0fa7365?api-version=2017-04 + response: + body: + string: '"{\"namespace\":\"example.avro\",\"type\":\"record\",\"name\":\"User\",\"fields\":[{\"name\":\"name\",\"type\":\"string\"},{\"name\":\"favorite_number\",\"type\":[\"int\",\"null\"]},{\"name\":\"favorite_food\",\"type\":[\"string\",\"null\"]}]}"' + headers: + content-type: application/json + date: Fri, 04 Sep 2020 22:07:42 GMT + location: https://sr-playground.servicebus.windows.net:443/$schemagroups/azsdk_python_test_group/schemas/test-schema-update-async24591503/versions/10?api-version=2017-04 + server: Microsoft-HTTPAPI/2.0 + strict-transport-security: max-age=31536000 + transfer-encoding: chunked + x-schema-id: 767b2ebdbe4e4ac2bfa48f58e0fa7365 + x-schema-id-location: https://sr-playground.servicebus.windows.net:443/$schemagroups/getschemabyid/767b2ebdbe4e4ac2bfa48f58e0fa7365?api-version=2017-04 + x-schema-type: Avro + x-schema-version: '10' + x-schema-versions-location: https://sr-playground.servicebus.windows.net:443/$schemagroups/azsdk_python_test_group/schemas/test-schema-update-async24591503/versions?api-version=2017-04 + status: + code: 200 + message: OK + url: https://sr-playground.servicebus.windows.net/$schemagroups/getSchemaById/767b2ebdbe4e4ac2bfa48f58e0fa7365?api-version=2017-04 +version: 1 diff --git a/sdk/schemaregistry/azure-schemaregistry/tests/async_tests/test_schema_registry_async.py b/sdk/schemaregistry/azure-schemaregistry/tests/async_tests/test_schema_registry_async.py new file mode 100644 index 000000000000..267549748aae --- /dev/null +++ b/sdk/schemaregistry/azure-schemaregistry/tests/async_tests/test_schema_registry_async.py @@ -0,0 +1,214 @@ +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the ""Software""), to +# deal in the Software without restriction, including without limitation the +# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +# sell copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +# IN THE SOFTWARE. +# +# -------------------------------------------------------------------------- +import pytest +import uuid +import os + +from azure.schemaregistry.aio import SchemaRegistryClient +from azure.identity.aio import ClientSecretCredential +from azure.core.exceptions import ClientAuthenticationError, ServiceRequestError, HttpResponseError + +from schemaregistry_preparer import SchemaRegistryPreparer +from devtools_testutils import AzureTestCase +from devtools_testutils.azure_testcase import _is_autorest_v3 + +from azure.core.credentials import AccessToken + + +class SchemaRegistryAsyncTests(AzureTestCase): + + class AsyncFakeCredential(object): + async def get_token(self, *scopes, **kwargs): + return AccessToken('fake_token', 2527537086) + + async def close(self): + pass + + def create_basic_client(self, client_class, **kwargs): + # This is the patch for creating client using aio identity + + tenant_id = os.environ.get("AZURE_TENANT_ID", None) + client_id = os.environ.get("AZURE_CLIENT_ID", None) + secret = os.environ.get("AZURE_CLIENT_SECRET", None) + + if tenant_id and client_id and secret and self.is_live: + if _is_autorest_v3(client_class): + # Create azure-identity class + from azure.identity.aio import ClientSecretCredential + credentials = ClientSecretCredential( + tenant_id=tenant_id, + client_id=client_id, + client_secret=secret + ) + else: + # Create msrestazure class + from msrestazure.azure_active_directory import ServicePrincipalCredentials + credentials = ServicePrincipalCredentials( + tenant=tenant_id, + client_id=client_id, + secret=secret + ) + else: + if _is_autorest_v3(client_class): + credentials = self.AsyncFakeCredential() + #credentials = self.settings.get_azure_core_credentials() + else: + credentials = self.settings.get_credentials() + + # Real client creation + # FIXME decide what is the final argument for that + # if self.is_playback(): + # kwargs.setdefault("polling_interval", 0) + if _is_autorest_v3(client_class): + kwargs.setdefault("logging_enable", True) + client = client_class( + credential=credentials, + **kwargs + ) + else: + client = client_class( + credentials=credentials, + **kwargs + ) + + if self.is_playback(): + try: + client._config.polling_interval = 0 # FIXME in azure-mgmt-core, make this a kwargs + except AttributeError: + pass + + if hasattr(client, "config"): # Autorest v2 + if self.is_playback(): + client.config.long_running_operation_timeout = 0 + client.config.enable_http_logger = True + return client + + @SchemaRegistryPreparer() + async def test_schema_basic_async(self, schemaregistry_endpoint, schemaregistry_group, **kwargs): + client = self.create_basic_client(SchemaRegistryClient, endpoint=schemaregistry_endpoint) + async with client: + schema_name = self.get_resource_name('test-schema-basic-async') + schema_str = """{"namespace":"example.avro","type":"record","name":"User","fields":[{"name":"name","type":"string"},{"name":"favorite_number","type":["int","null"]},{"name":"favorite_color","type":["string","null"]}]}""" + serialization_type = "Avro" + schema_properties = await client.register_schema(schemaregistry_group, schema_name, serialization_type, schema_str) + + assert schema_properties.schema_id is not None + assert schema_properties.location is not None + assert schema_properties.location_by_id is not None + assert schema_properties.version is 1 + assert schema_properties.serialization_type == "Avro" + + returned_schema = await client.get_schema(schema_id=schema_properties.schema_id) + + assert returned_schema.schema_properties.schema_id == schema_properties.schema_id + assert returned_schema.schema_properties.location is not None + assert returned_schema.schema_properties.location_by_id is not None + assert returned_schema.schema_properties.version == 1 + assert returned_schema.schema_properties.serialization_type == "Avro" + assert returned_schema.schema_content == schema_str + + returned_schema_properties = await client.get_schema_id(schemaregistry_group, schema_name, serialization_type, schema_str) + + assert returned_schema_properties.schema_id == schema_properties.schema_id + assert returned_schema_properties.location is not None + assert returned_schema_properties.location_by_id is not None + assert returned_schema_properties.version == 1 + assert returned_schema_properties.serialization_type == "Avro" + await client._generated_client._config.credential.close() + + @SchemaRegistryPreparer() + async def test_schema_update_async(self, schemaregistry_endpoint, schemaregistry_group, **kwargs): + client = self.create_basic_client(SchemaRegistryClient, endpoint=schemaregistry_endpoint) + async with client: + schema_name = self.get_resource_name('test-schema-update-async') + schema_str = """{"namespace":"example.avro","type":"record","name":"User","fields":[{"name":"name","type":"string"},{"name":"favorite_number","type":["int","null"]},{"name":"favorite_color","type":["string","null"]}]}""" + serialization_type = "Avro" + schema_properties = await client.register_schema(schemaregistry_group, schema_name, serialization_type, schema_str) + + assert schema_properties.schema_id is not None + assert schema_properties.location is not None + assert schema_properties.location_by_id is not None + assert schema_properties.version != 0 + assert schema_properties.serialization_type == "Avro" + + schema_str_new = """{"namespace":"example.avro","type":"record","name":"User","fields":[{"name":"name","type":"string"},{"name":"favorite_number","type":["int","null"]},{"name":"favorite_food","type":["string","null"]}]}""" + new_schema_properties = await client.register_schema(schemaregistry_group, schema_name, serialization_type, schema_str_new) + + assert new_schema_properties.schema_id is not None + assert new_schema_properties.location is not None + assert new_schema_properties.location_by_id is not None + assert new_schema_properties.version == schema_properties.version + 1 + assert new_schema_properties.serialization_type == "Avro" + + new_schema = await client.get_schema(schema_id=new_schema_properties.schema_id) + + assert new_schema.schema_properties.schema_id != schema_properties.schema_id + assert new_schema.schema_properties.schema_id == new_schema_properties.schema_id + assert new_schema.schema_properties.location is not None + assert new_schema.schema_properties.location_by_id is not None + assert new_schema.schema_content == schema_str_new + assert new_schema.schema_properties.version == schema_properties.version + 1 + assert new_schema.schema_properties.serialization_type == "Avro" + await client._generated_client._config.credential.close() + + @SchemaRegistryPreparer() + async def test_schema_same_twice_async(self, schemaregistry_endpoint, schemaregistry_group, **kwargs): + client = self.create_basic_client(SchemaRegistryClient, endpoint=schemaregistry_endpoint) + schema_name = self.get_resource_name('test-schema-twice-async') + schema_str = """{"namespace":"example.avro","type":"record","name":"User","fields":[{"name":"name","type":"string"},{"name":"age","type":["int","null"]},{"name":"city","type":["string","null"]}]}""" + serialization_type = "Avro" + async with client: + schema_properties = await client.register_schema(schemaregistry_group, schema_name, serialization_type, schema_str) + schema_properties_second = await client.register_schema(schemaregistry_group, schema_name, serialization_type, schema_str) + assert schema_properties.schema_id == schema_properties_second.schema_id + await client._generated_client._config.credential.close() + + @SchemaRegistryPreparer() + async def test_schema_negative_wrong_credential_async(self, schemaregistry_endpoint, schemaregistry_group, **kwargs): + credential = ClientSecretCredential(tenant_id="fake", client_id="fake", client_secret="fake") + client = SchemaRegistryClient(endpoint=schemaregistry_endpoint, credential=credential) + async with client, credential: + schema_name = self.get_resource_name('test-schema-negative-async') + schema_str = """{"namespace":"example.avro","type":"record","name":"User","fields":[{"name":"name","type":"string"},{"name":"favorite_number","type":["int","null"]},{"name":"favorite_color","type":["string","null"]}]}""" + serialization_type = "Avro" + with pytest.raises(ClientAuthenticationError): + await client.register_schema(schemaregistry_group, schema_name, serialization_type, schema_str) + + @SchemaRegistryPreparer() + async def test_schema_negative_wrong_endpoint_async(self, schemaregistry_endpoint, schemaregistry_group, **kwargs): + client = self.create_basic_client(SchemaRegistryClient, endpoint="nonexist.servicebus.windows.net") + async with client: + schema_name = self.get_resource_name('test-schema-nonexist-async') + schema_str = """{"namespace":"example.avro","type":"record","name":"User","fields":[{"name":"name","type":"string"},{"name":"favorite_number","type":["int","null"]},{"name":"favorite_color","type":["string","null"]}]}""" + serialization_type = "Avro" + with pytest.raises(ServiceRequestError): + await client.register_schema(schemaregistry_group, schema_name, serialization_type, schema_str) + await client._generated_client._config.credential.close() + + @SchemaRegistryPreparer() + async def test_schema_negative_no_schema_async(self, schemaregistry_endpoint, schemaregistry_group, **kwargs): + client = self.create_basic_client(SchemaRegistryClient, endpoint=schemaregistry_endpoint) + async with client: + with pytest.raises(HttpResponseError): + await client.get_schema('a') + + with pytest.raises(HttpResponseError): + await client.get_schema('a' * 32) + await client._generated_client._config.credential.close() diff --git a/sdk/schemaregistry/azure-schemaregistry/tests/recordings/test_schema_registry.test_schema_basic.yaml b/sdk/schemaregistry/azure-schemaregistry/tests/recordings/test_schema_registry.test_schema_basic.yaml new file mode 100644 index 000000000000..f5a3ae16820b --- /dev/null +++ b/sdk/schemaregistry/azure-schemaregistry/tests/recordings/test_schema_registry.test_schema_basic.yaml @@ -0,0 +1,140 @@ +interactions: +- request: + body: '"{\"namespace\":\"example.avro\",\"type\":\"record\",\"name\":\"User\",\"fields\":[{\"name\":\"name\",\"type\":\"string\"},{\"name\":\"favorite_number\",\"type\":[\"int\",\"null\"]},{\"name\":\"favorite_color\",\"type\":[\"string\",\"null\"]}]}"' + headers: + Accept: + - application/json + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + Content-Length: + - '245' + Content-Type: + - application/json + User-Agent: + - azsdk-python-azureschemaregistry/1.0.0b1 Python/3.7.7 (Windows-10-10.0.19041-SP0) + X-Schema-Type: + - Avro + method: PUT + uri: https://sr-playground.servicebus.windows.net/$schemagroups/azsdk_python_test_group/schemas/test-schema-basic31c70f88?api-version=2017-04 + response: + body: + string: '{"id":"4a24a2d868ff4d42bce7889243ba055c"}' + headers: + content-type: + - application/json + date: + - Fri, 04 Sep 2020 22:02:04 GMT + location: + - https://sr-playground.servicebus.windows.net:443/$schemagroups/azsdk_python_test_group/schemas/test-schema-basic31c70f88/versions/1?api-version=2017-04 + server: + - Microsoft-HTTPAPI/2.0 + strict-transport-security: + - max-age=31536000 + transfer-encoding: + - chunked + x-schema-id: + - 4a24a2d868ff4d42bce7889243ba055c + x-schema-id-location: + - https://sr-playground.servicebus.windows.net:443/$schemagroups/getschemabyid/4a24a2d868ff4d42bce7889243ba055c?api-version=2017-04 + x-schema-type: + - Avro + x-schema-version: + - '1' + x-schema-versions-location: + - https://sr-playground.servicebus.windows.net:443/$schemagroups/azsdk_python_test_group/schemas/test-schema-basic31c70f88/versions?api-version=2017-04 + status: + code: 200 + message: OK +- request: + body: null + headers: + Accept: + - application/json + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + User-Agent: + - azsdk-python-azureschemaregistry/1.0.0b1 Python/3.7.7 (Windows-10-10.0.19041-SP0) + method: GET + uri: https://sr-playground.servicebus.windows.net/$schemagroups/getSchemaById/4a24a2d868ff4d42bce7889243ba055c?api-version=2017-04 + response: + body: + string: '"{\"namespace\":\"example.avro\",\"type\":\"record\",\"name\":\"User\",\"fields\":[{\"name\":\"name\",\"type\":\"string\"},{\"name\":\"favorite_number\",\"type\":[\"int\",\"null\"]},{\"name\":\"favorite_color\",\"type\":[\"string\",\"null\"]}]}"' + headers: + content-type: + - application/json + date: + - Fri, 04 Sep 2020 22:02:06 GMT + location: + - https://sr-playground.servicebus.windows.net:443/$schemagroups/azsdk_python_test_group/schemas/test-schema-basic31c70f88/versions/1?api-version=2017-04 + server: + - Microsoft-HTTPAPI/2.0 + strict-transport-security: + - max-age=31536000 + transfer-encoding: + - chunked + x-schema-id: + - 4a24a2d868ff4d42bce7889243ba055c + x-schema-id-location: + - https://sr-playground.servicebus.windows.net:443/$schemagroups/getschemabyid/4a24a2d868ff4d42bce7889243ba055c?api-version=2017-04 + x-schema-type: + - Avro + x-schema-version: + - '1' + x-schema-versions-location: + - https://sr-playground.servicebus.windows.net:443/$schemagroups/azsdk_python_test_group/schemas/test-schema-basic31c70f88/versions?api-version=2017-04 + status: + code: 200 + message: OK +- request: + body: '"{\"namespace\":\"example.avro\",\"type\":\"record\",\"name\":\"User\",\"fields\":[{\"name\":\"name\",\"type\":\"string\"},{\"name\":\"favorite_number\",\"type\":[\"int\",\"null\"]},{\"name\":\"favorite_color\",\"type\":[\"string\",\"null\"]}]}"' + headers: + Accept: + - application/json + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + Content-Length: + - '245' + Content-Type: + - application/json + User-Agent: + - azsdk-python-azureschemaregistry/1.0.0b1 Python/3.7.7 (Windows-10-10.0.19041-SP0) + X-Schema-Type: + - Avro + method: POST + uri: https://sr-playground.servicebus.windows.net/$schemagroups/azsdk_python_test_group/schemas/test-schema-basic31c70f88?api-version=2017-04 + response: + body: + string: '{"id":"4a24a2d868ff4d42bce7889243ba055c"}' + headers: + content-type: + - application/json + date: + - Fri, 04 Sep 2020 22:02:06 GMT + location: + - https://sr-playground.servicebus.windows.net:443/$schemagroups/azsdk_python_test_group/schemas/test-schema-basic31c70f88/versions/1?api-version=2017-04 + server: + - Microsoft-HTTPAPI/2.0 + strict-transport-security: + - max-age=31536000 + transfer-encoding: + - chunked + x-schema-id: + - 4a24a2d868ff4d42bce7889243ba055c + x-schema-id-location: + - https://sr-playground.servicebus.windows.net:443/$schemagroups/getschemabyid/4a24a2d868ff4d42bce7889243ba055c?api-version=2017-04 + x-schema-type: + - Avro + x-schema-version: + - '1' + x-schema-versions-location: + - https://sr-playground.servicebus.windows.net:443/$schemagroups/azsdk_python_test_group/schemas/test-schema-basic31c70f88/versions?api-version=2017-04 + status: + code: 200 + message: OK +version: 1 diff --git a/sdk/schemaregistry/azure-schemaregistry/tests/recordings/test_schema_registry.test_schema_negative_no_schema.yaml b/sdk/schemaregistry/azure-schemaregistry/tests/recordings/test_schema_registry.test_schema_negative_no_schema.yaml new file mode 100644 index 000000000000..34ab6e84f860 --- /dev/null +++ b/sdk/schemaregistry/azure-schemaregistry/tests/recordings/test_schema_registry.test_schema_negative_no_schema.yaml @@ -0,0 +1,67 @@ +interactions: +- request: + body: null + headers: + Accept: + - application/json + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + User-Agent: + - azsdk-python-azureschemaregistry/1.0.0b1 Python/3.7.7 (Windows-10-10.0.19041-SP0) + method: GET + uri: https://sr-playground.servicebus.windows.net/$schemagroups/getSchemaById/a?api-version=2017-04 + response: + body: + string: 400SubCode=40000, UnknownType:The request + is invalid. [MGResponseHttpError=BadRequest]. TrackingId:8fffa255-f448-4289-9c6b-4fabe270883b_G1, + SystemTracker:sr-playground.servicebus.windows.net:$schemagroups/getSchemaById/a, + Timestamp:2020-09-04T22:02:07 + headers: + content-type: + - application/xml; charset=utf-8 + date: + - Fri, 04 Sep 2020 22:02:07 GMT + server: + - Microsoft-HTTPAPI/2.0 + strict-transport-security: + - max-age=31536000 + transfer-encoding: + - chunked + status: + code: 400 + message: Bad Request +- request: + body: null + headers: + Accept: + - application/json + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + User-Agent: + - azsdk-python-azureschemaregistry/1.0.0b1 Python/3.7.7 (Windows-10-10.0.19041-SP0) + method: GET + uri: https://sr-playground.servicebus.windows.net/$schemagroups/getSchemaById/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa?api-version=2017-04 + response: + body: + string: 404Schema id aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + does not exist. TrackingId:0f6141ae-5a35-4219-a634-fe3533912dcc_G1, SystemTracker:sr-playground.servicebus.windows.net:$schemagroups/getSchemaById/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa, + Timestamp:2020-09-04T22:02:08 + headers: + content-type: + - application/xml; charset=utf-8 + date: + - Fri, 04 Sep 2020 22:02:07 GMT + server: + - Microsoft-HTTPAPI/2.0 + strict-transport-security: + - max-age=31536000 + transfer-encoding: + - chunked + status: + code: 404 + message: Not Found +version: 1 diff --git a/sdk/schemaregistry/azure-schemaregistry/tests/recordings/test_schema_registry.test_schema_same_twice.yaml b/sdk/schemaregistry/azure-schemaregistry/tests/recordings/test_schema_registry.test_schema_same_twice.yaml new file mode 100644 index 000000000000..50bba1757a86 --- /dev/null +++ b/sdk/schemaregistry/azure-schemaregistry/tests/recordings/test_schema_registry.test_schema_same_twice.yaml @@ -0,0 +1,98 @@ +interactions: +- request: + body: '"{\"namespace\":\"example.avro\",\"type\":\"record\",\"name\":\"User\",\"fields\":[{\"name\":\"name\",\"type\":\"string\"},{\"name\":\"age\",\"type\":[\"int\",\"null\"]},{\"name\":\"city\",\"type\":[\"string\",\"null\"]}]}"' + headers: + Accept: + - application/json + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + Content-Length: + - '223' + Content-Type: + - application/json + User-Agent: + - azsdk-python-azureschemaregistry/1.0.0b1 Python/3.7.7 (Windows-10-10.0.19041-SP0) + X-Schema-Type: + - Avro + method: PUT + uri: https://sr-playground.servicebus.windows.net/$schemagroups/azsdk_python_test_group/schemas/test-schema-twice863b11a7?api-version=2017-04 + response: + body: + string: '{"id":"07a69522b495454e80c9dfee0bf43cc3"}' + headers: + content-type: + - application/json + date: + - Fri, 04 Sep 2020 22:02:14 GMT + location: + - https://sr-playground.servicebus.windows.net:443/$schemagroups/azsdk_python_test_group/schemas/test-schema-twice863b11a7/versions/1?api-version=2017-04 + server: + - Microsoft-HTTPAPI/2.0 + strict-transport-security: + - max-age=31536000 + transfer-encoding: + - chunked + x-schema-id: + - 07a69522b495454e80c9dfee0bf43cc3 + x-schema-id-location: + - https://sr-playground.servicebus.windows.net:443/$schemagroups/getschemabyid/07a69522b495454e80c9dfee0bf43cc3?api-version=2017-04 + x-schema-type: + - Avro + x-schema-version: + - '1' + x-schema-versions-location: + - https://sr-playground.servicebus.windows.net:443/$schemagroups/azsdk_python_test_group/schemas/test-schema-twice863b11a7/versions?api-version=2017-04 + status: + code: 200 + message: OK +- request: + body: '"{\"namespace\":\"example.avro\",\"type\":\"record\",\"name\":\"User\",\"fields\":[{\"name\":\"name\",\"type\":\"string\"},{\"name\":\"age\",\"type\":[\"int\",\"null\"]},{\"name\":\"city\",\"type\":[\"string\",\"null\"]}]}"' + headers: + Accept: + - application/json + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + Content-Length: + - '223' + Content-Type: + - application/json + User-Agent: + - azsdk-python-azureschemaregistry/1.0.0b1 Python/3.7.7 (Windows-10-10.0.19041-SP0) + X-Schema-Type: + - Avro + method: PUT + uri: https://sr-playground.servicebus.windows.net/$schemagroups/azsdk_python_test_group/schemas/test-schema-twice863b11a7?api-version=2017-04 + response: + body: + string: '{"id":"07a69522b495454e80c9dfee0bf43cc3"}' + headers: + content-type: + - application/json + date: + - Fri, 04 Sep 2020 22:02:14 GMT + location: + - https://sr-playground.servicebus.windows.net:443/$schemagroups/azsdk_python_test_group/schemas/test-schema-twice863b11a7/versions/1?api-version=2017-04 + server: + - Microsoft-HTTPAPI/2.0 + strict-transport-security: + - max-age=31536000 + transfer-encoding: + - chunked + x-schema-id: + - 07a69522b495454e80c9dfee0bf43cc3 + x-schema-id-location: + - https://sr-playground.servicebus.windows.net:443/$schemagroups/getschemabyid/07a69522b495454e80c9dfee0bf43cc3?api-version=2017-04 + x-schema-type: + - Avro + x-schema-version: + - '1' + x-schema-versions-location: + - https://sr-playground.servicebus.windows.net:443/$schemagroups/azsdk_python_test_group/schemas/test-schema-twice863b11a7/versions?api-version=2017-04 + status: + code: 200 + message: OK +version: 1 diff --git a/sdk/schemaregistry/azure-schemaregistry/tests/recordings/test_schema_registry.test_schema_update.yaml b/sdk/schemaregistry/azure-schemaregistry/tests/recordings/test_schema_registry.test_schema_update.yaml new file mode 100644 index 000000000000..64cf664e72d2 --- /dev/null +++ b/sdk/schemaregistry/azure-schemaregistry/tests/recordings/test_schema_registry.test_schema_update.yaml @@ -0,0 +1,140 @@ +interactions: +- request: + body: '"{\"namespace\":\"example.avro\",\"type\":\"record\",\"name\":\"User\",\"fields\":[{\"name\":\"name\",\"type\":\"string\"},{\"name\":\"favorite_number\",\"type\":[\"int\",\"null\"]},{\"name\":\"favorite_color\",\"type\":[\"string\",\"null\"]}]}"' + headers: + Accept: + - application/json + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + Content-Length: + - '245' + Content-Type: + - application/json + User-Agent: + - azsdk-python-azureschemaregistry/1.0.0b1 Python/3.7.7 (Windows-10-10.0.19041-SP0) + X-Schema-Type: + - Avro + method: PUT + uri: https://sr-playground.servicebus.windows.net/$schemagroups/azsdk_python_test_group/schemas/test-schema-update423f1009?api-version=2017-04 + response: + body: + string: '{"id":"c105c5cd1be740e38c05f35fb630f5ff"}' + headers: + content-type: + - application/json + date: + - Fri, 04 Sep 2020 22:02:16 GMT + location: + - https://sr-playground.servicebus.windows.net:443/$schemagroups/azsdk_python_test_group/schemas/test-schema-update423f1009/versions/5?api-version=2017-04 + server: + - Microsoft-HTTPAPI/2.0 + strict-transport-security: + - max-age=31536000 + transfer-encoding: + - chunked + x-schema-id: + - c105c5cd1be740e38c05f35fb630f5ff + x-schema-id-location: + - https://sr-playground.servicebus.windows.net:443/$schemagroups/getschemabyid/c105c5cd1be740e38c05f35fb630f5ff?api-version=2017-04 + x-schema-type: + - Avro + x-schema-version: + - '5' + x-schema-versions-location: + - https://sr-playground.servicebus.windows.net:443/$schemagroups/azsdk_python_test_group/schemas/test-schema-update423f1009/versions?api-version=2017-04 + status: + code: 200 + message: OK +- request: + body: '"{\"namespace\":\"example.avro\",\"type\":\"record\",\"name\":\"User\",\"fields\":[{\"name\":\"name\",\"type\":\"string\"},{\"name\":\"favorite_number\",\"type\":[\"int\",\"null\"]},{\"name\":\"favorite_food\",\"type\":[\"string\",\"null\"]}]}"' + headers: + Accept: + - application/json + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + Content-Length: + - '244' + Content-Type: + - application/json + User-Agent: + - azsdk-python-azureschemaregistry/1.0.0b1 Python/3.7.7 (Windows-10-10.0.19041-SP0) + X-Schema-Type: + - Avro + method: PUT + uri: https://sr-playground.servicebus.windows.net/$schemagroups/azsdk_python_test_group/schemas/test-schema-update423f1009?api-version=2017-04 + response: + body: + string: '{"id":"9f19901a6d2d4fd0bc79549d3a908764"}' + headers: + content-type: + - application/json + date: + - Fri, 04 Sep 2020 22:02:16 GMT + location: + - https://sr-playground.servicebus.windows.net:443/$schemagroups/azsdk_python_test_group/schemas/test-schema-update423f1009/versions/6?api-version=2017-04 + server: + - Microsoft-HTTPAPI/2.0 + strict-transport-security: + - max-age=31536000 + transfer-encoding: + - chunked + x-schema-id: + - 9f19901a6d2d4fd0bc79549d3a908764 + x-schema-id-location: + - https://sr-playground.servicebus.windows.net:443/$schemagroups/getschemabyid/9f19901a6d2d4fd0bc79549d3a908764?api-version=2017-04 + x-schema-type: + - Avro + x-schema-version: + - '6' + x-schema-versions-location: + - https://sr-playground.servicebus.windows.net:443/$schemagroups/azsdk_python_test_group/schemas/test-schema-update423f1009/versions?api-version=2017-04 + status: + code: 200 + message: OK +- request: + body: null + headers: + Accept: + - application/json + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + User-Agent: + - azsdk-python-azureschemaregistry/1.0.0b1 Python/3.7.7 (Windows-10-10.0.19041-SP0) + method: GET + uri: https://sr-playground.servicebus.windows.net/$schemagroups/getSchemaById/9f19901a6d2d4fd0bc79549d3a908764?api-version=2017-04 + response: + body: + string: '"{\"namespace\":\"example.avro\",\"type\":\"record\",\"name\":\"User\",\"fields\":[{\"name\":\"name\",\"type\":\"string\"},{\"name\":\"favorite_number\",\"type\":[\"int\",\"null\"]},{\"name\":\"favorite_food\",\"type\":[\"string\",\"null\"]}]}"' + headers: + content-type: + - application/json + date: + - Fri, 04 Sep 2020 22:02:17 GMT + location: + - https://sr-playground.servicebus.windows.net:443/$schemagroups/azsdk_python_test_group/schemas/test-schema-update423f1009/versions/6?api-version=2017-04 + server: + - Microsoft-HTTPAPI/2.0 + strict-transport-security: + - max-age=31536000 + transfer-encoding: + - chunked + x-schema-id: + - 9f19901a6d2d4fd0bc79549d3a908764 + x-schema-id-location: + - https://sr-playground.servicebus.windows.net:443/$schemagroups/getschemabyid/9f19901a6d2d4fd0bc79549d3a908764?api-version=2017-04 + x-schema-type: + - Avro + x-schema-version: + - '6' + x-schema-versions-location: + - https://sr-playground.servicebus.windows.net:443/$schemagroups/azsdk_python_test_group/schemas/test-schema-update423f1009/versions?api-version=2017-04 + status: + code: 200 + message: OK +version: 1 diff --git a/sdk/schemaregistry/azure-schemaregistry/tests/schemaregistry_preparer.py b/sdk/schemaregistry/azure-schemaregistry/tests/schemaregistry_preparer.py new file mode 100644 index 000000000000..511e2b8c147c --- /dev/null +++ b/sdk/schemaregistry/azure-schemaregistry/tests/schemaregistry_preparer.py @@ -0,0 +1,72 @@ +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the ""Software""), to +# deal in the Software without restriction, including without limitation the +# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +# sell copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +# IN THE SOFTWARE. +# +# -------------------------------------------------------------------------- + +import functools +import hashlib +import os +from collections import namedtuple + +from azure.identity import ClientSecretCredential +from azure_devtools.scenario_tests.exceptions import AzureTestError +from devtools_testutils import ( + ResourceGroupPreparer, AzureMgmtPreparer, FakeResource +) + + +SCHEMA_REGISTRY_ENDPOINT_PARAM = "schemaregistry_endpoint" +SCHEMA_REGISTRY_GROUP_PARAM = "schemaregistry_group" +SCHEMA_REGISTRY_ENDPOINT_ENV_KEY_NAME = 'SCHEMA_REGISTRY_ENDPOINT' +SCHEMA_REGISTRY_GROUP_ENV_KEY_NAME = 'SCHEMA_REGISTRY_GROUP' + + +class SchemaRegistryNamespacePreparer(AzureMgmtPreparer): + # TODO: SR doesn't have mgmt package + def __init__(self): + pass + + def create_resource(self, name, **kwargs): + pass + + def remove_resource(self, name, **kwargs): + pass + + +class SchemaRegistryPreparer(AzureMgmtPreparer): + def __init__( + self, + name_prefix='' + ): + super(SchemaRegistryPreparer, self).__init__(name_prefix, 24) + + def create_resource(self, name, **kwargs): + # TODO: right now the endpoint/group is fixed, as there is no way to create/delete resources using api, in the future we should be able to dynamically create and remove resources + if self.is_live: + return { + SCHEMA_REGISTRY_ENDPOINT_PARAM: os.environ[SCHEMA_REGISTRY_ENDPOINT_ENV_KEY_NAME], + SCHEMA_REGISTRY_GROUP_PARAM: os.environ[SCHEMA_REGISTRY_GROUP_ENV_KEY_NAME] + } + else: + return { + SCHEMA_REGISTRY_ENDPOINT_PARAM: "sr-playground.servicebus.windows.net", + SCHEMA_REGISTRY_GROUP_PARAM: "azsdk_python_test_group" + } + + def remove_resource(self, name, **kwargs): + pass diff --git a/sdk/schemaregistry/azure-schemaregistry/tests/test_placeholder.py b/sdk/schemaregistry/azure-schemaregistry/tests/test_placeholder.py deleted file mode 100644 index ace0d42403eb..000000000000 --- a/sdk/schemaregistry/azure-schemaregistry/tests/test_placeholder.py +++ /dev/null @@ -1,2 +0,0 @@ -def test_placeholder(): - assert 1 == 1 diff --git a/sdk/schemaregistry/azure-schemaregistry/tests/test_schema_registry.py b/sdk/schemaregistry/azure-schemaregistry/tests/test_schema_registry.py new file mode 100644 index 000000000000..29b490a7577b --- /dev/null +++ b/sdk/schemaregistry/azure-schemaregistry/tests/test_schema_registry.py @@ -0,0 +1,133 @@ +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the ""Software""), to +# deal in the Software without restriction, including without limitation the +# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +# sell copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +# IN THE SOFTWARE. +# +# -------------------------------------------------------------------------- +import pytest +import uuid + +from azure.schemaregistry import SchemaRegistryClient +from azure.identity import ClientSecretCredential +from azure.core.exceptions import ClientAuthenticationError, ServiceRequestError, HttpResponseError + +from schemaregistry_preparer import SchemaRegistryPreparer +from devtools_testutils import AzureTestCase + + +class SchemaRegistryTests(AzureTestCase): + + @SchemaRegistryPreparer() + def test_schema_basic(self, schemaregistry_endpoint, schemaregistry_group, **kwargs): + client = self.create_basic_client(SchemaRegistryClient, endpoint=schemaregistry_endpoint) + schema_name = self.get_resource_name('test-schema-basic') + schema_str = """{"namespace":"example.avro","type":"record","name":"User","fields":[{"name":"name","type":"string"},{"name":"favorite_number","type":["int","null"]},{"name":"favorite_color","type":["string","null"]}]}""" + serialization_type = "Avro" + schema_properties = client.register_schema(schemaregistry_group, schema_name, serialization_type, schema_str) + + assert schema_properties.schema_id is not None + assert schema_properties.location is not None + assert schema_properties.location_by_id is not None + assert schema_properties.version is 1 + assert schema_properties.serialization_type == "Avro" + + returned_schema = client.get_schema(schema_id=schema_properties.schema_id) + + assert returned_schema.schema_properties.schema_id == schema_properties.schema_id + assert returned_schema.schema_properties.location is not None + assert returned_schema.schema_properties.location_by_id is not None + assert returned_schema.schema_properties.version == 1 + assert returned_schema.schema_properties.serialization_type == "Avro" + assert returned_schema.schema_content == schema_str + + returned_schema_properties = client.get_schema_id(schemaregistry_group, schema_name, serialization_type, schema_str) + + assert returned_schema_properties.schema_id == schema_properties.schema_id + assert returned_schema_properties.location is not None + assert returned_schema_properties.location_by_id is not None + assert returned_schema_properties.version == 1 + assert returned_schema_properties.serialization_type == "Avro" + + @SchemaRegistryPreparer() + def test_schema_update(self, schemaregistry_endpoint, schemaregistry_group, **kwargs): + client = self.create_basic_client(SchemaRegistryClient, endpoint=schemaregistry_endpoint) + schema_name = self.get_resource_name('test-schema-update') + schema_str = """{"namespace":"example.avro","type":"record","name":"User","fields":[{"name":"name","type":"string"},{"name":"favorite_number","type":["int","null"]},{"name":"favorite_color","type":["string","null"]}]}""" + serialization_type = "Avro" + schema_properties = client.register_schema(schemaregistry_group, schema_name, serialization_type, schema_str) + + assert schema_properties.schema_id is not None + assert schema_properties.location is not None + assert schema_properties.location_by_id is not None + assert schema_properties.version != 0 + assert schema_properties.serialization_type == "Avro" + + schema_str_new = """{"namespace":"example.avro","type":"record","name":"User","fields":[{"name":"name","type":"string"},{"name":"favorite_number","type":["int","null"]},{"name":"favorite_food","type":["string","null"]}]}""" + new_schema_properties = client.register_schema(schemaregistry_group, schema_name, serialization_type, schema_str_new) + + assert new_schema_properties.schema_id is not None + assert new_schema_properties.location is not None + assert new_schema_properties.location_by_id is not None + assert new_schema_properties.version == schema_properties.version + 1 + assert new_schema_properties.serialization_type == "Avro" + + new_schema = client.get_schema(schema_id=new_schema_properties.schema_id) + + assert new_schema.schema_properties.schema_id != schema_properties.schema_id + assert new_schema.schema_properties.schema_id == new_schema_properties.schema_id + assert new_schema.schema_properties.location is not None + assert new_schema.schema_properties.location_by_id is not None + assert new_schema.schema_content == schema_str_new + assert new_schema.schema_properties.version == schema_properties.version + 1 + assert new_schema.schema_properties.serialization_type == "Avro" + + @SchemaRegistryPreparer() + def test_schema_same_twice(self, schemaregistry_endpoint, schemaregistry_group, **kwargs): + client = self.create_basic_client(SchemaRegistryClient, endpoint=schemaregistry_endpoint) + schema_name = self.get_resource_name('test-schema-twice') + schema_str = """{"namespace":"example.avro","type":"record","name":"User","fields":[{"name":"name","type":"string"},{"name":"age","type":["int","null"]},{"name":"city","type":["string","null"]}]}""" + serialization_type = "Avro" + schema_properties = client.register_schema(schemaregistry_group, schema_name, serialization_type, schema_str) + schema_properties_second = client.register_schema(schemaregistry_group, schema_name, serialization_type, schema_str) + assert schema_properties.schema_id == schema_properties_second.schema_id + + @SchemaRegistryPreparer() + def test_schema_negative_wrong_credential(self, schemaregistry_endpoint, schemaregistry_group, **kwargs): + credential = ClientSecretCredential(tenant_id="fake", client_id="fake", client_secret="fake") + client = SchemaRegistryClient(endpoint=schemaregistry_endpoint, credential=credential) + schema_name = self.get_resource_name('test-schema-negative') + schema_str = """{"namespace":"example.avro","type":"record","name":"User","fields":[{"name":"name","type":"string"},{"name":"favorite_number","type":["int","null"]},{"name":"favorite_color","type":["string","null"]}]}""" + serialization_type = "Avro" + with pytest.raises(ClientAuthenticationError): + client.register_schema(schemaregistry_group, schema_name, serialization_type, schema_str) + + @SchemaRegistryPreparer() + def test_schema_negative_wrong_endpoint(self, schemaregistry_endpoint, schemaregistry_group, **kwargs): + client = self.create_basic_client(SchemaRegistryClient, endpoint="nonexist.servicebus.windows.net") + schema_name = self.get_resource_name('test-schema-nonexist') + schema_str = """{"namespace":"example.avro","type":"record","name":"User","fields":[{"name":"name","type":"string"},{"name":"favorite_number","type":["int","null"]},{"name":"favorite_color","type":["string","null"]}]}""" + serialization_type = "Avro" + with pytest.raises(ServiceRequestError): + client.register_schema(schemaregistry_group, schema_name, serialization_type, schema_str) + + @SchemaRegistryPreparer() + def test_schema_negative_no_schema(self, schemaregistry_endpoint, schemaregistry_group, **kwargs): + client = self.create_basic_client(SchemaRegistryClient, endpoint=schemaregistry_endpoint) + with pytest.raises(HttpResponseError): + client.get_schema('a') + + with pytest.raises(HttpResponseError): + client.get_schema('a' * 32) diff --git a/sdk/schemaregistry/tests.yml b/sdk/schemaregistry/tests.yml index a800f04f3613..3dc85ec512a9 100644 --- a/sdk/schemaregistry/tests.yml +++ b/sdk/schemaregistry/tests.yml @@ -8,9 +8,11 @@ jobs: BuildTargetingString: azure-schemaregistry* EnvVars: AZURE_SUBSCRIPTION_ID: $(azure-subscription-id) - AZURE_TENANT_ID: $(aad-azure-sdk-test-tenant-id) - AZURE_CLIENT_ID: $(aad-azure-sdk-test-client-id) - AZURE_CLIENT_SECRET: $(aad-azure-sdk-test-client-secret) + AZURE_TENANT_ID: $(python-schema-registry-sdk-test-tenant-id) + AZURE_CLIENT_ID: $(python-schema-registry-sdk-test-client-id) + AZURE_CLIENT_SECRET: $(python-schema-registry-sdk-test-client-secret) + SCHEMA_REGISTRY_ENDPOINT: $(python-schema-registry-sdk-test-endpoint) + SCHEMA_REGISTRY_GROUP: $(python-schema-registry-sdk-test-group) AZURE_TEST_RUN_LIVE: 'true' Matrix: Linux_Python35: diff --git a/shared_requirements.txt b/shared_requirements.txt index cbd26339163f..2e397e351331 100644 --- a/shared_requirements.txt +++ b/shared_requirements.txt @@ -83,6 +83,7 @@ azure-mgmt-trafficmanager~=0.50.0 azure-mgmt-web~=0.35.0 azure-nspkg azure-keyvault-nspkg +azure-schemaregistry<2.0.0,>=1.0.0b1 azure-search-nspkg azure-security-nspkg azure-synapse-nspkg @@ -112,6 +113,7 @@ aiodns>=2.0 python-dateutil>=2.8.0 six>=1.6 isodate>=0.6.0 +avro<2.0.0,>=1.10.0 #override azure azure-keyvault~=1.0 #override azure-mgmt-core azure-core<2.0.0,>=1.7.0 #override azure-appconfiguration azure-core<2.0.0,>=1.0.0