Error Handling Example

This script shows how handle errors occurring during API calls to the Vibration service

The mvg design principle is that the client application using the MVG API is responsible for error handling

[1]:
import json
import os
import sys
import logging
from requests import HTTPError

# import mvg library with python bindings to mvg-API
from mvg import MVG

Note that the TOKEN is used both for authorization and authentication. Thus, each unique token represents a unique user and each user has their own unique database on the VA vibration service.

You need to insert your token received from Viking Analytics here:

[2]:
TOKEN = os.environ["TEST_TOKEN"]  # replace with your token

This is Viking Analytics default logging setup and can be adapted to suit your needs. Log messages are printed from mvg library, see the source code for details.

[3]:
root_logger = logging.getLogger()
root_logger.setLevel("INFO")
formatter = logging.Formatter("%(asctime)s - %(levelname)s - %(name)s - %(message)s")
stream_handler = logging.StreamHandler(stream=sys.stderr)
stream_handler.setFormatter(formatter)
root_logger.addHandler(stream_handler)

As we are interested in error handling we set the logger level to logging.DEBUG. That will trace out the detailed information on exceptions provided by the server.

Set log level to logging.ERROR to just see the results of proper error handling in the code root_logger.setLevel(logging.ERROR)

[4]:
root_logger.setLevel(logging.DEBUG)

Instantiate a session object with mvg library a session object basically caches the endpoint and the token, to simplify the the calls to the mvg library.

[5]:
ENDPOINT = "https://api.beta.multiviz.com"
session = MVG(ENDPOINT, TOKEN)
2021-09-22 19:53:10,807 - DEBUG - urllib3.connectionpool - Starting new HTTPS connection (1): api.beta.multiviz.com:443
2021-09-22 19:53:10,999 - DEBUG - urllib3.connectionpool - https://api.beta.multiviz.com:443 "GET / HTTP/1.1" 200 115

We now check if the server is alive. The hello message contains, amongst others the API version.

[6]:
hello_message = json.dumps(session.say_hello())
print(hello_message)
2021-09-22 19:53:11,007 - INFO - mvg.mvg - Getting API info for: https://api.beta.multiviz.com
2021-09-22 19:53:11,010 - DEBUG - urllib3.connectionpool - Starting new HTTPS connection (1): api.beta.multiviz.com:443
2021-09-22 19:53:11,193 - DEBUG - urllib3.connectionpool - https://api.beta.multiviz.com:443 "GET / HTTP/1.1" 200 115
{"api": {"name": "MultiViz Engine API", "version": "v0.2.0", "swagger": "http://api.beta.multiviz.com/docs"}}

Invalid Token

Lets provoke an error by creating a session with a non-valid token.

[7]:
unauth_session = MVG(ENDPOINT, "PASSKEY")
2021-09-22 19:53:11,205 - DEBUG - urllib3.connectionpool - Starting new HTTPS connection (1): api.beta.multiviz.com:443
2021-09-22 19:53:11,394 - DEBUG - urllib3.connectionpool - https://api.beta.multiviz.com:443 "GET / HTTP/1.1" 200 115

We need to call a method requiring authentication. Check http://endpoint/docs to see which methods require authentication, they have a lock icon.

[8]:
try:
    print(">>> Provoke Unathorized call")
    sources = unauth_session.list_sources()
except HTTPError as exc:
    # As we have rouge token
    # We'll end up here
    print("OUCH")
    # The exception will be risen by
    # raise_for_status from the requests
    # We'll print it and see that it
    # states an authorization error (401)
    print(exc)
2021-09-22 19:53:11,405 - INFO - mvg.mvg - endpoint https://api.beta.multiviz.com
2021-09-22 19:53:11,407 - INFO - mvg.mvg - listing sources
2021-09-22 19:53:11,409 - DEBUG - urllib3.connectionpool - Starting new HTTPS connection (1): api.beta.multiviz.com:443
2021-09-22 19:53:11,598 - DEBUG - urllib3.connectionpool - https://api.beta.multiviz.com:443 "GET /sources/ HTTP/1.1" 401 25
>>> Provoke Unathorized call
OUCH
401 Client Error: Unauthorized for url: https://api.beta.multiviz.com/sources/

We’ll now return to the original session and show how to get details when the server internally rejected a request for other reasons.

Illegal source_id

We provoke the service using an illegal string for the source_id

[9]:
try:
    print(">>> Provoke illegal source Id name")
    session.create_source("illegal%name", {}, channels=["acc"])
except HTTPError as exc:
    # As we have rouge token
    # We'll end up here
    print("OUCH")
    # The exception will be risen by
    # raise_for_status from the requests
    # We'll print it and see that it
    # states an authorization error (401)
    print(exc)
