Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion eng/tox/allowed_pylint_failures.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,6 @@
"azure-common",
"azure-nspkg",
"azure-servicemanagement-legacy",
"azure-eventgrid",
"azure-graphrbac",
"azure-loganalytics",
"azure-servicefabric",
Expand Down
2 changes: 1 addition & 1 deletion sdk/eventgrid/azure-eventgrid/azure/__init__.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
__path__ = __import__('pkgutil').extend_path(__path__, __name__)
__path__ = __import__('pkgutil').extend_path(__path__, __name__)
6 changes: 3 additions & 3 deletions sdk/eventgrid/azure-eventgrid/azure/eventgrid/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,11 @@
from ._publisher_client import EventGridPublisherClient
from ._consumer import EventGridConsumer
from ._helpers import generate_shared_access_signature
from ._models import CloudEvent, CustomEvent, EventGridEvent, StorageBlobCreatedEventData
from ._models import CloudEvent, CustomEvent, EventGridEvent
from ._shared_access_signature_credential import EventGridSharedAccessSignatureCredential
from ._version import VERSION

__all__ = ['EventGridPublisherClient', 'EventGridConsumer',
'CloudEvent', 'CustomEvent', 'EventGridEvent', 'StorageBlobCreatedEventData',
'generate_shared_access_signature', 'EventGridSharedAccessSignatureCredential']
'CloudEvent', 'CustomEvent', 'EventGridEvent', 'generate_shared_access_signature',
'EventGridSharedAccessSignatureCredential']
__version__ = VERSION
43 changes: 21 additions & 22 deletions sdk/eventgrid/azure-eventgrid/azure/eventgrid/_consumer.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,26 +7,21 @@
# --------------------------------------------------------------------------

from typing import TYPE_CHECKING
import json
import six
import logging

from azure.core import PipelineClient
from msrest import Deserializer, Serializer
from ._models import CloudEvent, EventGridEvent

if TYPE_CHECKING:
# pylint: disable=unused-import,ungrouped-imports
from typing import Any

_LOGGER = logging.getLogger(__name__)

from ._models import CloudEvent, EventGridEvent

class EventGridConsumer(object):
"""
A consumer responsible for deserializing event handler messages, to allow for access to strongly typed Event objects.
A consumer responsible for deserializing event handler messages, to allow for
access to strongly typed Event objects.
"""
def decode_cloud_event(self, cloud_event, **kwargs):
def decode_cloud_event(self, cloud_event, **kwargs): # pylint: disable=no-self-use
# type: (Union[str, dict, bytes], Any) -> CloudEvent
"""Single event following CloudEvent schema will be parsed and returned as Deserialized Event.
:param cloud_event: The event to be deserialized.
Expand All @@ -37,17 +32,19 @@ def decode_cloud_event(self, cloud_event, **kwargs):
"""
encode = kwargs.pop('encoding', 'utf-8')
try:
cloud_event = CloudEvent._from_json(cloud_event, encode)
deserialized_event = CloudEvent._from_generated(cloud_event)
CloudEvent._deserialize_data(deserialized_event, deserialized_event.type)
return deserialized_event
cloud_event = CloudEvent._from_json(cloud_event, encode) # pylint: disable=protected-access
deserialized_event = CloudEvent._from_generated(cloud_event) # pylint: disable=protected-access
CloudEvent._deserialize_data(deserialized_event, deserialized_event.type) # pylint: disable=protected-access
return deserialized_event
except Exception as err:
_LOGGER.error('Error: cannot deserialize event. Event does not have a valid format. Event must be a string, dict, or bytes following the CloudEvent schema.')
_LOGGER.error('Your event: {}'.format(cloud_event))
_LOGGER.error('Error: cannot deserialize event. Event does not have a valid format. \
Event must be a string, dict, or bytes following the CloudEvent schema.')
_LOGGER.error('Your event: %s', cloud_event)
_LOGGER.error(err)
raise ValueError('Error: cannot deserialize event. Event does not have a valid format. Event must be a string, dict, or bytes following the CloudEvent schema.')
raise ValueError('Error: cannot deserialize event. Event does not have a valid format. \
Event must be a string, dict, or bytes following the CloudEvent schema.')

