Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
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
80 changes: 56 additions & 24 deletions azure-iot-device/azure/iot/device/iothub/abstract_clients.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
logger = logging.getLogger(__name__)


def _validate_kwargs(**kwargs):
def _validate_kwargs(exclude=[], **kwargs):
"""Helper function to validate user provided kwargs.
Raises TypeError if an invalid option has been provided"""
valid_kwargs = [
Expand All @@ -30,11 +30,29 @@ def _validate_kwargs(**kwargs):
"cipher",
"server_verification_cert",
"proxy_options",
"sastoken_ttl",
]

for kwarg in kwargs:
if kwarg not in valid_kwargs:
raise TypeError("Got an unexpected keyword argument '{}'".format(kwarg))
if (kwarg not in valid_kwargs) or (kwarg in exclude):
raise TypeError("Unsupported keyword argument: '{}'".format(kwarg))


def _get_config_kwargs(**kwargs):
"""Get the subset of kwargs which pertain the config object"""
valid_config_kwargs = [
"product_info",
"websockets",
"cipher",
"server_verification_cert",
"proxy_options",
]

config_kwargs = {}
for kwarg in kwargs:
if kwarg in valid_config_kwargs:
config_kwargs[kwarg] = kwargs[kwarg]
return config_kwargs


def _form_sas_uri(hostname, device_id, module_id=None):
Expand Down Expand Up @@ -80,9 +98,11 @@ def create_from_connection_string(cls, connection_string, **kwargs):
arbitrary product info which is appended to the user agent string.
:param proxy_options: Options for sending traffic through proxy servers.
:type proxy_options: :class:`azure.iot.device.ProxyOptions`
:param int sastoken_ttl: The time to live (in seconds) for the created SasToken used for
authentication. Default is 3600 seconds (1 hour)

:raises: ValueError if given an invalid connection_string.
:raises: TypeError if given an unrecognized parameter.
:raises: TypeError if given an unsupported parameter.

:returns: An instance of an IoTHub client that uses a connection string for authentication.
"""
Expand All @@ -101,20 +121,22 @@ def create_from_connection_string(cls, connection_string, **kwargs):
signing_mechanism = auth.SymmetricKeySigningMechanism(
key=connection_string[cs.SHARED_ACCESS_KEY]
)
token_ttl = kwargs.get("sastoken_ttl", 3600)
try:
sastoken = st.SasToken(uri, signing_mechanism)
sastoken = st.SasToken(uri, signing_mechanism, ttl=token_ttl)
except st.SasTokenError as e:
new_err = ValueError("Could not create a SasToken using provided connection string")
new_err = ValueError("Could not create a SasToken using provided values")
new_err.__cause__ = e
raise new_err
# Pipeline Config setup
config_kwargs = _get_config_kwargs(**kwargs)
pipeline_configuration = pipeline.IoTHubPipelineConfig(
device_id=connection_string[cs.DEVICE_ID],
module_id=connection_string.get(cs.MODULE_ID),
hostname=connection_string[cs.HOST_NAME],
gateway_hostname=connection_string.get(cs.GATEWAY_HOST_NAME),
sastoken=sastoken,
**kwargs
**config_kwargs
)
if cls.__name__ == "IoTHubDeviceClient":
pipeline_configuration.blob_upload = True
Expand Down Expand Up @@ -194,16 +216,18 @@ def create_from_x509_certificate(cls, x509, hostname, device_id, **kwargs):
:param proxy_options: Options for sending traffic through proxy servers.
:type proxy_options: :class:`azure.iot.device.ProxyOptions`

:raises: TypeError if given an unrecognized parameter.
:raises: TypeError if given an unsupported parameter.

:returns: An instance of an IoTHub client that uses an X509 certificate for authentication.
"""
# Ensure no invalid kwargs were passed by the user
_validate_kwargs(**kwargs)
excluded_kwargs = ["sastoken_ttl"]
_validate_kwargs(exclude=excluded_kwargs, **kwargs)

# Pipeline Config setup
config_kwargs = _get_config_kwargs(**kwargs)
pipeline_configuration = pipeline.IoTHubPipelineConfig(
device_id=device_id, hostname=hostname, x509=x509, **kwargs
device_id=device_id, hostname=hostname, x509=x509, **config_kwargs
)
pipeline_configuration.blob_upload = True # Blob Upload is a feature on Device Clients