2021-09-22 19:53:11,611 - INFO - mvg.mvg - endpoint https://api.beta.multiviz.com
2021-09-22 19:53:11,614 - INFO - mvg.mvg - creating source with source id=illegal%name
2021-09-22 19:53:11,616 - INFO - mvg.mvg - metadata: {}
2021-09-22 19:53:11,619 - DEBUG - urllib3.connectionpool - Starting new HTTPS connection (1): api.beta.multiviz.com:443
2021-09-22 19:53:11,797 - DEBUG - urllib3.connectionpool - https://api.beta.multiviz.com:443 "POST /sources/ HTTP/1.1" 422 120
>>> Provoke illegal source Id name
OUCH
422 Client Error: Unprocessable Entity for url: https://api.beta.multiviz.com/sources/

We’ll show a couple of examples what can go wrong when creating a measurement

Non-existing Source

In some cases where there is detailed information provided by the server, we can retrieve it by inspecting the exception object exc, which actually contains all information about the request.

[10]:
try:
    print(">>> Non existing source")
    d = [1, 2, 3]
    session.create_measurement(sid="",
                               duration=-3,
                               timestamp=-5,
                               data=d,
                               meta={})
except HTTPError as exc:
    print(exc)
    print("Details on error")
    print(exc.response.json())
2021-09-22 19:53:11,808 - INFO - mvg.mvg - endpoint https://api.beta.multiviz.com
2021-09-22 19:53:11,809 - INFO - mvg.mvg - creating measurement from source id=
2021-09-22 19:53:11,812 - INFO - mvg.mvg -   duration:  -3
>>> Non existing source
2021-09-22 19:53:11,815 - INFO - mvg.mvg -   timestamp: -5
2021-09-22 19:53:11,816 - INFO - mvg.mvg -   meta data: {}
2021-09-22 19:53:11,820 - DEBUG - urllib3.connectionpool - Starting new HTTPS connection (1): api.beta.multiviz.com:443
2021-09-22 19:53:11,991 - DEBUG - urllib3.connectionpool - https://api.beta.multiviz.com:443 "POST /sources//measurements HTTP/1.1" 404 22
404 Client Error: Not Found for url: https://api.beta.multiviz.com/sources//measurements
Details on error
{'detail': 'Not Found'}

Parameter Value Out of Range

[11]:
try:
    print(">>> Parameter value out of range")
    d = {"acc": [1, 2, 3]}
    session.create_measurement(sid="u0001",
                               duration=-3,
                               timestamp=-5,
                               data=d,
                               meta={})
except HTTPError as exc:
    print(exc)
    # Retrieve details
    print("Details on error")
    print(exc.response.json())
2021-09-22 19:53:12,003 - INFO - mvg.mvg - endpoint https://api.beta.multiviz.com
2021-09-22 19:53:12,005 - INFO - mvg.mvg - creating measurement from source id=u0001
2021-09-22 19:53:12,007 - INFO - mvg.mvg -   duration:  -3
2021-09-22 19:53:12,008 - INFO - mvg.mvg -   timestamp: -5
2021-09-22 19:53:12,008 - INFO - mvg.mvg -   meta data: {}
>>> Parameter value out of range
2021-09-22 19:53:12,012 - DEBUG - urllib3.connectionpool - Starting new HTTPS connection (1): api.beta.multiviz.com:443
2021-09-22 19:53:12,206 - DEBUG - urllib3.connectionpool - https://api.beta.multiviz.com:443 "POST /sources/u0001/measurements HTTP/1.1" 422 101
422 Client Error: Unprocessable Entity for url: https://api.beta.multiviz.com/sources/u0001/measurements
Details on error
{'detail': [{'loc': ['body', 0, 'timestamp'], 'msg': 'Timestamp cannot be negative', 'type': 'value_error'}]}

Missing Parameter

In this case we have an error on the client side. This means that the data is never sent to the server so we do not get an HTTPError, but a TypeError instead. Only the HTTPError contains any details from the server, so to be on the safe side we should catch them separately.

[12]:
try:
    print(">>> Parameter missing in mvg call")
    session.create_measurement(sid="u0001",
                               duration=-3,
                               timestamp=-5,
                               meta={})
except HTTPError as exc:
    print(exc)
    print("Details on error")
    print(exc.response.json())
except Exception as exc:
    print(f"{type(exc).__name__}: {exc}")
    print("No details from server available")
>>> Parameter missing in mvg call
TypeError: create_measurement() missing 1 required positional argument: 'data'
No details from server available