def decode_eventgrid_event(self, eventgrid_event, **kwargs):
def decode_eventgrid_event(self, eventgrid_event, **kwargs): # pylint: disable=no-self-use
# type: (Union[str, dict, bytes], Any) -> EventGridEvent
"""Single event following EventGridEvent schema will be parsed and returned as Deserialized Event.
:param eventgrid_event: The event to be deserialized.
Expand All @@ -58,12 +55,14 @@ def decode_eventgrid_event(self, eventgrid_event, **kwargs):
"""
encode = kwargs.pop('encoding', 'utf-8')
try:
eventgrid_event = EventGridEvent._from_json(eventgrid_event, encode)
eventgrid_event = EventGridEvent._from_json(eventgrid_event, encode) # pylint: disable=protected-access
deserialized_event = EventGridEvent.deserialize(eventgrid_event)
EventGridEvent._deserialize_data(deserialized_event, deserialized_event.event_type)
EventGridEvent._deserialize_data(deserialized_event, deserialized_event.event_type) # pylint: disable=protected-access
return deserialized_event
except Exception as err:
_LOGGER.error('Error: cannot deserialize event. Event does not have a valid format. Event must be a string, dict, or bytes following the CloudEvent schema.')
_LOGGER.error('Your event: {}'.format(eventgrid_event))
_LOGGER.error('Error: cannot deserialize event. Event does not have a valid format. \
Event must be a string, dict, or bytes following the CloudEvent schema.')
_LOGGER.error('Your event: %s', eventgrid_event)
_LOGGER.error(err)
raise ValueError('Error: cannot deserialize event. Event does not have a valid format. Event must be a string, dict, or bytes following the CloudEvent schema.')
raise ValueError('Error: cannot deserialize event. Event does not have a valid format. \
Event must be a string, dict, or bytes following the CloudEvent schema.')
14 changes: 9 additions & 5 deletions sdk/eventgrid/azure-eventgrid/azure/eventgrid/_event_mappings.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@
"Microsoft.EventGrid.SubscriptionValidationEvent": models.SubscriptionValidationEventData,
"Microsoft.EventGrid.SubscriptionDeletedEvent": models.SubscriptionDeletedEventData,
"Microsoft.EventHub.CaptureFileCreated": models.EventHubCaptureFileCreatedEventData,
"Microsoft.MachineLearningServices.DatasetDriftDetected": models.MachineLearningServicesDatasetDriftDetectedEventData,
"Microsoft.MachineLearningServices.DatasetDriftDetected":
models.MachineLearningServicesDatasetDriftDetectedEventData,
"Microsoft.MachineLearningServices.ModelDeployed": models.MachineLearningServicesModelDeployedEventData,
"Microsoft.MachineLearningServices.ModelRegistered": models.MachineLearningServicesModelRegisteredEventData,
"Microsoft.MachineLearningServices.RunCompleted": models.MachineLearningServicesRunCompletedEventData,
Expand Down Expand Up @@ -47,7 +48,8 @@
"Microsoft.Media.LiveEventEncoderDisconnected": models.MediaLiveEventEncoderDisconnectedEventData,
"Microsoft.Media.LiveEventIncomingStreamReceived": models.MediaLiveEventIncomingStreamReceivedEventData,
"Microsoft.Media.LiveEventIncomingStreamsOutOfSync": models.MediaLiveEventIncomingStreamsOutOfSyncEventData,
"Microsoft.Media.LiveEventIncomingVideoStreamsOutOfSync": models.MediaLiveEventIncomingVideoStreamsOutOfSyncEventData,
"Microsoft.Media.LiveEventIncomingVideoStreamsOutOfSync":
models.MediaLiveEventIncomingVideoStreamsOutOfSyncEventData,
"Microsoft.Media.LiveEventIncomingDataChunkDropped": models.MediaLiveEventIncomingDataChunkDroppedEventData,
"Microsoft.Media.LiveEventIngestHeartbeat": models.MediaLiveEventIngestHeartbeatEventData,
"Microsoft.Media.LiveEventTrackDiscontinuityDetected": models.MediaLiveEventTrackDiscontinuityDetectedEventData,
Expand All @@ -60,8 +62,10 @@
"Microsoft.Resources.ResourceActionSuccess": models.ResourceActionSuccessData,
"Microsoft.Resources.ResourceActionFailure": models.ResourceActionFailureData,
"Microsoft.Resources.ResourceActionCancel": models.ResourceActionCancelData,
"Microsoft.ServiceBus.ActiveMessagesAvailableWithNoListeners": models.ServiceBusActiveMessagesAvailableWithNoListenersEventData,
"Microsoft.ServiceBus.DeadletterMessagesAvailableWithNoListener": models.ServiceBusDeadletterMessagesAvailableWithNoListenersEventData,
"Microsoft.ServiceBus.ActiveMessagesAvailableWithNoListeners":
models.ServiceBusActiveMessagesAvailableWithNoListenersEventData,
"Microsoft.ServiceBus.DeadletterMessagesAvailableWithNoListener":
models.ServiceBusDeadletterMessagesAvailableWithNoListenersEventData,
"Microsoft.Storage.BlobCreated": models.StorageBlobCreatedEventData,
"Microsoft.Storage.BlobDeleted": models.StorageBlobDeletedEventData,
"Microsoft.Storage.BlobRenamed": models.StorageBlobRenamedEventData,
Expand All @@ -82,4 +86,4 @@
"Microsoft.Web.SlotSwapWithPreviewStarted": models.WebSlotSwapWithPreviewStartedEventData,
"Microsoft.Web.SlotSwapWithPreviewCancelled": models.WebSlotSwapWithPreviewCancelledEventData,
"Microsoft.Web.AppServicePlanUpdated": models.WebAppServicePlanUpdatedEventData,
}
}
23 changes: 16 additions & 7 deletions sdk/eventgrid/azure-eventgrid/azure/eventgrid/_helpers.py
Original file line number Diff line number Diff line change
@@ -1,18 +1,20 @@
# --------------------------------------------------------------------------------------------
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT License. See License.txt in the project root for license information.
# --------------------------------------------------------------------------------------------
import hashlib
import hmac
import base64
try:
from urllib.parse import quote
except ImportError:
from urllib2 import quote # type: ignore
import datetime