Expand Down Expand Up @@ -235,8 +259,10 @@ def create_from_symmetric_key(cls, symmetric_key, hostname, device_id, **kwargs)
arbitrary product info which is appended to the user agent string.
:param proxy_options: Options for sending traffic through proxy servers.
:type proxy_options: :class:`azure.iot.device.ProxyOptions`
:param int sastoken_ttl: The time to live (in seconds) for the created SasToken used for
authentication. Default is 3600 seconds (1 hour)
Copy link
Member

Choose a reason for hiding this comment

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

random comment: I like to see the level of detail in our docstrings go up. If you haven't looked at the public-facing docstrings in the other Azure SDKs, you should (I think I'm thinking of eventhub, but maybe uamqp).

Copy link
Member Author

Choose a reason for hiding this comment

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

Yeah I agree, we could do a bit more. Once we get docgen occurring correctly, there's a number of improvements I think could be made.


:raises: TypeError if given an unrecognized parameter.
:raises: TypeError if given an unsupported parameter.

:return: An instance of an IoTHub client that uses a symmetric key for authentication.
"""
Expand All @@ -246,16 +272,18 @@ def create_from_symmetric_key(cls, symmetric_key, hostname, device_id, **kwargs)
# Create SasToken
uri = _form_sas_uri(hostname=hostname, device_id=device_id)
signing_mechanism = auth.SymmetricKeySigningMechanism(key=symmetric_key)
token_ttl = kwargs.get("sastoken_ttl", 3600)
try:
sastoken = st.SasToken(uri, signing_mechanism)
sastoken = st.SasToken(uri, signing_mechanism, ttl=token_ttl)
except st.SasTokenError as e:
new_err = ValueError("Could not create a SasToken using provided values")
new_err.__cause__ = e
raise new_err

# Pipeline Config setup
config_kwargs = _get_config_kwargs(**kwargs)
pipeline_configuration = pipeline.IoTHubPipelineConfig(
device_id=device_id, hostname=hostname, sastoken=sastoken, **kwargs
device_id=device_id, hostname=hostname, sastoken=sastoken, **config_kwargs
)
pipeline_configuration.blob_upload = True # Blob Upload is a feature on Device Clients

Expand Down Expand Up @@ -297,19 +325,19 @@ def create_from_edge_environment(cls, **kwargs):
arbitrary product info which is appended to the user agent string.
:param proxy_options: Options for sending traffic through proxy servers.
:type proxy_options: :class:`azure.iot.device.ProxyOptions`
:param int sastoken_ttl: The time to live (in seconds) for the created SasToken used for
authentication. Default is 3600 seconds (1 hour)

:raises: OSError if the IoT Edge container is not configured correctly.
:raises: ValueError if debug variables are invalid.
:raises: TypeError if given an unsupported parameter.

:returns: An instance of an IoTHub client that uses the IoT Edge environment for
authentication.
"""
# Ensure no invalid kwargs were passed by the user
_validate_kwargs(**kwargs)
if kwargs.get("server_verification_cert"):
raise TypeError(
"'server_verification_cert' is not supported by clients using an IoT Edge environment"
)
excluded_kwargs = ["server_verification_cert"]
_validate_kwargs(exclude=excluded_kwargs, **kwargs)

# First try the regular Edge container variables
try:
Expand Down Expand Up @@ -382,24 +410,26 @@ def create_from_edge_environment(cls, **kwargs):

# Create SasToken
uri = _form_sas_uri(hostname=hostname, device_id=device_id, module_id=module_id)
token_ttl = kwargs.get("sastoken_ttl", 3600)
try:
sastoken = st.SasToken(uri, signing_mechanism)
sastoken = st.SasToken(uri, signing_mechanism, ttl=token_ttl)
except st.SasTokenError as e:
new_err = ValueError(
"Could not create a SasToken using the values in the Edge environment"
"Could not create a SasToken using the values provided, or in the Edge environment"
)
new_err.__cause__ = e
raise new_err

# Pipeline Config setup
config_kwargs = _get_config_kwargs(**kwargs)
pipeline_configuration = pipeline.IoTHubPipelineConfig(
device_id=device_id,
module_id=module_id,
hostname=hostname,
gateway_hostname=gateway_hostname,
sastoken=sastoken,
server_verification_cert=server_verification_cert,
**kwargs
**config_kwargs
)
pipeline_configuration.method_invoke = (
True
Expand Down Expand Up @@ -439,16 +469,18 @@ def create_from_x509_certificate(cls, x509, hostname, device_id, module_id, **kw
:param proxy_options: Options for sending traffic through proxy servers.
:type proxy_options: :class:`azure.iot.device.ProxyOptions`

:raises: TypeError if given an unrecognized parameter.
:raises: TypeError if given an unsupported parameter.

:returns: An instance of an IoTHub client that uses an X509 certificate for authentication.
"""
# Ensure no invalid kwargs were passed by the user
_validate_kwargs(**kwargs)
excluded_kwargs = ["sastoken_ttl"]
_validate_kwargs(exclude=excluded_kwargs, **kwargs)

# Pipeline Config setup
config_kwargs = _get_config_kwargs(**kwargs)
pipeline_configuration = pipeline.IoTHubPipelineConfig(
device_id=device_id, module_id=module_id, hostname=hostname, x509=x509, **kwargs
device_id=device_id, module_id=module_id, hostname=hostname, x509=x509, **config_kwargs
)

# Pipeline setup
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,15 +19,26 @@
logger = logging.getLogger(__name__)


def _validate_kwargs(**kwargs):
def _validate_kwargs(exclude=[], **kwargs):
"""Helper function to validate user provided kwargs.
Raises TypeError if an invalid option has been provided"""
# TODO: add support for server_verification_cert
valid_kwargs = ["websockets", "cipher", "proxy_options"]
valid_kwargs = ["websockets", "cipher", "proxy_options", "sastoken_ttl"]

for kwarg in kwargs:
if kwarg not in valid_kwargs:
raise TypeError("Got an unexpected keyword argument '{}'".format(kwarg))
if (kwarg not in valid_kwargs) or (kwarg in exclude):
raise TypeError("Unsupported keyword argument '{}'".format(kwarg))


def _get_config_kwargs(**kwargs):
"""Get the subset of kwargs which pertain the config object"""
valid_config_kwargs = ["websockets", "cipher", "proxy_options"]

config_kwargs = {}
for kwarg in kwargs:
if kwarg in valid_config_kwargs:
config_kwargs[kwarg] = kwargs[kwarg]
return config_kwargs


def _form_sas_uri(id_scope, registration_id):
Expand Down Expand Up @@ -98,20 +109,22 @@ def create_from_symmetric_key(
# Create SasToken
uri = _form_sas_uri(id_scope=id_scope, registration_id=registration_id)
signing_mechanism = auth.SymmetricKeySigningMechanism(key=symmetric_key)
token_ttl = kwargs.get("sastoken_ttl", 3600)
try:
sastoken = st.SasToken(uri, signing_mechanism)
sastoken = st.SasToken(uri, signing_mechanism, ttl=token_ttl)
except st.SasTokenError as e:
new_err = ValueError("Could not create a SasToken using the provided values")
new_err.__cause__ = e
raise new_err

# Pipeline Config setup
config_kwargs = _get_config_kwargs(**kwargs)
pipeline_configuration = pipeline.ProvisioningPipelineConfig(
hostname=provisioning_host,
registration_id=registration_id,
id_scope=id_scope,
sastoken=sastoken,
**kwargs
**config_kwargs
)

# Pipeline setup
Expand Down Expand Up @@ -154,15 +167,17 @@ def create_from_x509_certificate(
:returns: A ProvisioningDeviceClient which can register via Symmetric Key.
"""
# Ensure no invalid kwargs were passed by the user
_validate_kwargs(**kwargs)
excluded_kwargs = ["sastoken_ttl"]
_validate_kwargs(exclude=excluded_kwargs, **kwargs)

# Pipeline Config setup
config_kwargs = _get_config_kwargs(**kwargs)
pipeline_configuration = pipeline.ProvisioningPipelineConfig(
hostname=provisioning_host,
registration_id=registration_id,
id_scope=id_scope,
x509=x509,
**kwargs
**config_kwargs
)

# Pipeline setup
Expand Down
Loading