from azure.core.pipeline.policies import AzureKeyCredentialPolicy
from azure.core.credentials import AzureKeyCredential
from ._shared_access_signature_credential import EventGridSharedAccessSignatureCredential
from ._signature_credential_policy import EventGridSharedAccessSignatureCredentialPolicy
from . import _constants as constants
from ._event_mappings import _event_mappings

def generate_shared_access_signature(topic_hostname, shared_access_key, expiration_date_utc, **kwargs):
# type: (str, str, datetime.Datetime, Any) -> str
Expand All @@ -21,13 +23,17 @@ def generate_shared_access_signature(topic_hostname, shared_access_key, expirati
Similar to <YOUR-TOPIC-NAME>.<YOUR-REGION-NAME>-1.eventgrid.azure.net
:param str shared_access_key: The shared access key to be used for generating the token
:param datetime.datetime expiration_date_utc: The expiration datetime in UTC for the signature.
:param str api_version: The API Version to include in the signature. If not provided, the default API version will be used.
:param str api_version: The API Version to include in the signature.
If not provided, the default API version will be used.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Need a space to keep it part of the param doc

:rtype: str
"""

full_topic_hostname = _get_full_topic_hostname(topic_hostname)

full_topic_hostname = "{}?apiVersion={}".format(full_topic_hostname, kwargs.get('api_version', None) or constants.DEFAULT_API_VERSION)
full_topic_hostname = "{}?apiVersion={}".format(
full_topic_hostname,
kwargs.get('api_version', None) or constants.DEFAULT_API_VERSION
)
encoded_resource = quote(full_topic_hostname, safe=constants.SAFE_ENCODE)
encoded_expiration_utc = quote(str(expiration_date_utc), safe=constants.SAFE_ENCODE)

Expand All @@ -43,7 +49,7 @@ def _get_topic_hostname_only_fqdn(topic_hostname):
topic_hostname = topic_hostname.replace("https://", "")
if topic_hostname.endswith("/api/events"):
topic_hostname = topic_hostname.replace("/api/events", "")

return topic_hostname

def _get_full_topic_hostname(topic_hostname):
Expand All @@ -53,7 +59,7 @@ def _get_full_topic_hostname(topic_hostname):
topic_hostname = "https://{}".format(topic_hostname)
if not topic_hostname.endswith("/api/events"):
topic_hostname = "{}/api/events".format(topic_hostname)

return topic_hostname

def _generate_hmac(key, message):
Expand All @@ -69,7 +75,10 @@ def _get_authentication_policy(credential):
if isinstance(credential, AzureKeyCredential):
authentication_policy = AzureKeyCredentialPolicy(credential=credential, name=constants.EVENTGRID_KEY_HEADER)
if isinstance(credential, EventGridSharedAccessSignatureCredential):
authentication_policy = EventGridSharedAccessSignatureCredentialPolicy(credential=credential, name=constants.EVENTGRID_TOKEN_HEADER)
authentication_policy = EventGridSharedAccessSignatureCredentialPolicy(
credential=credential,
name=constants.EVENTGRID_TOKEN_HEADER
)
return authentication_policy

def _is_cloud_event(event):
Expand Down
21 changes: 9 additions & 12 deletions sdk/eventgrid/azure-eventgrid/azure/eventgrid/_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,12 @@
# Licensed under the MIT License. See License.txt in the project root for license information.
# --------------------------------------------------------------------------------------------
# pylint:disable=protected-access
from typing import Optional
from msrest.serialization import UTC
import datetime as dt
import uuid
import json
import six
from ._generated import models
from ._generated.models import StorageBlobCreatedEventData, \
EventGridEvent as InternalEventGridEvent, \
CloudEvent as InternalCloudEvent
from msrest.serialization import UTC
from ._generated.models import EventGridEvent as InternalEventGridEvent, CloudEvent as InternalCloudEvent
from ._shared.mixins import DictMixin
from ._event_mappings import _event_mappings

Expand Down Expand Up @@ -55,8 +51,8 @@ class CloudEvent(EventMixin): #pylint:disable=too-many-instance-attributes

All required parameters must be populated in order to send to Azure.

:param source: Required. Identifies the context in which an event happened. The combination of id and source must be
unique for each distinct event. If publishing to a domain topic, source must be the domain name.
:param source: Required. Identifies the context in which an event happened. The combination of id and source must
be unique for each distinct event. If publishing to a domain topic, source must be the domain name.
:type source: str
:param data: Event data specific to the event type.
:type data: object
Expand All @@ -75,7 +71,7 @@ class CloudEvent(EventMixin): #pylint:disable=too-many-instance-attributes
unique for each distinct event.
:type id: Optional[str]
"""
def __init__(self, source, type, **kwargs):
def __init__(self, source, type, **kwargs): # pylint: disable=redefined-builtin
# type: (str, str, Any) -> None
self.source = source
self.type = type
Expand All @@ -87,13 +83,13 @@ def __init__(self, source, type, **kwargs):
self.dataschema = kwargs.pop("dataschema", None)
self.subject = kwargs.pop("subject", None)
self.extensions = {}
self.extensions.update({k:v for k, v in kwargs.pop('extensions', {}).items()})
self.extensions.update(dict(kwargs.pop('extensions', {})))

@classmethod
def _from_generated(cls, cloud_event, **kwargs):
generated = InternalCloudEvent.deserialize(cloud_event)
if generated.additional_properties:
extensions = {k:v for k, v in generated.additional_properties.items()}
extensions = dict(generated.additional_properties)
kwargs.setdefault('extensions', extensions)
return cls(
id=generated.id,
Expand Down Expand Up @@ -154,7 +150,8 @@ class EventGridEvent(InternalEventGridEvent, EventMixin):
:param id: Optional. An identifier for the event. The combination of id and source must be
unique for each distinct event.
:type id: Optional[str]
:param event_time: Optional.The time (in UTC) of the event. If not provided, it will be the time (in UTC) the event was generated.
:param event_time: Optional.The time (in UTC) of the event. If not provided,
it will be the time (in UTC) the event was generated.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

need a space here

:type event_time: Optional[~datetime.datetime]
"""

Expand Down
26 changes: 13 additions & 13 deletions sdk/eventgrid/azure-eventgrid/azure/eventgrid/_publisher_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,11 @@
# license information.
# --------------------------------------------------------------------------

from typing import TYPE_CHECKING, Sequence
import json
from typing import TYPE_CHECKING

from azure.core import PipelineClient
from msrest import Deserializer, Serializer
from ._models import CloudEvent, EventGridEvent, CustomEvent
from ._helpers import _get_topic_hostname_only_fqdn, _get_authentication_policy, _is_cloud_event
from ._generated._event_grid_publisher_client import EventGridPublisherClient as EventGridPublisherClientImpl

if TYPE_CHECKING:
# pylint: disable=unused-import,ungrouped-imports
Expand All @@ -25,18 +25,17 @@
List[Dict]
]

from ._models import CloudEvent, EventGridEvent, CustomEvent
from ._helpers import _get_topic_hostname_only_fqdn, _get_authentication_policy, _is_cloud_event
from ._generated._event_grid_publisher_client import EventGridPublisherClient as EventGridPublisherClientImpl
from . import _constants as constants


class EventGridPublisherClient(object):
"""EventGrid Python Publisher Client.

:param str topic_hostname: The topic endpoint to send the events to.
:param credential: The credential object used for authentication which implements SAS key authentication or SAS token authentication.
:type credential: Union[~azure.core.credentials.AzureKeyCredential, azure.eventgrid.EventGridSharedAccessSignatureCredential]
:param credential: The credential object used for authentication which
implements SAS key authentication or SAS token authentication.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

need a space here

:type credential: Union[
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not the docstring syntax:

:type credential: ~azure.core.credentials.AzureKeyCredential or ~azure.eventgrid.EventGridSharedAccessSignatureCredential

~azure.core.credentials.AzureKeyCredential,
azure.eventgrid.EventGridSharedAccessSignatureCredential
]
"""

def __init__(self, topic_hostname, credential, **kwargs):
Expand All @@ -54,7 +53,8 @@ def send(self, events, **kwargs):
:param events: A list or an instance of CloudEvent/EventGridEvent/CustomEvent to be sent.
:type events: SendType
:keyword str content_type: The type of content to be used to send the events.
Has default value "application/json; charset=utf-8" for EventGridEvents, with "cloudevents-batch+json" for CloudEvents
Has default value "application/json; charset=utf-8" for EventGridEvents,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

space

with "cloudevents-batch+json" for CloudEvents
:rtype: None
:raises: :class:`ValueError`, when events do not follow specified SendType.
"""
Expand All @@ -63,7 +63,7 @@ def send(self, events, **kwargs):

if all(isinstance(e, CloudEvent) for e in events) or all(_is_cloud_event(e) for e in events):
try:
events = [e._to_generated(**kwargs) for e in events]
events = [e._to_generated(**kwargs) for e in events] # pylint: disable=protected-access
except AttributeError:
pass # means it's a dictionary
kwargs.setdefault("content_type", "application/cloudevents-batch+json; charset=utf-8")
Expand Down
10 changes: 5 additions & 5 deletions sdk/eventgrid/azure-eventgrid/azure/eventgrid/_shared/mixins.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,22 +37,22 @@ def __ne__(self, other):
def __str__(self):
return str({k: v for k, v in self.__dict__.items() if not k.startswith('_')})

def has_key(self, k, **kwargs):
def has_key(self, k):
return k in self.__dict__

def update(self, *args, **kwargs):
return self.__dict__.update(*args, **kwargs)

def keys(self, **kwargs):
def keys(self):
return [k for k in self.__dict__ if not k.startswith('_')]

def values(self, **kwargs):
def values(self):
return [v for k, v in self.__dict__.items() if not k.startswith('_')]

def items(self, **kwargs):
def items(self):
return [(k, v) for k, v in self.__dict__.items() if not k.startswith('_')]

def get(self, key, default=None, **kwargs):
def get(self, key, default=None):
if key in self.__dict__:
return self.__dict__[key]
return default
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ def signature(self):
:rtype: str
"""
return self._signature

def update(self, signature):
# type: (str) -> None
"""
Expand Down
Loading