From 56cc0a48cd03d3b91fe4b2dd7f8eeeefab8f25f1 Mon Sep 17 00:00:00 2001 From: Adam Galloway Date: Thu, 17 Dec 2015 15:32:49 -0500 Subject: [PATCH 001/104] added find_content renamed network to consumer app fixed bug in device auth fixed required port --- README.md | 4 +++- exp/api/__init__.py | 11 ++++++++--- exp/lib/credentials.py | 16 +++++++--------- exp/runtime.py | 24 +++++++++++++++++++++--- 4 files changed, 39 insertions(+), 16 deletions(-) diff --git a/README.md b/README.md index 106f1a4..15b7cd3 100644 --- a/README.md +++ b/README.md @@ -32,6 +32,8 @@ exp.runtime.start( organization="exp") # Authenticate with device uuid and secret. exp.runtime.start(uuid="[uuid]", secret="[secret]") +# Authenticate with consumer app uuid and api key. +exp.runtime.start(uuid="[uuid]", apiKey="[apiKey]") ``` ## exp.runtime.stop() @@ -59,7 +61,7 @@ devices = exp.api.find_devices(**params) # Query for device objects (url params device = exp.api.get_device(uuid) # Get device by UUID. device = exp.api.create_device(document) # Create a device from a dictionary ``` -Other available namespaces: experiences, locations, content, data. contents do not currently support queries or creation, only "get_content(uuid)". +Other available namespaces: experiences, locations, content, data. content does not currently support creation, only "get_content(uuid) and find_content(params)". ## API Resources Each resource object contains a "document" field which is a dictionary representation of the raw resource, along with "save" and "delete" methods. diff --git a/exp/api/__init__.py b/exp/api/__init__.py index 2fd62a7..ce7cd10 100644 --- a/exp/api/__init__.py +++ b/exp/api/__init__.py @@ -13,9 +13,14 @@ """ Content """ def get_content(uuid): - return Content( - api_utils.get("/api/content/" + uuid + "/children"), - _is_children_populated=True) + return Content( + api_utils.get("/api/content/" + uuid + "/children"), + _is_children_populated=True) + +def find_content(**params): + query = api_utils.get('/api/content', params=params) + empty = [] + return [Content(x, _is_children_populated=False) for x in query.get("results", empty)] """ Devices """ diff --git a/exp/lib/credentials.py b/exp/lib/credentials.py index 42ee657..09b6665 100644 --- a/exp/lib/credentials.py +++ b/exp/lib/credentials.py @@ -13,7 +13,6 @@ _vars["password"] = None _vars["token"] = None _vars["time"] = 0 -_vars["networkUuid"] = None _vars["apiKey"] = None def _reset(): @@ -24,7 +23,6 @@ def _reset(): _vars["organization"] = None _vars["token"] = None _vars["time"] = 0 - _vars["networkUuid"] = None _vars["apiKey"] = None def _jwtHS256encode(payload, secret): @@ -46,9 +44,9 @@ def set_device_credentials(uuid, secret): _vars["uuid"] = uuid _vars["secret"] = secret -def set_network_credentials(uuid, apiKey): +def set_consumer_app_credentials(uuid, apiKey): _reset() - _vars["networkUuid"] = uuid + _vars["consumerAppUuid"] = uuid _vars["apiKey"] = apiKey def set_token(token): @@ -66,8 +64,8 @@ def _generate_token(): _generate_device_token() elif _vars["username"] and _vars["password"]: _generate_user_token() - elif _vars["networkUuid"] and _vars["apiKey"]: - _generate_network_token() + elif _vars["uuid"] and _vars["apiKey"]: + _generate_consumer_app_token() else: _vars["token"] = '' _vars["time"] = 0 @@ -79,13 +77,13 @@ def _generate_user_token(): def _generate_device_token(): payload = {} - payload["uuid"] = cls._uuid + payload["uuid"] = _vars["uuid"] _vars["token"] = _jwtHS256encode(payload, _vars["secret"]) _vars["time"] = time.time() -def _generate_network_token(): +def _generate_consumer_app_token(): payload = {} - payload["networkUuid"] = _vars["networkUuid"] + payload["uuid"] = _vars["uuid"] _vars["token"] = _jwtHS256encode(payload, _vars["apiKey"]) _vars["time"] = time.time() diff --git a/exp/runtime.py b/exp/runtime.py index 7fec5e0..cbe7067 100644 --- a/exp/runtime.py +++ b/exp/runtime.py @@ -16,27 +16,45 @@ def _trigger_offline(): socket.on('disconnected', _trigger_offline) def start( - host='http://api.exp.scala.com/', - port=80, + host='https://api.exp.scala.com/', + port=None, uuid=None, + deviceUuid=None, secret=None, username=None, password=None, organization=None, token=None, networkUuid=None, + consumerAppUuid=None, apiKey=None, **kwargs): + + if port is None: + if host.startswith('http:'): + port = 80 + else: + port = 443 + config.set(host=host, port=port) + if uuid and secret: credentials.set_device_credentials(uuid, secret) + elif deviceUuid and secret: + credentials.set_device_credentials(deviceUuid, secret) + elif uuid and apiKey: + credentials.set_consumer_app_credentials(uuid, apiKey) elif networkUuid and apiKey: - credentials.set_network_credentials(networkUuid, apiKey) + credentials.set_consumer_app_credentials(networkUuid, apiKey) + elif consumerAppUuid and apiKey: + credentials.set_consumer_app_credentials(consumerAppUuid, apiKey) elif username and password and organization: credentials.set_user_credentials(username, password, organization) elif token: credentials.set_token(token) + socket.start(host, port, credentials.get_token()) + def stop(): socket.stop() From 880a15840d50bc6eaa3e488b62008aa7de0ec630 Mon Sep 17 00:00:00 2001 From: James Dalessio Date: Fri, 18 Dec 2015 10:44:33 -0500 Subject: [PATCH 002/104] Add feed get_data() to docs. --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 15b7cd3..be87c22 100644 --- a/README.md +++ b/README.md @@ -87,6 +87,8 @@ data = exp.api.create_data(key="4", group="cats", { "name": "fluffy" }) The "content" resource has a ```get_children()``` method that returns the content's children (a list of content objects). Every content object also has a ```get_url()``` and ```get_variant_url(https://github.com/name)``` method that returns a delivery url for the content. +The "feed" resource has a ```get_data()``` method that returns a the feed's decoded JSON document. + # exp.channels Parent namespace for interaction with the event bus. Available channels are: From 4d556b0380ae57da972f2e6bad980d3d77a6f7e1 Mon Sep 17 00:00:00 2001 From: James Dalessio Date: Wed, 30 Dec 2015 11:43:57 -0500 Subject: [PATCH 003/104] Fix bug with device and consumer app credentials. --- exp/lib/api_utils.py | 2 +- exp/lib/credentials.py | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/exp/lib/api_utils.py b/exp/lib/api_utils.py index a7587ec..2c8e595 100644 --- a/exp/lib/api_utils.py +++ b/exp/lib/api_utils.py @@ -1,6 +1,6 @@ import requests import urllib - + from . import config from . import credentials diff --git a/exp/lib/credentials.py b/exp/lib/credentials.py index 09b6665..7e3cfb1 100644 --- a/exp/lib/credentials.py +++ b/exp/lib/credentials.py @@ -46,7 +46,7 @@ def set_device_credentials(uuid, secret): def set_consumer_app_credentials(uuid, apiKey): _reset() - _vars["consumerAppUuid"] = uuid + _vars["uuid"] = uuid _vars["apiKey"] = apiKey def set_token(token): @@ -77,13 +77,13 @@ def _generate_user_token(): def _generate_device_token(): payload = {} - payload["uuid"] = _vars["uuid"] + payload["deviceUuid"] = _vars["uuid"] _vars["token"] = _jwtHS256encode(payload, _vars["secret"]) _vars["time"] = time.time() def _generate_consumer_app_token(): payload = {} - payload["uuid"] = _vars["uuid"] + payload["consumerAppUuid"] = _vars["uuid"] _vars["token"] = _jwtHS256encode(payload, _vars["apiKey"]) _vars["time"] = time.time() - + From c3843d642fba2b3a08752ceaa177b0194d14adda Mon Sep 17 00:00:00 2001 From: Axel Denker Date: Mon, 4 Jan 2016 21:23:34 +0100 Subject: [PATCH 004/104] Adding timeout Adding optional connection time out to exp.runtime.start(). Defaults to 10 seconds, set in api_utils.py --- exp/lib/api_utils.py | 18 +++++++++++++----- exp/runtime.py | 3 ++- 2 files changed, 15 insertions(+), 6 deletions(-) diff --git a/exp/lib/api_utils.py b/exp/lib/api_utils.py index 2c8e595..0e32aa4 100644 --- a/exp/lib/api_utils.py +++ b/exp/lib/api_utils.py @@ -4,6 +4,13 @@ from . import config from . import credentials +def timeout(): + if config.get("timeout"): + timeout=config.get("timeout") + else: + timeout=10 + return timeout + def generate_url(https://github.com/path): base = config.get("host") if config.get("port"): @@ -24,8 +31,9 @@ def authenticate(username, password, organization): def get(path, params=None): url = generate_url(https://github.com/path) headers = {} + timeOut = timeout() headers["Authorization"] = "Bearer " + credentials.get_token() - response = requests.get(url, params=params, headers=headers) + response = requests.get(url, timeout=timeOut, params=params, headers=headers) response.raise_for_status() return response.json() @@ -33,7 +41,7 @@ def post(path, payload=None, params=None): url = generate_url(https://github.com/path) headers = {} headers["Authorization"] = "Bearer " + credentials.get_token() - response = requests.post(url, params=params, json=payload, headers=headers) + response = requests.post(url, timeout=timeOut, params=params, json=payload, headers=headers) response.raise_for_status() return response.json() @@ -41,7 +49,7 @@ def patch(path, payload=None, params=None): url = generate_url(https://github.com/path) headers = {} headers["Authorization"] = "Bearer " + credentials.get_token() - response = requests.patch(url, params=params, json=payload, headers=headers) + response = requests.patch(url, timeout=timeOut, params=params, json=payload, headers=headers) response.raise_for_status() return response.json() @@ -49,7 +57,7 @@ def put(path, payload=None, params=None): url = generate_url(https://github.com/path) headers = {} headers["Authorization"] = "Bearer " + credentials.get_token() - response = requests.put(url, params=params, json=payload, headers=headers) + response = requests.put(url, timeout=timeOut, params=params, json=payload, headers=headers) response.raise_for_status() return response.json() @@ -57,7 +65,7 @@ def delete(path, payload=None, params=None): url = generate_url(https://github.com/path) headers = {} headers["Authorization"] = "Bearer " + credentials.get_token() - response = requests.delete(url, params=params, json=payload, headers=headers) + response = requests.delete(url, timeout=timeOut, params=params, json=payload, headers=headers) response.raise_for_status() return response.json() diff --git a/exp/runtime.py b/exp/runtime.py index cbe7067..1f71930 100644 --- a/exp/runtime.py +++ b/exp/runtime.py @@ -28,6 +28,7 @@ def start( networkUuid=None, consumerAppUuid=None, apiKey=None, + timeOut=None, **kwargs): if port is None: @@ -36,7 +37,7 @@ def start( else: port = 443 - config.set(host=host, port=port) + config.set(host=host, port=port, timeout=timeout) if uuid and secret: credentials.set_device_credentials(uuid, secret) From bcd4d4f742e061d51f79b114e29aba388fb0bd54 Mon Sep 17 00:00:00 2001 From: Axel Denker Date: Mon, 4 Jan 2016 21:47:02 +0100 Subject: [PATCH 005/104] Single line return --- exp/lib/api_utils.py | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/exp/lib/api_utils.py b/exp/lib/api_utils.py index 0e32aa4..8a7eef0 100644 --- a/exp/lib/api_utils.py +++ b/exp/lib/api_utils.py @@ -5,11 +5,7 @@ from . import credentials def timeout(): - if config.get("timeout"): - timeout=config.get("timeout") - else: - timeout=10 - return timeout + return config.get('timeout') or 10 def generate_url(https://github.com/path): base = config.get("host") From a60e8d71f6b4f7d48f9f2a36377fc7735b025b32 Mon Sep 17 00:00:00 2001 From: James Dalessio Date: Mon, 4 Jan 2016 15:54:27 -0500 Subject: [PATCH 006/104] Fix a few bugs and naming conventions. --- exp/lib/api_utils.py | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/exp/lib/api_utils.py b/exp/lib/api_utils.py index 8a7eef0..4792750 100644 --- a/exp/lib/api_utils.py +++ b/exp/lib/api_utils.py @@ -4,7 +4,7 @@ from . import config from . import credentials -def timeout(): +def get_timeout(): return config.get('timeout') or 10 def generate_url(https://github.com/path): @@ -27,41 +27,45 @@ def authenticate(username, password, organization): def get(path, params=None): url = generate_url(https://github.com/path) headers = {} - timeOut = timeout() + timeout = get_timeout() headers["Authorization"] = "Bearer " + credentials.get_token() - response = requests.get(url, timeout=timeOut, params=params, headers=headers) + response = requests.get(url, timeout=timeout, params=params, headers=headers) response.raise_for_status() return response.json() def post(path, payload=None, params=None): url = generate_url(https://github.com/path) + timeout = get_timeout() headers = {} headers["Authorization"] = "Bearer " + credentials.get_token() - response = requests.post(url, timeout=timeOut, params=params, json=payload, headers=headers) + response = requests.post(url, timeout=timeout, params=params, json=payload, headers=headers) response.raise_for_status() return response.json() def patch(path, payload=None, params=None): url = generate_url(https://github.com/path) + timeout = get_timeout() headers = {} headers["Authorization"] = "Bearer " + credentials.get_token() - response = requests.patch(url, timeout=timeOut, params=params, json=payload, headers=headers) + response = requests.patch(url, timeout=timeout, params=params, json=payload, headers=headers) response.raise_for_status() return response.json() def put(path, payload=None, params=None): url = generate_url(https://github.com/path) + timeout = get_timeout() headers = {} headers["Authorization"] = "Bearer " + credentials.get_token() - response = requests.put(url, timeout=timeOut, params=params, json=payload, headers=headers) + response = requests.put(url, timeout=timeout, params=params, json=payload, headers=headers) response.raise_for_status() return response.json() def delete(path, payload=None, params=None): url = generate_url(https://github.com/path) + timeout = get_timeout() headers = {} headers["Authorization"] = "Bearer " + credentials.get_token() - response = requests.delete(url, timeout=timeOut, params=params, json=payload, headers=headers) + response = requests.delete(url, timeout=timeout, params=params, json=payload, headers=headers) response.raise_for_status() return response.json() From ccb3472dbfd7bf2af01b38df87187b08f3cf957e Mon Sep 17 00:00:00 2001 From: James Dalessio Date: Fri, 8 Jan 2016 13:49:55 -0500 Subject: [PATCH 007/104] Update runtime.py --- exp/runtime.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/exp/runtime.py b/exp/runtime.py index 1f71930..189a0d8 100644 --- a/exp/runtime.py +++ b/exp/runtime.py @@ -28,7 +28,7 @@ def start( networkUuid=None, consumerAppUuid=None, apiKey=None, - timeOut=None, + timeout=None, **kwargs): if port is None: From c7b701e544054e50816371ec5a523ca88f0dbada Mon Sep 17 00:00:00 2001 From: James Dalessio Date: Mon, 11 Jan 2016 10:29:46 -0500 Subject: [PATCH 008/104] Add get layout url. --- exp/lib/models/location.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/exp/lib/models/location.py b/exp/lib/models/location.py index a2f145e..6fd2895 100644 --- a/exp/lib/models/location.py +++ b/exp/lib/models/location.py @@ -18,5 +18,5 @@ def delete(self): api_utils.delete("/api/locations/" + self.document["uuid"]) return self - - + def get_layout_url(https://github.com/self): + return api_utils.generate_url('https://github.com/api/locations/'%20+%20self.document['uuid']%20+%20'/layout') \ No newline at end of file From 324224bc87d459dd93585b6fad005e14d8b8187a Mon Sep 17 00:00:00 2001 From: Adam Galloway Date: Mon, 11 Jan 2016 10:54:06 -0500 Subject: [PATCH 009/104] Added flag to disable event bus EXP-1265 --- exp/runtime.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/exp/runtime.py b/exp/runtime.py index cbe7067..356c46c 100644 --- a/exp/runtime.py +++ b/exp/runtime.py @@ -28,6 +28,7 @@ def start( networkUuid=None, consumerAppUuid=None, apiKey=None, + enableEvents=True, **kwargs): if port is None: @@ -53,7 +54,8 @@ def start( elif token: credentials.set_token(token) - socket.start(host, port, credentials.get_token()) + if enableEvents: + socket.start(host, port, credentials.get_token()) def stop(): From a45ec8740f871dd832d8dc3683aa04264860f2d7 Mon Sep 17 00:00:00 2001 From: Adam Galloway Date: Tue, 12 Jan 2016 15:34:09 -0500 Subject: [PATCH 010/104] Added delay to busy waiting heartbeat (when web sockets fail) --- exp/lib/socket.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/exp/lib/socket.py b/exp/lib/socket.py index 23d3367..0a5d231 100644 --- a/exp/lib/socket.py +++ b/exp/lib/socket.py @@ -54,7 +54,7 @@ def _connect(host, port, token): while time.time() - start < 5: if not _vars["io"]: try: - _vars["io"] = SocketIO(host, port, params={ "token": token }, Namespace=_Namespace) + _vars["io"] = SocketIO(host, port, params={ "token": token }, Namespace=_Namespace, hurry_interval_in_seconds=10) except: _vars["io"] = None time.sleep(1) From 856025b8e98a82388abec0ae6dcf93f19f2c7def Mon Sep 17 00:00:00 2001 From: Adam Galloway Date: Wed, 13 Jan 2016 14:59:39 -0500 Subject: [PATCH 011/104] fixed feed save function and added signal_handler to allow ctrl + C to kill --- exp/lib/models/feed.py | 2 +- exp/runtime.py | 7 +++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/exp/lib/models/feed.py b/exp/lib/models/feed.py index 735d74b..4213efc 100644 --- a/exp/lib/models/feed.py +++ b/exp/lib/models/feed.py @@ -11,7 +11,7 @@ def save(self): self.document = api_utils.post('/api/connectors/feeds', payload=self.document) self._new = False else: - self.document = api_utils.patch('/api/connectors/feeds' + self.document['uuid'], payload=self.document) + self.document = api_utils.patch('/api/connectors/feeds/' + self.document['uuid'], payload=self.document) return self def get_data (self): diff --git a/exp/runtime.py b/exp/runtime.py index ff826a0..c730484 100644 --- a/exp/runtime.py +++ b/exp/runtime.py @@ -2,6 +2,8 @@ from lib import event_node from lib import credentials from lib import config +import signal +import sys _events = event_node.EventNode() on = _events.on @@ -62,5 +64,10 @@ def start( def stop(): socket.stop() +def signal_handler(signal, frame): + socket.stop() + sys.exit(0) + +signal.signal(signal.SIGINT, signal_handler) From 92b1077219b4a768046f20722a77616808c13941 Mon Sep 17 00:00:00 2001 From: James Dalessio Date: Tue, 2 Feb 2016 21:25:20 -0500 Subject: [PATCH 012/104] Begin refactor for new sdk sigs --- exp/__init__.py | 23 +- exp/lib/CredentialsManager.py | 0 exp/lib/api_utils.py | 2 +- exp/lib/channel.py | 2 +- exp/lib/network.py | 0 exp/lib/network/__init__.py | 12 + exp/lib/network/channel.py | 68 +++ exp/lib/network/listener.py | 7 + exp/lib/network/network.py | 0 expsend.py | 831 ++++++++++++++++++++++++++++++++++ exptest.py | 42 ++ 11 files changed, 963 insertions(+), 24 deletions(-) create mode 100644 exp/lib/CredentialsManager.py create mode 100644 exp/lib/network.py create mode 100644 exp/lib/network/__init__.py create mode 100644 exp/lib/network/channel.py create mode 100644 exp/lib/network/listener.py create mode 100644 exp/lib/network/network.py create mode 100644 expsend.py create mode 100644 exptest.py diff --git a/exp/__init__.py b/exp/__init__.py index 3c17b39..8a45400 100644 --- a/exp/__init__.py +++ b/exp/__init__.py @@ -2,25 +2,4 @@ from . import channels from . import runtime -from . import api - - - - - - - - - - - - - - - - - - - - - +from . import api \ No newline at end of file diff --git a/exp/lib/CredentialsManager.py b/exp/lib/CredentialsManager.py new file mode 100644 index 0000000..e69de29 diff --git a/exp/lib/api_utils.py b/exp/lib/api_utils.py index 4792750..40970c1 100644 --- a/exp/lib/api_utils.py +++ b/exp/lib/api_utils.py @@ -27,7 +27,7 @@ def authenticate(username, password, organization): def get(path, params=None): url = generate_url(https://github.com/path) headers = {} - timeout = get_timeout() + timeout = get_timeout() headers["Authorization"] = "Bearer " + credentials.get_token() response = requests.get(url, timeout=timeout, params=params, headers=headers) response.raise_for_status() diff --git a/exp/lib/channel.py b/exp/lib/channel.py index fbce471..412f30e 100644 --- a/exp/lib/channel.py +++ b/exp/lib/channel.py @@ -12,7 +12,7 @@ class Error(Exception): class Channel(object): Timeout = Timeout - Error = Error + Error = Error def __init__(self, name): self._name = name diff --git a/exp/lib/network.py b/exp/lib/network.py new file mode 100644 index 0000000..e69de29 diff --git a/exp/lib/network/__init__.py b/exp/lib/network/__init__.py new file mode 100644 index 0000000..4ebdf0a --- /dev/null +++ b/exp/lib/network/__init__.py @@ -0,0 +1,12 @@ + + +class Network: + pass + + +class Channel: + pass + +class Subscription: + pass + diff --git a/exp/lib/network/channel.py b/exp/lib/network/channel.py new file mode 100644 index 0000000..510f46d --- /dev/null +++ b/exp/lib/network/channel.py @@ -0,0 +1,68 @@ + +class Listener: + + def __init__(self, namespace): + this._namespace = namespace + this._messages = [] + + def receive (message): + pass + + def cancel (self): + pass + + + +class Namespace: + + def __init__(self, channel): + this._listeners = [] + this._channel = channel + + def unlisten (self, listener): + try: + this._listeners.remove(listener) + except ValueError: + pass + if not this._listeners: + this._channel.unlisten(self) + + def listen (self): + this._listeners.append(Listener()) + return this._listeners[-1] + + def receive (message): + return [listener.receive(message) for listener in this._listeners] + + +class Channel: + + def __init__(self, id): + this._id = id + this._namespaces = {} + + def recieve (self, message): + namespace = this._namespaces.get(message.get('name')) + if not namespace: return + return namespace.receive(message) + + def broadcast (self, **kwargs): + kwargs['id'] = self._id + return Network.broadcast(**kwargs) + + def listen (self, name): + Network._subscribe(self._id) + if not self._namespaces[name]: + self._namespaces[name] = Namespace() + return self._namespaces[name].listen() + + def _subscribe (self): + return Network.subscribe(self._id) + + def _unsubscribe (self): + return Network.unsubscribe(self._id) + + def _unlisten (self, namespace): + self._namespaces.pop(namespace, None) + if not bool(self._namespaces): + self._unsubscribe() diff --git a/exp/lib/network/listener.py b/exp/lib/network/listener.py new file mode 100644 index 0000000..8149f07 --- /dev/null +++ b/exp/lib/network/listener.py @@ -0,0 +1,7 @@ + + + +class Listener: + + def __init__(self): + pass diff --git a/exp/lib/network/network.py b/exp/lib/network/network.py new file mode 100644 index 0000000..e69de29 diff --git a/expsend.py b/expsend.py new file mode 100644 index 0000000..b1ee96e --- /dev/null +++ b/expsend.py @@ -0,0 +1,831 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + expsend.py | Scala Inc Slack + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + Menu + + +

+ + + Scala Inc + +

+ + + +
+ +
+ +
+ + + +
+ + + +
+ +

+ 374b Python snippet created on November 25th 2015. + This file is private. +

+ + + Actions + + + + + + +
+
import exp
+print
+print "Starting EXP runtime..."
+exp.runtime.start(
+  username="peter.cherna@scala.com",
+  password="...",
+  host="https://api-develop.exp.scala.com",
+  number="443",
+  organization="scala")
+print "Started!"
+print "Sending!"
+exp.channels.organization.broadcast(name="hello")
+print "Stopping EXP runtime..."
+exp.runtime.stop()
+print "Stopped!"
+
+ +

+ +

+ +
+ +
+
+
+
+
+
+ + + + + + + + shift+enter to add a new line +
+ + +
+
+
+
+ + + + +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/exptest.py b/exptest.py new file mode 100644 index 0000000..b1e86bf --- /dev/null +++ b/exptest.py @@ -0,0 +1,42 @@ +import exp +import time +print +print "Starting EXP runtime..." +exp.runtime.start( + username="email@email.com", + password="Password12321", + host="https://api-develop.exp.scala.com/", + port=443, + organization="scala") + + + +def test (message): + print 'HERE' + print message + + +try: + experiences = exp.api.find_experiences() +except Exception as error: + print error +print experiences +while True: + time.sleep(1) + + + +"""exp.channels.organization.listen(name="hello", callback=test) +print "Started!" +print "Sending!" +time.sleep(1) +exp.channels.organization.broadcast(name="hello") +print "Stopping EXP runtime..." + +exp.channels.organization.broadcast(name="hello")""" + + + + +exp.runtime.stop() +print "Stopped!" From 927a5161ce4dad00f92fcbf2702867c6f7ecbd9e Mon Sep 17 00:00:00 2001 From: James Dalessio Date: Wed, 17 Feb 2016 16:09:58 -0500 Subject: [PATCH 013/104] Update readme to include new channel stuff. --- README.md | 98 +++++++++++++++++++++++++++++++++---------------------- 1 file changed, 59 insertions(+), 39 deletions(-) diff --git a/README.md b/README.md index be87c22..3781de7 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ The SDK is an importable Python module that facilitates API and event bus action ```python import exp -exp.runtime.start( +exp.start( username="joe@exp.com", password="joesmoe25", host="http://localhost/", @@ -21,19 +21,19 @@ exp.runtime.stop() # exp.runtime -## exp.runtime.start() -The SDK must be initialized by calling ```exp.runtime.start()``` and passing in configuration options. This starts the event bus and automatically authenticates API calls. The start command will block until a connection is first established. +## exp.start() +The SDK must be initialized by calling ```exp.start()``` and passing in configuration options. This starts the event bus and automatically authenticates API calls. The start command will block until a connection is first established. ```python # Authenticate with username and password. -exp.runtime.start( +exp.start( username="joe@exp.com", password="joesmoe25", organization="exp") # Authenticate with device uuid and secret. -exp.runtime.start(uuid="[uuid]", secret="[secret]") +exp.start(uuid="[uuid]", secret="[secret]") # Authenticate with consumer app uuid and api key. -exp.runtime.start(uuid="[uuid]", apiKey="[apiKey]") +exp.start(uuid="[uuid]", apiKey="[apiKey]") ``` ## exp.runtime.stop() @@ -90,54 +90,74 @@ The "content" resource has a ```get_children()``` method that returns the conten The "feed" resource has a ```get_data()``` method that returns a the feed's decoded JSON document. -# exp.channels -Parent namespace for interaction with the event bus. Available channels are: -```python -exp.channels.system # Calls to and from the system -exp.channels.experience -exp.channels.location -exp.channels.organization -``` -## exp.channels.[channel].fling -Fling content on a channel. -```python -uuid = '4abd....' -exp.channels.organization.fling(uuid) -``` -## exp.channels.[channel].request -Send a request on this channel. + + +## The EXP Network + +The EXP network facilitates real time communication between entities connected to EXP. A user or device can broadcast a JSON serializable payload to users and devices in your organization, and listeners to those broadcasts can respond to the broadcasters. + +### Channels + +All messages on the EXP network are sent over a channel. Channels have a name, and two flags: ```system``` and ```consumer```. + ```python -response = exp.channels.location.request( - name="getSomething", - target= { "device" : "[uuid]"}, payload= { "info": 123 }) +channel = exp.get_channel("my_channel", system=False, consumer=False) ``` -## exp.channels.[channel].respond -Attach a callback to handle requests to this device. Return value of callback is response content. Must be JSON serializable. +Use ```system=True``` to get a system channel. You cannot send messages on a system channels but can listen for system notifications, such as updates to API resources. + +Use ```consumer=True``` to get a consumer channel. Consumer devices can only listen or broadcast on consumer channels. When ```consumer=False``` you will not receive consumer device broadcasts and consumer devices will not be able to hear your broadcasts. + +Both ```system``` and ```consumer``` default to ```False``` except for consumer devices, where ```consumer``` will always be ```True``` for all channels. + + +### Broadcasting + +Use the broadcast method of a channel object to send a named message with a JSON serializable payload to other entities on the EXP network. You can optionally include a timeout to wait for responses to the broadcast. The broadcast will block for approximately the given timeout and return a list of response payloads. Each response payload can any JSON serializable type. + ```python -def get_something(payload=None): - return "Something" -exp.channels.location.respond(name="getSomething", callback=get_something_callback) +channel = exp.get_channel("my_channel") +responses = channel.broadcast(name='Hello!', timeout=5, payload=[1, 2, 3]) +[print response for response in responses] ``` -## exp.channels.[channel].broadcast -Sends a broadcast message on the channel. + +### Listening + +To listen for broadcasts, call the listen method of a channel object. + ```python -exp.channels.experience.broadcast(name="Hi!", payload={}) +channel = exp.get_channel("my_channel") +listener = channel.listen("my_event") + +while True: + broadcast = listener.wait(5) + if broadcast: + print "Message received!" + print broadcast.payload ``` -## exp.channels.[channel].listen -Listens for broadcasts on the channel. Non-blocking, callback is spawned in new thread. + + + +### Responding + +To respond to broadcast, call the respond method on the broadcast object, optionally passing in a JSON serializable response payload. + ```python -def my_method(payload=None): - print payload -exp.channels.experience.listen(name="Hi!", callback=my_method) -``` +channel = exp.get_channel("my_channel") +listener = channel.listen(name="my_custom_event") +while True: + broadcast = listener.wait(5) + if broadcast and broadcast.payload is "hello!": + print "Responding to broadcast." + broadcast.respond("Nice to meet you!") +``` From 13afa27039844a3002231bc1e22cab8537e7bcbc Mon Sep 17 00:00:00 2001 From: James Dalessio Date: Fri, 26 Feb 2016 17:42:27 -0500 Subject: [PATCH 014/104] Wip --- exp/__init__.py | 4 +- exp/channels.py | 6 - exp/exp.py | 30 ++ exp/lib/CredentialsManager.py | 0 exp/lib/network.py | 0 exp/network.py | 6 + exp/runtime.py | 204 ++++++--- expsend.py | 831 ---------------------------------- exptest.py | 42 -- makes.py | 61 +++ 10 files changed, 241 insertions(+), 943 deletions(-) delete mode 100644 exp/channels.py create mode 100644 exp/exp.py delete mode 100644 exp/lib/CredentialsManager.py delete mode 100644 exp/lib/network.py create mode 100644 exp/network.py delete mode 100644 expsend.py delete mode 100644 exptest.py create mode 100644 makes.py diff --git a/exp/__init__.py b/exp/__init__.py index 8a45400..bad50a4 100644 --- a/exp/__init__.py +++ b/exp/__init__.py @@ -1,5 +1,3 @@ """ Main module for Scala EXP SDK """ -from . import channels -from . import runtime -from . import api \ No newline at end of file +from . import exp \ No newline at end of file diff --git a/exp/channels.py b/exp/channels.py deleted file mode 100644 index 58a1e3a..0000000 --- a/exp/channels.py +++ /dev/null @@ -1,6 +0,0 @@ -from lib import channel - -system = channel.Channel('system') -organization = channel.Channel('organization') -location = channel.Channel('location') -experience = channel.Channel('experience') diff --git a/exp/exp.py b/exp/exp.py new file mode 100644 index 0000000..5e08935 --- /dev/null +++ b/exp/exp.py @@ -0,0 +1,30 @@ +import signal +import sys +from . import network +from .runtime import runtime + + + + +class Exp (object): + + def start (*args, **kwargs): + print 'STARTING' + return runtime.start(**kwargs) + + def stop (*args, **kwargs): + return runtime.stop(*args, **kwargs) + + + +#def stop(): +# socket.stop() + +def signal_handler(signal, frame): + print 'GOT SIGING' + sys.exit(0) + +signal.signal(signal.SIGINT, signal_handler) +exp = Exp() + +#signal.pause() diff --git a/exp/lib/CredentialsManager.py b/exp/lib/CredentialsManager.py deleted file mode 100644 index e69de29..0000000 diff --git a/exp/lib/network.py b/exp/lib/network.py deleted file mode 100644 index e69de29..0000000 diff --git a/exp/network.py b/exp/network.py new file mode 100644 index 0000000..abad5db --- /dev/null +++ b/exp/network.py @@ -0,0 +1,6 @@ +import sys + +class Network (object): + pass + +sys.modules['exp.network'] = Network() \ No newline at end of file diff --git a/exp/runtime.py b/exp/runtime.py index c730484..fde1f74 100644 --- a/exp/runtime.py +++ b/exp/runtime.py @@ -1,73 +1,155 @@ -from lib import socket -from lib import event_node -from lib import credentials -from lib import config -import signal + + + +import sys import sys +import time +import json +import hmac +import base64 +import hashlib +import requests +import threading +from . import network + + +class RuntimeException (Exception): + pass + +class Runtime (object): + + def __init__ (self): + self.is_started = False + self.auth = None + self.semaphore = threading.Semaphore() + self.exception = None + + @property + def RuntimeException (): + return RuntimeException + + def start (self, enable_events=True, host='https://api.goexp.io/', **kwargs): + if self.is_started: + raise RuntimeException('Runtime already started.') + self.is_started = True + + self.options = kwargs + self.options['host'] = host + self.options['enable_events'] = enable_events + + if not self.options.get('port'): + if self.options.get('host').startswith('http:'): + self.options['port'] = 80 + else: + self.options['port'] = 443 + + thread = threading.Thread(target=lambda: self.fork()) + thread.daemon = True + thread.start() -_events = event_node.EventNode() -on = _events.on - -def _trigger_online(): - _events.trigger('online') - -def _trigger_offline(): - _events.trigger('offline') - -socket.on('connected', _trigger_online) -socket.on('disconnected', _trigger_offline) - -def start( - host='https://api.exp.scala.com/', - port=None, - uuid=None, - deviceUuid=None, - secret=None, - username=None, - password=None, - organization=None, - token=None, - networkUuid=None, - consumerAppUuid=None, - apiKey=None, - timeout=None, - enableEvents=True, - **kwargs): - - if port is None: - if host.startswith('http:'): - port = 80 + if self.exception: + raise self.exception + + self.semaphore.acquire() + + if self.exception: + raise self.exception + + + def fork (self): + try: + self.validate() + self.login() + except Exception as exception: + self.exception = exception + finally: + self.semaphore.release() + + + def validate (self): + if self.options.get('type') is 'user' or self.options.get('username') or self.options.get('password') or self.options.get('organization'): + self.options['type'] = 'user' + if not self.options.get('username'): + self.abort(RuntimeException('Please specify the username.')) + if not self.options.get('password'): + raise RuntimeException('Please specify the password.') + if not self.options.get('organization'): raise RuntimeException('Please specify the organization.') + elif self.options.get('type') is 'device' or self.options.get('secret'): + self.options['type'] = 'device' + if not self.options.get('uuid') and not self.options.get('allow_pairing'): raise RuntimeException('Please specify the device uuid.') + if not self.options.get('secret') and not self.options.get('allow_pairing'): raise RuntimeException('Please specify the device secret.') + elif self.options.get('type') is 'consumer_app' or self.options.get('api_key'): + self.options['type'] = 'consumer_app' + if not self.options.get('uuid'): raise RuntimeException('Please specify the consumer app uuid.') + if not self.options.get('api_key'): raise RuntimeException('Please specify the consumer app api key.') else: - port = 443 + raise RuntimeException('Please specify authentication type.') + + + def login (self): + print 'Logging in to exp.' + payload = {} + if self.options.get('type') is 'user': + payload['type'] = 'user' + payload['username'] = self.options.get('username') + payload['password'] = self.options.get('password') + payload['organization'] = self.options.get('organization') + elif self.options.get('type') is 'device': + token_payload = {} + token_payload['type'] = 'device' + token_payload['uuid'] = self.options.get('uuid', '_') + token_payload['allowPairing'] = self.options.get('allow_paiing') + payload['token'] = self.generate_jwt(token_payload, self.options.get('secret', '_')) + elif self.options.get('type') is 'consumer_app': + token_payload = {} + token_payload['type'] = 'consumerApp' + token_payload['uuid'] = self.options.get('uuid', '_') + payload['token'] = self.generate_jwt(token_payload, self.options.get('apiKey', '_')) + response = requests.request('POST', self.options.get('host') + '/api/auth/login', json=payload) + if response.status_code is 401: + raise RuntimeException('Invalid credentials.') + elif response.status_code is 200: + print 'GOT CREDS' + self.auth = response.json() + # Release the semaphore + time.sleep((self.auth['expiration'] - int(time.time())) / 2.0 / 1000.0) + self.refresh() + else: + print 'REQUEST FAILED' + time.sleep(5) + self.login() + + def refresh (self): + print 'Refresh' + headers = { 'Authorization': 'Bearer ' + self.auth['token'] } + response = requests.request('POST', self.options.get('host') + '/api/auth/token', headers=headers) + if response.status_code is 401: + self.login() + elif response.status_code is 200: + self.auth = response.json() + time.sleep((self.auth['expiration'] - int(time.time())) / 2.0 / 1000.0) + else: + time.sleep(5) + self.refresh() + - config.set(host=host, port=port, timeout=timeout) + @staticmethod + def generate_jwt (payload, secret): - if uuid and secret: - credentials.set_device_credentials(uuid, secret) - elif deviceUuid and secret: - credentials.set_device_credentials(deviceUuid, secret) - elif uuid and apiKey: - credentials.set_consumer_app_credentials(uuid, apiKey) - elif networkUuid and apiKey: - credentials.set_consumer_app_credentials(networkUuid, apiKey) - elif consumerAppUuid and apiKey: - credentials.set_consumer_app_credentials(consumerAppUuid, apiKey) - elif username and password and organization: - credentials.set_user_credentials(username, password, organization) - elif token: - credentials.set_token(token) + algorithm = { 'alg': 'hs256', 'typ': 'JWT' } + algorithm_json = json.dumps(algorithm, separators(',', ':')).encode('utf-8') + algorithm_b64 = base64.urlsafe_b64encode(algorithm_json) - if enableEvents: - socket.start(host, port, credentials.get_token()) + payload['exp'] = (int(time.time()) + 30) * 1000 + payload_json = json.dumps(payload, separators(',', ':')).encode('utf-8') + payload_b64 = base64.urlsafe_b64encode(payload_json).rstrip('=') + signature = hmac.new(secret.encode('utf-8'), '.'.join([algorithm_b64, payload_b64]), hashlib.sha256).digest() + signature_b64 = urlsafe_b64encode(signature).rstrip('=') -def stop(): - socket.stop() + return '.'.join([algorithm_b64, payload_b64, signature_b64]) -def signal_handler(signal, frame): - socket.stop() - sys.exit(0) -signal.signal(signal.SIGINT, signal_handler) +runtime = Runtime() diff --git a/expsend.py b/expsend.py deleted file mode 100644 index b1ee96e..0000000 --- a/expsend.py +++ /dev/null @@ -1,831 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - expsend.py | Scala Inc Slack - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - Menu - - -

- - - Scala Inc - -

- - - -
- -
- -
- - - -
- - - -
- -

- 374b Python snippet created on November 25th 2015. - This file is private. -

- - - Actions - - - - - - -
-
import exp
-print
-print "Starting EXP runtime..."
-exp.runtime.start(
-  username="peter.cherna@scala.com",
-  password="...",
-  host="https://api-develop.exp.scala.com",
-  number="443",
-  organization="scala")
-print "Started!"
-print "Sending!"
-exp.channels.organization.broadcast(name="hello")
-print "Stopping EXP runtime..."
-exp.runtime.stop()
-print "Stopped!"
-
- -

- -

- -
- -
-
-
-
-
-
- - - - - - - - shift+enter to add a new line -
- - -
-
-
-
- - - - -
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/exptest.py b/exptest.py deleted file mode 100644 index b1e86bf..0000000 --- a/exptest.py +++ /dev/null @@ -1,42 +0,0 @@ -import exp -import time -print -print "Starting EXP runtime..." -exp.runtime.start( - username="email@email.com", - password="Password12321", - host="https://api-develop.exp.scala.com/", - port=443, - organization="scala") - - - -def test (message): - print 'HERE' - print message - - -try: - experiences = exp.api.find_experiences() -except Exception as error: - print error -print experiences -while True: - time.sleep(1) - - - -"""exp.channels.organization.listen(name="hello", callback=test) -print "Started!" -print "Sending!" -time.sleep(1) -exp.channels.organization.broadcast(name="hello") -print "Stopping EXP runtime..." - -exp.channels.organization.broadcast(name="hello")""" - - - - -exp.runtime.stop() -print "Stopped!" diff --git a/makes.py b/makes.py new file mode 100644 index 0000000..6372306 --- /dev/null +++ b/makes.py @@ -0,0 +1,61 @@ +import exp + +exp.runtime.start(username="email@email.com", password="Password12321", organization="scala", host="https://api-develop.exp.scala.com/", port=443) +""" +# experiences = scala.api.get_experiences() +# experience = experiences[1]; +# document = experience.document +# dayplan = document["dayPlans"][0] +# dayplan["blocks"] = []; +# for i in range(86400 / 60): +# dayplan["blocks"].append({ +# "appKey": "default", +# "startTime": i * 60 * 1000, +# "endTime": i * 60 * 1000 + 10 * 1000 +# }) +# dayplan["blocks"].append({ +# "appKey": "default", +# "startTime": i * 60 * 1000 + 20 * 1000, +# "endTime": i * 60 * 1000 + 30 * 1000 +# }) +# print dayplan["blocks"][34] +# experience.save() + + +experiences = exp.api.find_experiences() +print experiences + +exp.api.create_data(key="fluffy", group="cats", value={ "awesome": 3}) + +data = exp.api.get_data('fluffy', 'cats') +data.value = { "test": "cat" } +data.save() + +contents = exp.api.get_content('root') +children = contents.get_children() +for child in children: + try: + print child.get_url() + except Exception as e: + print e + +exp.runtime.stop() + +""" +import time + +def cb(tmp): + print "I got a message!!!!!!" + print "I _________________________________________" + +print "Started!" +print "Sending!" +exp.channels.organization.listen(name="hello", callback=cb) +time.sleep(2) +exp.channels.organization.broadcast(name="hello") +print "hi" +time.sleep(3) +print "Stopping EXP runtime..." +exp.runtime.stop() +print "Stopped!" + From 95c0fffcae6a8b813029870a3961f23e43db0a47 Mon Sep 17 00:00:00 2001 From: James Dalessio Date: Sat, 27 Feb 2016 10:54:42 -0500 Subject: [PATCH 015/104] Add logging. --- .gitignore | 1 + check.py | 11 ++++++ exp/exp.py | 5 +++ exp/network.py | 70 +++++++++++++++++++++++++++++++-- exp/runtime.py | 103 ++++++++++++++++++++++++++++++++----------------- exp/utils.py | 0 6 files changed, 151 insertions(+), 39 deletions(-) create mode 100644 check.py create mode 100644 exp/utils.py diff --git a/.gitignore b/.gitignore index 0d20b64..83658ec 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ *.pyc +*.log diff --git a/check.py b/check.py new file mode 100644 index 0000000..ff2e26e --- /dev/null +++ b/check.py @@ -0,0 +1,11 @@ +import exp + +import time + +if __name__ == '__main__': + print 'B' + print 'A' + print exp.exp + exp.exp.exp.start(username='email@email.com', password='Password12321', organization='scala', host='http://localhost:9000/') + while True: + time.sleep(4) \ No newline at end of file diff --git a/exp/exp.py b/exp/exp.py index 5e08935..ea0ea8f 100644 --- a/exp/exp.py +++ b/exp/exp.py @@ -2,6 +2,10 @@ import sys from . import network from .runtime import runtime +import logging +import utils + + @@ -9,6 +13,7 @@ class Exp (object): def start (*args, **kwargs): + print 'STARTING' return runtime.start(**kwargs) diff --git a/exp/network.py b/exp/network.py index abad5db..cbae795 100644 --- a/exp/network.py +++ b/exp/network.py @@ -1,6 +1,70 @@ -import sys -class Network (object): + + +class Listener (object): + pass + + +class Channel (object): + pass + + +class Subscription (object): pass -sys.modules['exp.network'] = Network() \ No newline at end of file + +class Network (object): + + def __init__(self): + self.channels = {} + self.subscriptions = {} + self.socket = None + + @property + def isConnected (self): + return self.socket not None + + + def connect (self, auth): + self.disconnect() + self.auth = auth + # Establish socket connection. + + def disconnect (self): + if self.socket: + self.socket.disconnect() + self.socket = None + + def broadcast (self, message): + pass + + def listen (self): + pass + + def get_channel (self): + pass + + + def digest (self): + if not self.socket: + if self.auth: + pass + else: + if self.outoing + + + if self.auth and not self.socket: + try: + self.socket = None ## Create socket + except Exception: + return + + + + # Connect if auth has been updated + # Disconnect if we are to terminate. + # Send outgoing messages + # Receive and route incoming messages to channels + + +network = Network() diff --git a/exp/runtime.py b/exp/runtime.py index fde1f74..21ce273 100644 --- a/exp/runtime.py +++ b/exp/runtime.py @@ -1,6 +1,3 @@ - - - import sys import sys import time @@ -12,6 +9,12 @@ import threading from . import network +import logging + +from logging.handlers import RotatingFileHandler + + + class RuntimeException (Exception): pass @@ -19,16 +22,35 @@ class RuntimeException (Exception): class Runtime (object): def __init__ (self): + self.is_started = False self.auth = None - self.semaphore = threading.Semaphore() + self.event = threading.Event() self.exception = None - @property - def RuntimeException (): - return RuntimeException + def configure_logging (self, **kwargs): + + logger = logging.getLogger('exp') + + file_handler_formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s') + file_handler = RotatingFileHandler('debug.log', mode='a', maxBytes=5*1024*1024, backupCount=5, encoding=None, delay=0) + file_handler.setFormatter(file_handler_formatter) + file_handler.setLevel(logging.DEBUG) + logger.addHandler(file_handler) + + stream_handler_formatter = logging.Formatter('EXP/%(levelname)-10s: %(message)s') + stream_handler = logging.StreamHandler() + stream_handler.setLevel(logging.INFO) + stream_handler.setFormatter(stream_handler_formatter) + logger.addHandler(stream_handler) + + logger.setLevel(logging.DEBUG) + self.logger = logger + def start (self, enable_events=True, host='https://api.goexp.io/', **kwargs): + self.configure_logging(**kwargs) + self.logger.info('Starting runtime.') if self.is_started: raise RuntimeException('Runtime already started.') self.is_started = True @@ -46,92 +68,104 @@ def start (self, enable_events=True, host='https://api.goexp.io/', **kwargs): thread = threading.Thread(target=lambda: self.fork()) thread.daemon = True thread.start() + self.event.wait() if self.exception: raise self.exception - - self.semaphore.acquire() - - if self.exception: - raise self.exception - + self.logger.info('Connected to EXP.') def fork (self): try: self.validate() self.login() except Exception as exception: + self.logger.critical('Runtime aborted due to error: %s', exception) self.exception = exception - finally: - self.semaphore.release() - + self.event.set() def validate (self): + self.logger.debug('Validating runtime options: %s', self.options) if self.options.get('type') is 'user' or self.options.get('username') or self.options.get('password') or self.options.get('organization'): + self.logger.debug('Validating user credentials.') self.options['type'] = 'user' if not self.options.get('username'): - self.abort(RuntimeException('Please specify the username.')) + raise RuntimeException('Please specify the username.') if not self.options.get('password'): raise RuntimeException('Please specify the password.') - if not self.options.get('organization'): raise RuntimeException('Please specify the organization.') + if not self.options.get('organization'): + raise RuntimeException('Please specify the organization.') elif self.options.get('type') is 'device' or self.options.get('secret'): + self.logger.debug('Validating device credentials.') self.options['type'] = 'device' - if not self.options.get('uuid') and not self.options.get('allow_pairing'): raise RuntimeException('Please specify the device uuid.') - if not self.options.get('secret') and not self.options.get('allow_pairing'): raise RuntimeException('Please specify the device secret.') + if not self.options.get('uuid') and not self.options.get('allow_pairing'): + raise RuntimeException('Please specify the device uuid.') + if not self.options.get('secret') and not self.options.get('allow_pairing'): + raise RuntimeException('Please specify the device secret.') elif self.options.get('type') is 'consumer_app' or self.options.get('api_key'): + self.logger.debug('Validating consumer app credentials.') self.options['type'] = 'consumer_app' - if not self.options.get('uuid'): raise RuntimeException('Please specify the consumer app uuid.') - if not self.options.get('api_key'): raise RuntimeException('Please specify the consumer app api key.') + if not self.options.get('uuid'): + raise RuntimeException('Please specify the consumer app uuid.') + if not self.options.get('api_key'): + raise RuntimeException('Please specify the consumer app api key.') else: raise RuntimeException('Please specify authentication type.') def login (self): - print 'Logging in to exp.' payload = {} if self.options.get('type') is 'user': + self.logger.debug('Authenticating as a user.') payload['type'] = 'user' payload['username'] = self.options.get('username') payload['password'] = self.options.get('password') payload['organization'] = self.options.get('organization') elif self.options.get('type') is 'device': + self.logger.debug('Authenticating as a device.') token_payload = {} token_payload['type'] = 'device' token_payload['uuid'] = self.options.get('uuid', '_') token_payload['allowPairing'] = self.options.get('allow_paiing') payload['token'] = self.generate_jwt(token_payload, self.options.get('secret', '_')) elif self.options.get('type') is 'consumer_app': + self.logger.debug('Authenticating as a consumer app.') token_payload = {} token_payload['type'] = 'consumerApp' token_payload['uuid'] = self.options.get('uuid', '_') payload['token'] = self.generate_jwt(token_payload, self.options.get('apiKey', '_')) response = requests.request('POST', self.options.get('host') + '/api/auth/login', json=payload) if response.status_code is 401: + self.logger.critical('Credentials invalid. Runtime cannot continue.') raise RuntimeException('Invalid credentials.') elif response.status_code is 200: - print 'GOT CREDS' - self.auth = response.json() - # Release the semaphore - time.sleep((self.auth['expiration'] - int(time.time())) / 2.0 / 1000.0) - self.refresh() + self.logger.debug('Login successful.') + self.on_update(response) else: - print 'REQUEST FAILED' + self.logger.warning('Unknown error trying to login. Retrying in five seconds.') time.sleep(5) self.login() def refresh (self): - print 'Refresh' + self.logger.debug('Refresing authentication token.') headers = { 'Authorization': 'Bearer ' + self.auth['token'] } response = requests.request('POST', self.options.get('host') + '/api/auth/token', headers=headers) if response.status_code is 401: + self.logger.warning('Token refresh failed due to invalid authentication.') self.login() elif response.status_code is 200: - self.auth = response.json() - time.sleep((self.auth['expiration'] - int(time.time())) / 2.0 / 1000.0) + self.logger.debug('Token refresh successful.') + self.on_update(response) else: + self.logger.warning('Token refresh failed for unknown reason.') time.sleep(5) self.refresh() + def on_update(self, response): + self.auth = response.json() + self.logger.debug('Authentication updated: %s', json.dumps(self.auth, indent=4, separators=(',', ': '))) + self.event.set() + time.sleep((self.auth['expiration'] - int(time.time())*1000.0) / 2.0 / 1000.0) + self.refresh() @staticmethod def generate_jwt (payload, secret): @@ -149,7 +183,4 @@ def generate_jwt (payload, secret): return '.'.join([algorithm_b64, payload_b64, signature_b64]) - - - -runtime = Runtime() +runtime = Runtime() \ No newline at end of file diff --git a/exp/utils.py b/exp/utils.py new file mode 100644 index 0000000..e69de29 From 3e5698b7ddafac445c2ab28b7f48f3cbfbf4286c Mon Sep 17 00:00:00 2001 From: James Dalessio Date: Mon, 29 Feb 2016 00:38:48 -0500 Subject: [PATCH 016/104] wip --- README.md | 84 ++++++++-- check.py | 9 +- exp/__init__.py | 15 +- exp/api/__init__.py | 16 +- exp/authenticator.py | 97 +++++++++++ exp/exceptions.py | 21 +++ exp/exp.py | 35 ---- exp/logger.py | 19 +++ exp/network.py | 185 ++++++++++++++++---- exp/{lib => old}/__init__.py | 0 exp/{lib => old}/api_utils.py | 0 exp/{lib => old}/channel.py | 0 exp/{lib => old}/config.py | 0 exp/{lib => old}/credentials.py | 0 exp/{lib => old}/event_node.py | 0 exp/{lib => old}/models/__init__.py | 0 exp/{lib => old}/models/content.py | 0 exp/{lib => old}/models/data.py | 0 exp/{lib => old}/models/device.py | 0 exp/{lib => old}/models/experience.py | 0 exp/{lib => old}/models/feed.py | 0 exp/{lib => old}/models/location.py | 0 exp/{lib => old}/models/thing.py | 0 exp/{lib => old}/network/__init__.py | 0 exp/{lib => old}/network/channel.py | 0 exp/{lib => old}/network/listener.py | 0 exp/{lib => old}/network/network.py | 0 exp/{lib => old}/socket.py | 0 exp/runtime.py | 215 +++++------------------- makes.py | 61 ------- setup.py | 3 +- exp/utils.py => tests/__init__.py | 0 tests/runtime_start_test.py | 233 ++++++++++++++++++++++++++ tests/test0.py | 10 -- 34 files changed, 658 insertions(+), 345 deletions(-) create mode 100644 exp/authenticator.py create mode 100644 exp/exceptions.py delete mode 100644 exp/exp.py create mode 100644 exp/logger.py rename exp/{lib => old}/__init__.py (100%) rename exp/{lib => old}/api_utils.py (100%) rename exp/{lib => old}/channel.py (100%) rename exp/{lib => old}/config.py (100%) rename exp/{lib => old}/credentials.py (100%) rename exp/{lib => old}/event_node.py (100%) rename exp/{lib => old}/models/__init__.py (100%) rename exp/{lib => old}/models/content.py (100%) rename exp/{lib => old}/models/data.py (100%) rename exp/{lib => old}/models/device.py (100%) rename exp/{lib => old}/models/experience.py (100%) rename exp/{lib => old}/models/feed.py (100%) rename exp/{lib => old}/models/location.py (100%) rename exp/{lib => old}/models/thing.py (100%) rename exp/{lib => old}/network/__init__.py (100%) rename exp/{lib => old}/network/channel.py (100%) rename exp/{lib => old}/network/listener.py (100%) rename exp/{lib => old}/network/network.py (100%) rename exp/{lib => old}/socket.py (100%) delete mode 100644 makes.py rename exp/utils.py => tests/__init__.py (100%) create mode 100644 tests/runtime_start_test.py delete mode 100644 tests/test0.py diff --git a/README.md b/README.md index 3781de7..042a91f 100644 --- a/README.md +++ b/README.md @@ -1,24 +1,72 @@ -# Summary and Example -The SDK is an importable Python module that facilitates API and event bus actions on EXP. + + + + + +# [Starting the SDK](#runtime) + +The SDK is started by calling ```exp.start``` and specifying your credentials and configuration options as keyword arguments. ```exp.start``` will start some background processes that keep you authenticated and process network events. You must supply credentials to ```exp.start```. You can supply user, device, or consumer app credentials. You can also authenticate in pairing mode. + +When ```exp.start``` returns, you are authenticated and (optionally) connected to the EXP network. The SDK is non blocking and will stop when your main script finishes. + +## Using User Credentials + +Users must specify their ```username```, ```password```, and ```organization``` as keyword arguments to ```exp.start```. ```python import exp -exp.start( - username="joe@exp.com", - password="joesmoe25", - host="http://localhost/", - port=9000, - organization="exp") -devices = exp.api.get_devices() -devices[0].document.name = "My Device" -devices[0].save() -exp.channels.organization.broadcast(name="I changed a device!") -response = exp.channels.experience.request(name="sendMeMoney", target={ - "device": devices[0].document["uuid"] }) -print response -exp.runtime.stop() + +exp.start(username='joe@joemail.com', password='JoeRocks42', organization='joesorg') + +``` + +### Using Device Credentials + +Devices must specify their ```uuid``` and ```secret``` as keyword arguments. + +```python +import exp + +exp.start(uuid='[uuid]', secret='[secret]') + ``` +### Using Consumer App Credentials + +Consumer apps must specify their ```uuid``` and ```api_key``` as keyword arguments. + +```python +import exp + +exp.start(uuid='[uuid]', api_key='[api key]') + +``` + +### Pairing Mode + +Advanced users can authenticate in pairing mode by setting ```allow_pairing``` to ```False```. + +```python +import exp + +exp.start(allow_pairing=False) + +``` + + +## Additional Options + +Name | Default | Description +--- | --- | --- +host | ```'https://api.goexp.io/'``` | The api server to authenticate with. +enable_network | ```True``` | Whether to enable real time network communication. If set to ```False``` you will be unable to listen on the ### EXP network [network] ###. + +### Exceptions + +If the SDK is already running an ```exp.RuntimeError``` will be raised. If the arguments specified to ```exp.start``` are invalid an ```exp.OptionsError``` will be raised. + + + # exp.runtime ## exp.start() @@ -63,6 +111,8 @@ device = exp.api.create_device(document) # Create a device from a dictionary ``` Other available namespaces: experiences, locations, content, data. content does not currently support creation, only "get_content(uuid) and find_content(params)". +# [Interacting with API Resources](#api) + ## API Resources Each resource object contains a "document" field which is a dictionary representation of the raw resource, along with "save" and "delete" methods. ```python @@ -94,7 +144,7 @@ The "feed" resource has a ```get_data()``` method that returns a the feed's deco -## The EXP Network +# [Communicating on the EXP Network](#network) The EXP network facilitates real time communication between entities connected to EXP. A user or device can broadcast a JSON serializable payload to users and devices in your organization, and listeners to those broadcasts can respond to the broadcasters. diff --git a/check.py b/check.py index ff2e26e..619a294 100644 --- a/check.py +++ b/check.py @@ -3,9 +3,6 @@ import time if __name__ == '__main__': - print 'B' - print 'A' - print exp.exp - exp.exp.exp.start(username='email@email.com', password='Password12321', organization='scala', host='http://localhost:9000/') - while True: - time.sleep(4) \ No newline at end of file + exp.start(username='email@email.com', password='Password12321', organization='scala', host='http://localhost:9000/') + time.sleep(5) + print 'EXITED CLEANLY' \ No newline at end of file diff --git a/exp/__init__.py b/exp/__init__.py index bad50a4..35f3748 100644 --- a/exp/__init__.py +++ b/exp/__init__.py @@ -1,3 +1,16 @@ """ Main module for Scala EXP SDK """ -from . import exp \ No newline at end of file +import signal +import sys + +from .runtime import runtime as _runtime +from .network import network as _network +from .authenticator import authenticator as _authenticator +from .exceptions import * + +# Terminate the SDK when Ctrl-C is pressed. +signal.signal(signal.SIGINT, lambda signal, frame: sys.exit(1)) + +# Start the SDK. +def start (*args, **kwargs): + return _runtime.start(*args, **kwargs) diff --git a/exp/api/__init__.py b/exp/api/__init__.py index ce7cd10..76b6795 100644 --- a/exp/api/__init__.py +++ b/exp/api/__init__.py @@ -1,13 +1,13 @@ import urllib -from .. lib import api_utils -from .. lib.models.device import Device -from .. lib.models.location import Location -from .. lib.models.experience import Experience -from .. lib.models.content import Content -from .. lib.models.data import Data -from .. lib.models.thing import Thing -from .. lib.models.feed import Feed +#from .. lib import api_utils +#from .. lib.models.device import Device +#from .. lib.models.location import Location +#from .. lib.models.experience import Experience +#from .. lib.models.content import Content +#from .. lib.models.data import Data +#from .. lib.models.thing import Thing +#from .. lib.models.feed import Feed """ Content """ diff --git a/exp/authenticator.py b/exp/authenticator.py new file mode 100644 index 0000000..d60d295 --- /dev/null +++ b/exp/authenticator.py @@ -0,0 +1,97 @@ + +from .logger import logger +from .exceptions import AuthenticationError, UnexpectedError + +class _Authenticator (object): + + def start (self, **options): + pass + + def wait (self): + pass + + @classmethod + def login (cls, **kwargs): + return cls._get_login_response(**kwargs) + + @classmethod + def refresh (cls, **kwargs): + try: + return cls._get_refresh_response(**kwargs) + except AuthenticationError as exception: + logger.warning('Authentication token refresh failed.') + logger.debug(traceback.format_exc()) + return cls._login(**kwargs) + + + def on_update(self, response): + self.auth = response.json() + self.logger.debug('Authentication updated: %s', json.dumps(self.auth, indent=4, separators=(',', ': '))) + network.connect(self.auth) + self.event.set() + time.sleep((self.auth['expiration'] - int(time.time())*1000.0) / 2.0 / 1000.0) + self.refresh() + + @classmethod + def _get_login_response (cls, type=None, host=None, + username=None, password=None, organization=None, + uuid=None, secret=None, api_key=None, allow_pairing=None, **kwargs): + payload = {} + if type is 'user': + logger.debug('Logging in as a user.') + payload['type'] = 'user' + payload['username'] = username + payload['password'] = password + payload['organization'] = organization + elif type is 'device': + logger.debug('Logging in as a device.') + token_payload = {} + token_payload['type'] = 'device' + token_payload['uuid'] = uuid or '_' + token_payload['allowPairing'] = allow_pairing + payload['token'] = self.generate_jwt(token_payload, secret or '_') + elif self.options.get('type') is 'consumer_app': + logger.debug('Logging in as a consumer app.') + token_payload = {} + token_payload['type'] = 'consumerApp' + token_payload['uuid'] = uuid + payload['token'] = self.generate_jwt(token_payload, api_key) + response = requests.request('POST', host + '/api/auth/login', json=payload) + if response.status_code is 200: + logger.debug('Login successful.') + return response.json() + elif response.status_code is 401: + raise exceptions.AuthenticationFailed('Authentication failed.') + else: + raise exceptions.UnexpectedError('Authentication failed due to an unexpected status code: %s.' % response.status_code) + + @classmethod + def _get_refresh_response (cls, token=None, host=None, **kwargs): + headers = { 'Authorization': 'Bearer ' + token } + response = requests.request('POST', host + '/api/auth/token', headers=headers) + if response.status_code is 200: + logger.debug('Authentication token refresh sucessful.') + return response.json() + elif response.status_code is 401: + raise exceptions.AuthenticationFailed('Authentication token refresh failed due to invalid or expired token.') + else: + raise exceptions.UnexpectedError('Authentication token refresh failed due to an unexpected status code: %d.' % response.status_code) + + @staticmethod + def generate_jwt (payload, secret): + + algorithm = { 'alg': 'hs256', 'typ': 'JWT' } + algorithm_json = json.dumps(algorithm, separators(',', ':')).encode('utf-8') + algorithm_b64 = base64.urlsafe_b64encode(algorithm_json) + + payload['exp'] = (int(time.time()) + 30) * 1000 + payload_json = json.dumps(payload, separators(',', ':')).encode('utf-8') + payload_b64 = base64.urlsafe_b64encode(payload_json).rstrip('=') + + signature = hmac.new(secret.encode('utf-8'), '.'.join([algorithm_b64, payload_b64]), hashlib.sha256).digest() + signature_b64 = urlsafe_b64encode(signature).rstrip('=') + + return '.'.join([algorithm_b64, payload_b64, signature_b64]) + + +authenticator = _Authenticator() \ No newline at end of file diff --git a/exp/exceptions.py b/exp/exceptions.py new file mode 100644 index 0000000..efce7c6 --- /dev/null +++ b/exp/exceptions.py @@ -0,0 +1,21 @@ + + +class ExpError (Exception): + pass + + +class AuthenticationError (ExpError): + pass + +class UnexpectedError (ExpError): + pass + + + +# Options to exp.start were invalid or incomplete. +class OptionsError(ExpError): + pass + +# Cannot execute desired action. +class RuntimeError(ExpError): + pass diff --git a/exp/exp.py b/exp/exp.py deleted file mode 100644 index ea0ea8f..0000000 --- a/exp/exp.py +++ /dev/null @@ -1,35 +0,0 @@ -import signal -import sys -from . import network -from .runtime import runtime -import logging -import utils - - - - - - -class Exp (object): - - def start (*args, **kwargs): - - print 'STARTING' - return runtime.start(**kwargs) - - def stop (*args, **kwargs): - return runtime.stop(*args, **kwargs) - - - -#def stop(): -# socket.stop() - -def signal_handler(signal, frame): - print 'GOT SIGING' - sys.exit(0) - -signal.signal(signal.SIGINT, signal_handler) -exp = Exp() - -#signal.pause() diff --git a/exp/logger.py b/exp/logger.py new file mode 100644 index 0000000..8336c51 --- /dev/null +++ b/exp/logger.py @@ -0,0 +1,19 @@ + +import logging +from logging.handlers import RotatingFileHandler + + +_file_handler_formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s') +_file_handler = RotatingFileHandler('debug.log', mode='a', maxBytes=5*1024*1024, backupCount=5, encoding=None, delay=0) +_file_handler.setFormatter(_file_handler_formatter) +_file_handler.setLevel(logging.DEBUG) + +_stream_handler_formatter = logging.Formatter('EXP/%(levelname)-10s: %(message)s') +_stream_handler = logging.StreamHandler() +_stream_handler.setLevel(logging.INFO) +_stream_handler.setFormatter(_stream_handler_formatter) + +logger = logging.getLogger('exp') +logger.addHandler(_file_handler) +logger.addHandler(_stream_handler) +logger.setLevel(logging.DEBUG) \ No newline at end of file diff --git a/exp/network.py b/exp/network.py index cbae795..b680770 100644 --- a/exp/network.py +++ b/exp/network.py @@ -1,70 +1,183 @@ +print 'import bnetwork' + +class Broadcast (object): + + def __init__(self, message): + self._message = message + self._time = int(time.time()) + + @property + def time(self): + return self._time + + @property + def payload(self): + return self._message.get('payload', None) + + def respond(self, payload): + try: + # TODO: Make API call to respond to message. + pass + except Exception as exception: + # TODO: Warn about failure to send response. + # TODO: Rethrow a network exception. + pass + class Listener (object): - pass + + def __init__(self, max_age=60): + self._event = threading.Event() + self._broadcasts = [] + self._max_age = max_age + self._is_active = True + + def receive (self, broadcast): + now = int(time.time()) + self._broadcasts = [broadcast for broadcast in self._broadcasts if now - broadcast.time < self._max_age] + self._broadcasts.append(broadcast) + self._event.set() + + def wait (self, timeout=None): + try: + return self._broadcasts.pop(0) + except IndexError: + self._event.clear() + self._event.wait(timeout) + try: + return self._broadcasts.pop(0) + except IndexError: + return None + + @property + def is_active(self): + return self._is_active + + def cancel(self): + self._is_active = False + + +class Namespace (object): + + def __init__(self): + self._listeners = {} + + def cancel (self, id): + del self._listeners[id] + + def listen (self): + id = str(uuid.uuid4()) + listener = Listener(id, self) + self._listeners.append(listener) + return listener + + @property + def has_active_listeners(self): + return any([listener.is_active for id, listener in self._listeners.iteritems()]) + class Channel (object): - pass + + def __init__(self, id): + self._id = id + self._namespaces = {} + + def broadcast (self, name, payload): + # Emit and return API response. + pass + + def listen (self, name): + + return self.get_channel(channel_id).listen(name) + + @property + def has_listeners (self): + return all([namepsace.has_listeners for key, namepsace in self._namespaces.iteritems()]) class Subscription (object): pass +class SocketHandler (object): + pass -class Network (object): - def __init__(self): - self.channels = {} - self.subscriptions = {} - self.socket = None +import urlparse +from socketIO_client import SocketIO, BaseNamespace - @property - def isConnected (self): - return self.socket not None +"""class SocketNamespace(BaseNamespace): + def on_message(self, message): + print 'BRO' - def connect (self, auth): - self.disconnect() - self.auth = auth - # Establish socket connection. + def on_connect(self): + print 'CON' - def disconnect (self): - if self.socket: - self.socket.disconnect() - self.socket = None + def on_disconnect(self): + print 'DIS' + + def on_subscribed(self, message): + print 'SUB' - def broadcast (self, message): + def on_channels(self, message): + print 'CHAN'""" + +class Network (object): + + def __init__(self): + self._socket = None + self._is_connecting = False + + def on_broadcast (self, message): + if not isinstance(message, dict): + # TODO: Warning about invalid incoming message. + return + channel = self.channels.get(message.get('channel')) + if not channel: + # TODO: Log debug message about dropped message. + return + channel.receive(broadcast) + + def on_channels (self, ids): pass - def listen (self): + def on_subscribed (self, ids): pass - def get_channel (self): + def start (self, **options): pass + def wait (self): + pass + + @property + def isConnected (self): + return self._socket is not None - def digest (self): - if not self.socket: - if self.auth: - pass - else: - if self.outoing + def connect (self, auth): + self._auth = auth + self._disconnect() + self._is_connecting = True - if self.auth and not self.socket: - try: - self.socket = None ## Create socket - except Exception: - return + def disconnect (self): + if not self._socket: + return + self._socket.disconnect() + self._socket = None + def _attempt_connect(self): + #parsed_host = urlparse.urlparse(self._auth.get('network', {}).get('host')) + #self._socket = SocketIO(parsed_host.hostname, parsed_host.port, params={ "token": self._auth.get('token') }, Namespace=SocketNamespace, hurry_interval_in_seconds=10) + print 'ESTABLISHED SOCKET CONNECTION' - # Connect if auth has been updated - # Disconnect if we are to terminate. - # Send outgoing messages - # Receive and route incoming messages to channels + def digest (self): + if self._is_connecting: + return self._attempt_connect() network = Network() diff --git a/exp/lib/__init__.py b/exp/old/__init__.py similarity index 100% rename from exp/lib/__init__.py rename to exp/old/__init__.py diff --git a/exp/lib/api_utils.py b/exp/old/api_utils.py similarity index 100% rename from exp/lib/api_utils.py rename to exp/old/api_utils.py diff --git a/exp/lib/channel.py b/exp/old/channel.py similarity index 100% rename from exp/lib/channel.py rename to exp/old/channel.py diff --git a/exp/lib/config.py b/exp/old/config.py similarity index 100% rename from exp/lib/config.py rename to exp/old/config.py diff --git a/exp/lib/credentials.py b/exp/old/credentials.py similarity index 100% rename from exp/lib/credentials.py rename to exp/old/credentials.py diff --git a/exp/lib/event_node.py b/exp/old/event_node.py similarity index 100% rename from exp/lib/event_node.py rename to exp/old/event_node.py diff --git a/exp/lib/models/__init__.py b/exp/old/models/__init__.py similarity index 100% rename from exp/lib/models/__init__.py rename to exp/old/models/__init__.py diff --git a/exp/lib/models/content.py b/exp/old/models/content.py similarity index 100% rename from exp/lib/models/content.py rename to exp/old/models/content.py diff --git a/exp/lib/models/data.py b/exp/old/models/data.py similarity index 100% rename from exp/lib/models/data.py rename to exp/old/models/data.py diff --git a/exp/lib/models/device.py b/exp/old/models/device.py similarity index 100% rename from exp/lib/models/device.py rename to exp/old/models/device.py diff --git a/exp/lib/models/experience.py b/exp/old/models/experience.py similarity index 100% rename from exp/lib/models/experience.py rename to exp/old/models/experience.py diff --git a/exp/lib/models/feed.py b/exp/old/models/feed.py similarity index 100% rename from exp/lib/models/feed.py rename to exp/old/models/feed.py diff --git a/exp/lib/models/location.py b/exp/old/models/location.py similarity index 100% rename from exp/lib/models/location.py rename to exp/old/models/location.py diff --git a/exp/lib/models/thing.py b/exp/old/models/thing.py similarity index 100% rename from exp/lib/models/thing.py rename to exp/old/models/thing.py diff --git a/exp/lib/network/__init__.py b/exp/old/network/__init__.py similarity index 100% rename from exp/lib/network/__init__.py rename to exp/old/network/__init__.py diff --git a/exp/lib/network/channel.py b/exp/old/network/channel.py similarity index 100% rename from exp/lib/network/channel.py rename to exp/old/network/channel.py diff --git a/exp/lib/network/listener.py b/exp/old/network/listener.py similarity index 100% rename from exp/lib/network/listener.py rename to exp/old/network/listener.py diff --git a/exp/lib/network/network.py b/exp/old/network/network.py similarity index 100% rename from exp/lib/network/network.py rename to exp/old/network/network.py diff --git a/exp/lib/socket.py b/exp/old/socket.py similarity index 100% rename from exp/lib/socket.py rename to exp/old/socket.py diff --git a/exp/runtime.py b/exp/runtime.py index 21ce273..184e22b 100644 --- a/exp/runtime.py +++ b/exp/runtime.py @@ -1,5 +1,4 @@ -import sys -import sys + import time import json import hmac @@ -7,180 +6,58 @@ import hashlib import requests import threading -from . import network - -import logging - -from logging.handlers import RotatingFileHandler - +from .network import network +from .authenticator import authenticator +from .exceptions import RuntimeError, OptionsError - -class RuntimeException (Exception): - pass - -class Runtime (object): +class _Runtime (object): def __init__ (self): - - self.is_started = False - self.auth = None - self.event = threading.Event() - self.exception = None - - def configure_logging (self, **kwargs): - - logger = logging.getLogger('exp') - - file_handler_formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s') - file_handler = RotatingFileHandler('debug.log', mode='a', maxBytes=5*1024*1024, backupCount=5, encoding=None, delay=0) - file_handler.setFormatter(file_handler_formatter) - file_handler.setLevel(logging.DEBUG) - logger.addHandler(file_handler) - - stream_handler_formatter = logging.Formatter('EXP/%(levelname)-10s: %(message)s') - stream_handler = logging.StreamHandler() - stream_handler.setLevel(logging.INFO) - stream_handler.setFormatter(stream_handler_formatter) - logger.addHandler(stream_handler) - - logger.setLevel(logging.DEBUG) - self.logger = logger - - - def start (self, enable_events=True, host='https://api.goexp.io/', **kwargs): - self.configure_logging(**kwargs) - self.logger.info('Starting runtime.') - if self.is_started: - raise RuntimeException('Runtime already started.') - self.is_started = True - - self.options = kwargs - self.options['host'] = host - self.options['enable_events'] = enable_events - - if not self.options.get('port'): - if self.options.get('host').startswith('http:'): - self.options['port'] = 80 - else: - self.options['port'] = 443 - - thread = threading.Thread(target=lambda: self.fork()) - thread.daemon = True - thread.start() - self.event.wait() - - if self.exception: - raise self.exception - self.logger.info('Connected to EXP.') - - def fork (self): - try: - self.validate() - self.login() - except Exception as exception: - self.logger.critical('Runtime aborted due to error: %s', exception) - self.exception = exception - self.event.set() - - def validate (self): - self.logger.debug('Validating runtime options: %s', self.options) - if self.options.get('type') is 'user' or self.options.get('username') or self.options.get('password') or self.options.get('organization'): - self.logger.debug('Validating user credentials.') - self.options['type'] = 'user' - if not self.options.get('username'): - raise RuntimeException('Please specify the username.') - if not self.options.get('password'): - raise RuntimeException('Please specify the password.') - if not self.options.get('organization'): - raise RuntimeException('Please specify the organization.') - elif self.options.get('type') is 'device' or self.options.get('secret'): - self.logger.debug('Validating device credentials.') - self.options['type'] = 'device' - if not self.options.get('uuid') and not self.options.get('allow_pairing'): - raise RuntimeException('Please specify the device uuid.') - if not self.options.get('secret') and not self.options.get('allow_pairing'): - raise RuntimeException('Please specify the device secret.') - elif self.options.get('type') is 'consumer_app' or self.options.get('api_key'): - self.logger.debug('Validating consumer app credentials.') - self.options['type'] = 'consumer_app' - if not self.options.get('uuid'): - raise RuntimeException('Please specify the consumer app uuid.') - if not self.options.get('api_key'): - raise RuntimeException('Please specify the consumer app api key.') + self._is_started = False + + def start (self, enable_events=True, host='https://api.goexp.io/', **options): + + options['host'] = host + options['enable_events'] = enable_events + + if options.get('type') is 'user' or ((options.get('username') or options.get('password') or options.get('organization')) and not options.get('type')): + options['type'] = 'user' + if not options.get('username'): + raise OptionsError('Please specify the username.') + if not options.get('password'): + raise OptionsError('Please specify the password.') + if not options.get('organization'): + raise OptionsError('Please specify the organization.') + elif options.get('type') is 'device' or ((options.get('secret') or options.get('allow_pairing')) and not options.get('type')): + options['type'] = 'device' + if not options.get('uuid') and not options.get('allow_pairing'): + raise OptionsError('Please specify the device uuid.') + if not options.get('secret') and not options.get('allow_pairing'): + raise OptionsError('Please specify the device secret.') + elif options.get('type') is 'consumer_app' or (options.get('api_key') and not options.get('type')): + options['type'] = 'consumer_app' + if not options.get('uuid'): + raise OptionsError('Please specify the consumer app uuid.') + if not options.get('api_key'): + raise OptionsError('Please specify the consumer app api key.') else: - raise RuntimeException('Please specify authentication type.') - - - def login (self): - payload = {} - if self.options.get('type') is 'user': - self.logger.debug('Authenticating as a user.') - payload['type'] = 'user' - payload['username'] = self.options.get('username') - payload['password'] = self.options.get('password') - payload['organization'] = self.options.get('organization') - elif self.options.get('type') is 'device': - self.logger.debug('Authenticating as a device.') - token_payload = {} - token_payload['type'] = 'device' - token_payload['uuid'] = self.options.get('uuid', '_') - token_payload['allowPairing'] = self.options.get('allow_paiing') - payload['token'] = self.generate_jwt(token_payload, self.options.get('secret', '_')) - elif self.options.get('type') is 'consumer_app': - self.logger.debug('Authenticating as a consumer app.') - token_payload = {} - token_payload['type'] = 'consumerApp' - token_payload['uuid'] = self.options.get('uuid', '_') - payload['token'] = self.generate_jwt(token_payload, self.options.get('apiKey', '_')) - response = requests.request('POST', self.options.get('host') + '/api/auth/login', json=payload) - if response.status_code is 401: - self.logger.critical('Credentials invalid. Runtime cannot continue.') - raise RuntimeException('Invalid credentials.') - elif response.status_code is 200: - self.logger.debug('Login successful.') - self.on_update(response) - else: - self.logger.warning('Unknown error trying to login. Retrying in five seconds.') - time.sleep(5) - self.login() - - def refresh (self): - self.logger.debug('Refresing authentication token.') - headers = { 'Authorization': 'Bearer ' + self.auth['token'] } - response = requests.request('POST', self.options.get('host') + '/api/auth/token', headers=headers) - if response.status_code is 401: - self.logger.warning('Token refresh failed due to invalid authentication.') - self.login() - elif response.status_code is 200: - self.logger.debug('Token refresh successful.') - self.on_update(response) - else: - self.logger.warning('Token refresh failed for unknown reason.') - time.sleep(5) - self.refresh() - - def on_update(self, response): - self.auth = response.json() - self.logger.debug('Authentication updated: %s', json.dumps(self.auth, indent=4, separators=(',', ': '))) - self.event.set() - time.sleep((self.auth['expiration'] - int(time.time())*1000.0) / 2.0 / 1000.0) - self.refresh() - - @staticmethod - def generate_jwt (payload, secret): + raise OptionsError('Please specify authentication type.') - algorithm = { 'alg': 'hs256', 'typ': 'JWT' } - algorithm_json = json.dumps(algorithm, separators(',', ':')).encode('utf-8') - algorithm_b64 = base64.urlsafe_b64encode(algorithm_json) + if self._is_started: + raise RuntimeError('Runtime already started.') + self._is_started = True - payload['exp'] = (int(time.time()) + 30) * 1000 - payload_json = json.dumps(payload, separators(',', ':')).encode('utf-8') - payload_b64 = base64.urlsafe_b64encode(payload_json).rstrip('=') + authenticator_thread = threading.Thread(target=lambda: authenticator.start(**options)) + authenticator_thread.daemon = True + authenticator_thread.start() + authenticator.wait() - signature = hmac.new(secret.encode('utf-8'), '.'.join([algorithm_b64, payload_b64]), hashlib.sha256).digest() - signature_b64 = urlsafe_b64encode(signature).rstrip('=') + if enable_events: + network_thread = threading.Thread(target=lambda: network.start(**options)) + network_thread.daemon = True + network_thread.start() + network.wait() - return '.'.join([algorithm_b64, payload_b64, signature_b64]) -runtime = Runtime() \ No newline at end of file +runtime = _Runtime() diff --git a/makes.py b/makes.py deleted file mode 100644 index 6372306..0000000 --- a/makes.py +++ /dev/null @@ -1,61 +0,0 @@ -import exp - -exp.runtime.start(username="email@email.com", password="Password12321", organization="scala", host="https://api-develop.exp.scala.com/", port=443) -""" -# experiences = scala.api.get_experiences() -# experience = experiences[1]; -# document = experience.document -# dayplan = document["dayPlans"][0] -# dayplan["blocks"] = []; -# for i in range(86400 / 60): -# dayplan["blocks"].append({ -# "appKey": "default", -# "startTime": i * 60 * 1000, -# "endTime": i * 60 * 1000 + 10 * 1000 -# }) -# dayplan["blocks"].append({ -# "appKey": "default", -# "startTime": i * 60 * 1000 + 20 * 1000, -# "endTime": i * 60 * 1000 + 30 * 1000 -# }) -# print dayplan["blocks"][34] -# experience.save() - - -experiences = exp.api.find_experiences() -print experiences - -exp.api.create_data(key="fluffy", group="cats", value={ "awesome": 3}) - -data = exp.api.get_data('fluffy', 'cats') -data.value = { "test": "cat" } -data.save() - -contents = exp.api.get_content('root') -children = contents.get_children() -for child in children: - try: - print child.get_url() - except Exception as e: - print e - -exp.runtime.stop() - -""" -import time - -def cb(tmp): - print "I got a message!!!!!!" - print "I _________________________________________" - -print "Started!" -print "Sending!" -exp.channels.organization.listen(name="hello", callback=cb) -time.sleep(2) -exp.channels.organization.broadcast(name="hello") -print "hi" -time.sleep(3) -print "Stopping EXP runtime..." -exp.runtime.stop() -print "Stopped!" - diff --git a/setup.py b/setup.py index c8e95ee..ccdb496 100644 --- a/setup.py +++ b/setup.py @@ -2,9 +2,8 @@ from setuptools import setup, find_packages setup( - name="scala-sdk", + name="exp", version="0.0.0", packages=find_packages(), install_requires=["requests", "socketIO_client"] ) - diff --git a/exp/utils.py b/tests/__init__.py similarity index 100% rename from exp/utils.py rename to tests/__init__.py diff --git a/tests/runtime_start_test.py b/tests/runtime_start_test.py new file mode 100644 index 0000000..02d931c --- /dev/null +++ b/tests/runtime_start_test.py @@ -0,0 +1,233 @@ + + +import unittest +import exp + +class TestException (Exception): + pass + + +def fail_stub(): + raise TestException() + +def noop(*args, **kwargs): + pass + +original_network_start = exp._network.start +original_network_wait = exp._network.wait +original_authenticator_start = exp._authenticator.start +original_authenticator_wait = exp._authenticator.wait + + + +class Base(object): + + type_ = None + + username = None + password = None + organization = None + + uuid = None + secret = None + api_key = None + + allow_pairing = None + enable_events = True + + did_network_wait = False + did_authenticator_wait = False + + def on_network_wait (self): + self.did_network_wait = True + + def on_authenticator_wait(self): + self.did_authenticator_wait = True + + def setUp (self): + self.stub() + + def tearDown (self): + exp._runtime._is_started = False + self.unstub() + + def stub (self): + exp._network.start = noop + exp._network.wait = lambda: self.on_network_wait() + exp._authenticator.start = noop + exp._authenticator.wait = lambda: self.on_authenticator_wait() + + def unstub (self): + exp._network.start = original_network_start + exp._network.wait = original_network_wait + exp._authenticator.start = original_authenticator_start + exp._authenticator.wait = original_authenticator_wait + + def start (self): + return exp.start(type=self.type_, + username=self.username, password=self.password, organization=self.organization, + uuid=self.uuid, secret=self.secret, api_key=self.api_key, + allow_pairing=self.allow_pairing, enable_events=self.enable_events) + + +class ErrorBase(Base): + + exception = Exception + + def test (self): + self.assertRaises(self.exception, lambda: self.start()) + + +class RuntimeErrorBase(ErrorBase): + + exception = exp.RuntimeError + + +class OptionsErrorBase(ErrorBase): + + exception = exp.OptionsError + + +class StartTwice (RuntimeErrorBase, unittest.TestCase): + + username = '_' + password = '_' + organization = '_' + + def test (self): + try: + self.start() + except: + pass + super(StartTwice, self).test() + + +class NoOptions (OptionsErrorBase, unittest.TestCase): + pass + + +class NoUsername (OptionsErrorBase, unittest.TestCase): + password = '_' + organization = '_' + + +class NoPassword (OptionsErrorBase, unittest.TestCase): + username = '_' + organization = '_' + + +class NoOrganization (OptionsErrorBase, unittest.TestCase): + username = '_' + password = '_' + + +class NoDeviceUuid (OptionsErrorBase, unittest.TestCase): + secret = '_' + + +class NoConsumerAppUuid (OptionsErrorBase, unittest.TestCase): + api_key = '_' + + +class NoDeviceSecret (OptionsErrorBase, unittest.TestCase): + type_ = 'device' + uuid = '_' + +class NoConsumerAppApiKey (OptionsErrorBase, unittest.TestCase): + type_ = 'consumer_app' + uuid = '_' + +class NoSecretOrApiKey (OptionsErrorBase, unittest.TestCase): + uuid = '_' + + +class SuccessBase (Base): + + should_network_start = True + + def test (self): + self.start() + self.assertEquals(self.did_authenticator_wait, True) + self.assertEquals(self.did_network_wait, self.should_network_start) + +class UserCredentials (SuccessBase, unittest.TestCase): + username = '_' + password = '_' + organization = '_' + +class UserTypedCredentials (SuccessBase, unittest.TestCase): + type_ = 'user' + username = '_' + password = '_' + organization = '_' + +class DeviceCredentials (SuccessBase, unittest.TestCase): + uuid = '_' + secret = '_' + +class DeviceTypedCredentials (SuccessBase, unittest.TestCase): + type_ = 'device' + uuid = '_' + secret = '_' + +class PairingCredentials (SuccessBase, unittest.TestCase): + allow_pairing = True + +class ConsumerAppCredentials (SuccessBase, unittest.TestCase): + uuid = '_' + api_key = '_' + +class ConsumerAppTypedCredentials (SuccessBase, unittest.TestCase): + type_ = 'consumer_app' + uuid = '_' + api_key = '_' + +""" + +class RuntimeExceptionsTestCase(BaseTestCase): + + def setUp (self): + exp._network.start = noop + exp._network.wait = noop + exp._authenticator.start = noop + exp._authenticator.wait = noop + + def tearDown (self): + exp._runtime._is_started = False + unstub() + + def test_start_with_no_options(self): + self.assertRaises(exp.OptionsError, start_with_no_options) + + def test_double_start (self): + self.assertRaises(exp.OptionsError, start_with_no_options) + self.assertRaises(exp.RuntimeError, start_with_no_options) + + def test_start_with_no_username(self): + self.assertRaises(exp.OptionsError, start_with_no_username) + + def test_start_with_no_password(self): + self.assertRaises(exp.OptionsError, start_with_no_password) + + def test_start_with_no_organization(self): + self.assertRaises(exp.OptionsError, start_with_no_password) + + def test_start_as_user (self): + start_as_user() + + def test_starts_network (self): + pass + + def test_starts_runtime (self): + pass + +class RuntimeSuccessTestCase(unittest.TestCase): + + def tearDown (self): + exp._runtime._is_started = False + + +""" + +if __name__ == '__main__': + unittest.main() diff --git a/tests/test0.py b/tests/test0.py deleted file mode 100644 index 21eae2f..0000000 --- a/tests/test0.py +++ /dev/null @@ -1,10 +0,0 @@ -import unittest -import exp - -class TestBoilerplate(unittest.TestCase): - - def test_something(self): - self.assertEqual(0, 0) - -if __name__ == '__main__': - unittest.main() From 123d3dc1cf32df85f80e67c68a136a9587e9189f Mon Sep 17 00:00:00 2001 From: James Dalessio Date: Mon, 29 Feb 2016 00:41:02 -0500 Subject: [PATCH 017/104] Testing linking --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 042a91f..7ef6fe3 100644 --- a/README.md +++ b/README.md @@ -59,7 +59,7 @@ exp.start(allow_pairing=False) Name | Default | Description --- | --- | --- host | ```'https://api.goexp.io/'``` | The api server to authenticate with. -enable_network | ```True``` | Whether to enable real time network communication. If set to ```False``` you will be unable to listen on the ### EXP network [network] ###. +enable_network | ```True``` | Whether to enable real time network communication. If set to ```False``` you will be unable to listen on the [EXP network](#network). ### Exceptions From e7d0f4eedad3a31aa713bab42455098d366edfb5 Mon Sep 17 00:00:00 2001 From: James Dalessio Date: Mon, 29 Feb 2016 00:43:34 -0500 Subject: [PATCH 018/104] Testing linking 2 --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 7ef6fe3..aafbf68 100644 --- a/README.md +++ b/README.md @@ -59,7 +59,7 @@ exp.start(allow_pairing=False) Name | Default | Description --- | --- | --- host | ```'https://api.goexp.io/'``` | The api server to authenticate with. -enable_network | ```True``` | Whether to enable real time network communication. If set to ```False``` you will be unable to listen on the [EXP network](#network). +enable_network | ```True``` | Whether to enable real time network communication. If set to ```False``` you will be unable to listen on the [EXP network](# Communicating on the EXP Network). ### Exceptions From cf70ecb7f13958a9955a0420fc5c5ef0a708a9f2 Mon Sep 17 00:00:00 2001 From: James Dalessio Date: Mon, 29 Feb 2016 00:46:58 -0500 Subject: [PATCH 019/104] Working on readme. --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 042a91f..c5494bd 100644 --- a/README.md +++ b/README.md @@ -111,7 +111,7 @@ device = exp.api.create_device(document) # Create a device from a dictionary ``` Other available namespaces: experiences, locations, content, data. content does not currently support creation, only "get_content(uuid) and find_content(params)". -# [Interacting with API Resources](#api) +# Interacting with API Resources ## API Resources Each resource object contains a "document" field which is a dictionary representation of the raw resource, along with "save" and "delete" methods. @@ -144,7 +144,7 @@ The "feed" resource has a ```get_data()``` method that returns a the feed's deco -# [Communicating on the EXP Network](#network) +# Communicating on the EXP Network The EXP network facilitates real time communication between entities connected to EXP. A user or device can broadcast a JSON serializable payload to users and devices in your organization, and listeners to those broadcasts can respond to the broadcasters. From 57a1e1f961c194065acdbcbc8cb383bc0c535deb Mon Sep 17 00:00:00 2001 From: James Dalessio Date: Mon, 29 Feb 2016 15:27:51 -0500 Subject: [PATCH 020/104] wip --- check.py | 6 +- exp/__init__.py | 15 +++- exp/authenticator.py | 169 ++++++++++++++++++++++++----------- exp/exceptions.py | 3 +- exp/lib/network/Channel.py | 32 +++++++ exp/lib/network/Listener.py | 12 +++ exp/lib/network/Namespace.py | 22 +++++ exp/lib/network/Network.py | 13 +++ exp/network.py | 81 ++++++++--------- exp/runtime.py | 14 ++- test2.py | 23 +++++ 11 files changed, 283 insertions(+), 107 deletions(-) create mode 100644 exp/lib/network/Channel.py create mode 100644 exp/lib/network/Listener.py create mode 100644 exp/lib/network/Namespace.py create mode 100644 exp/lib/network/Network.py create mode 100644 test2.py diff --git a/check.py b/check.py index 619a294..d80f609 100644 --- a/check.py +++ b/check.py @@ -3,6 +3,8 @@ import time if __name__ == '__main__': - exp.start(username='email@email.com', password='Password12321', organization='scala', host='http://localhost:9000/') + exp.start( + username='email@email.com', password='Password12321', + organization='scala', host='http://localhost:9000/') time.sleep(5) - print 'EXITED CLEANLY' \ No newline at end of file + exp.stop() \ No newline at end of file diff --git a/exp/__init__.py b/exp/__init__.py index 35f3748..4a3c5f4 100644 --- a/exp/__init__.py +++ b/exp/__init__.py @@ -8,9 +8,18 @@ from .authenticator import authenticator as _authenticator from .exceptions import * -# Terminate the SDK when Ctrl-C is pressed. -signal.signal(signal.SIGINT, lambda signal, frame: sys.exit(1)) - # Start the SDK. def start (*args, **kwargs): return _runtime.start(*args, **kwargs) + +def stop (): + _runtime.stop() + return sys.exit(1) + +def get_auth (): + return _authenticator.get_auth() + + +# Terminate the SDK when Ctrl-C is pressed. +signal.signal(signal.SIGINT, lambda signal, frame: stop()) + diff --git a/exp/authenticator.py b/exp/authenticator.py index d60d295..b2b3cf9 100644 --- a/exp/authenticator.py +++ b/exp/authenticator.py @@ -1,81 +1,144 @@ +import time +import traceback +import requests from .logger import logger -from .exceptions import AuthenticationError, UnexpectedError +from .exceptions import AuthenticationError, UnexpectedError, NotAuthenticatedError class _Authenticator (object): + def __init__(self): + self._auth = None + self._do_stop = False + self._exception = None + self._options = None + + def get_auth(self): + if not self._auth: + raise NotAuthenticatedError() + return self._auth + + def start (self, **options): - pass + self._options = options + self._main_event_loop() + + + def stop (self): + self._do_stop = True + def wait (self): - pass + while not self._auth and not self._do_stop and not self._exception: + time.sleep(.5) + if self._exception: + raise self._exception + + + def _main_event_loop (self): + while not self._do_stop: + if self._auth and self._is_refresh_needed: + self._auth = self._refresh() + elif not self._auth: + self._auth = self._login() + time.sleep(1) + + @property + def _is_refresh_needed (self): + return self._auth.get('expiration') / 1000.0 - int(time.time()) < 3600 - @classmethod - def login (cls, **kwargs): - return cls._get_login_response(**kwargs) - @classmethod - def refresh (cls, **kwargs): + def _login (self): + logger.info('Logging into EXP.') try: - return cls._get_refresh_response(**kwargs) + auth = self._get_login_response() except AuthenticationError as exception: + logger.critical('Authentication failed. Credentials are invalid.') + self._exception = exception + except Exception as exception: + logger.warning('Authentication failed for unexpected reason. See debug log for more information.') + logger.debug(traceback.format_exc()) + else: + logger.info('Login successful.') + return auth + + def _refresh (self): + logger.info('Refreshing authentication token.') + try: + auth = self._get_refresh_response() + except Exception as exception: logger.warning('Authentication token refresh failed.') logger.debug(traceback.format_exc()) - return cls._login(**kwargs) + else: + logger.info('Authentication token refresh successful.') + return auth + + def _get_login_payload (self): + if self._options.get('type') is 'user': + return self._get_user_login_payload() + elif self._options.get('type') is 'device': + return self._get_device_login_payload() + elif self._options.get('type') is 'consumer_app': + return self._get_consumer_app_login_payload() - def on_update(self, response): - self.auth = response.json() - self.logger.debug('Authentication updated: %s', json.dumps(self.auth, indent=4, separators=(',', ': '))) - network.connect(self.auth) - self.event.set() - time.sleep((self.auth['expiration'] - int(time.time())*1000.0) / 2.0 / 1000.0) - self.refresh() - @classmethod - def _get_login_response (cls, type=None, host=None, - username=None, password=None, organization=None, - uuid=None, secret=None, api_key=None, allow_pairing=None, **kwargs): + def _get_user_login_payload (self): payload = {} - if type is 'user': - logger.debug('Logging in as a user.') - payload['type'] = 'user' - payload['username'] = username - payload['password'] = password - payload['organization'] = organization - elif type is 'device': - logger.debug('Logging in as a device.') - token_payload = {} - token_payload['type'] = 'device' - token_payload['uuid'] = uuid or '_' - token_payload['allowPairing'] = allow_pairing - payload['token'] = self.generate_jwt(token_payload, secret or '_') - elif self.options.get('type') is 'consumer_app': - logger.debug('Logging in as a consumer app.') - token_payload = {} - token_payload['type'] = 'consumerApp' - token_payload['uuid'] = uuid - payload['token'] = self.generate_jwt(token_payload, api_key) - response = requests.request('POST', host + '/api/auth/login', json=payload) - if response.status_code is 200: - logger.debug('Login successful.') + payload['username'] = self._options.get('username') + payload['password'] = self._options.get('password') + payload['organization'] = self._options.get('organization') + return payload + + + def _get_device_login_payload (self): + payload = {} + token_payload = {} + token_payload['uuid'] = self._options.get('uuid') or '_' + token_payload['allowPairing'] = self._options.get('allow_pairing') + payload['token'] = self.generate_jwt(token_payload, self._options.get('secret') or '_') + return payload + + + def _get_consumer_app_login_payload (self): + payload = {} + token_payload = {} + token_payload['uuid'] = self._options.get('uuid') or '_' + payload['token'] = self.generate_jwt(token_payload, self._options.get('api_key')) + return payload + + + def _get_login_url (self): + return self._options.get('host') + '/api/auth/login' + + + def _get_refresh_url (self): + return self._options.get('host') + '/api/auth/token' + + + def _get_login_response (self): + response = requests.request('POST', self._get_login_url(), json=self._get_login_payload()) + if response.status_code == 200: return response.json() - elif response.status_code is 401: - raise exceptions.AuthenticationFailed('Authentication failed.') + elif response.status_code == 401: + raise AuthenticationError('Authentiation failed.') else: - raise exceptions.UnexpectedError('Authentication failed due to an unexpected status code: %s.' % response.status_code) + raise UnexpectedError('Authentication failed due to an unexpected status code: %s.' % response.status_code) - @classmethod - def _get_refresh_response (cls, token=None, host=None, **kwargs): - headers = { 'Authorization': 'Bearer ' + token } - response = requests.request('POST', host + '/api/auth/token', headers=headers) + + def _get_refresh_headers (self): + return { 'Authorization': 'Bearer ' + self._auth.get('token') } + + + def _get_refresh_response (self): + response = requests.request('POST', self._get_refresh_url(), headers=self._get_refresh_headers()) if response.status_code is 200: - logger.debug('Authentication token refresh sucessful.') return response.json() elif response.status_code is 401: - raise exceptions.AuthenticationFailed('Authentication token refresh failed due to invalid or expired token.') + raise AuthenticationError('Authentication failed.') else: - raise exceptions.UnexpectedError('Authentication token refresh failed due to an unexpected status code: %d.' % response.status_code) + raise UnexpectedError('Authentication token refresh failed due to an unexpected status code: %d.' % response.status_code) + @staticmethod def generate_jwt (payload, secret): diff --git a/exp/exceptions.py b/exp/exceptions.py index efce7c6..150c93e 100644 --- a/exp/exceptions.py +++ b/exp/exceptions.py @@ -10,7 +10,8 @@ class AuthenticationError (ExpError): class UnexpectedError (ExpError): pass - +class NotAuthenticatedError (ExpError): + pass # Options to exp.start were invalid or incomplete. class OptionsError(ExpError): diff --git a/exp/lib/network/Channel.py b/exp/lib/network/Channel.py new file mode 100644 index 0000000..3f9f73c --- /dev/null +++ b/exp/lib/network/Channel.py @@ -0,0 +1,32 @@ + +class Channel: + + def __init__(self, id): + this._id = id + this._namespaces = {} + + def recieve (self, message): + namespace = this._namespaces.get(message.get('name')) + if not namespace: return + return namespace.receive(message) + + def broadcast (self, **kwargs): + kwargs['id'] = self._id + return Network.broadcast(**kwargs) + + def listen (self, name): + Network._subscribe(self._id) + if not self._namespaces[name]: + self._namespaces[name] = Namespace() + return self._namespaces[name].listen() + + def _subscribe (self): + return Network.subscribe(self._id) + + def _unsubscribe (self): + return Network.unsubscribe(self._id) + + def _unlisten (self, namespace): + self._namespaces.pop(namespace, None) + if not bool(self._namespaces): + self._unsubscribe() diff --git a/exp/lib/network/Listener.py b/exp/lib/network/Listener.py new file mode 100644 index 0000000..ea4a676 --- /dev/null +++ b/exp/lib/network/Listener.py @@ -0,0 +1,12 @@ + +class Listener: + + def __init__(self, namespace): + this._namespace = namespace + this._messages = [] + + def wait (message): + pass + + def cancel (self): + pass diff --git a/exp/lib/network/Namespace.py b/exp/lib/network/Namespace.py new file mode 100644 index 0000000..3a7ed3f --- /dev/null +++ b/exp/lib/network/Namespace.py @@ -0,0 +1,22 @@ + +class Namespace: + + def __init__(self, channel): + this._listeners = [] + this._channel = channel + + def unlisten (self, listener): + try: + this._listeners.remove(listener) + except ValueError: + pass + if not this._listeners: + this._channel.unlisten(self) + + def listen (self): + this._listeners.append(Listener()) + return this._listeners[-1] + + def receive (message): + return [listener.receive(message) for listener in this._listeners] + diff --git a/exp/lib/network/Network.py b/exp/lib/network/Network.py new file mode 100644 index 0000000..46e6bd9 --- /dev/null +++ b/exp/lib/network/Network.py @@ -0,0 +1,13 @@ + + +class Network (object): + + def __init__(self): + pass + + + def broadcast (message): + pass + + def listen(): + pass diff --git a/exp/network.py b/exp/network.py index b680770..62c923a 100644 --- a/exp/network.py +++ b/exp/network.py @@ -1,5 +1,7 @@ +import time -print 'import bnetwork' +from .authenticator import authenticator +from .exceptions import NotAuthenticatedError class Broadcast (object): @@ -128,56 +130,45 @@ def on_channels(self, message): class Network (object): def __init__(self): + self._options = None self._socket = None - self._is_connecting = False - - def on_broadcast (self, message): - if not isinstance(message, dict): - # TODO: Warning about invalid incoming message. - return - channel = self.channels.get(message.get('channel')) - if not channel: - # TODO: Log debug message about dropped message. - return - channel.receive(broadcast) - - def on_channels (self, ids): - pass - - def on_subscribed (self, ids): - pass + self._is_connected = False + self._auth = None + self._do_stop = False def start (self, **options): - pass + self._options = options + self._main_event_loop() def wait (self): - pass - - @property - def isConnected (self): - return self._socket is not None - - - def connect (self, auth): - self._auth = auth - self._disconnect() - self._is_connecting = True - - def disconnect (self): - if not self._socket: - return - self._socket.disconnect() - self._socket = None - - - def _attempt_connect(self): - #parsed_host = urlparse.urlparse(self._auth.get('network', {}).get('host')) - #self._socket = SocketIO(parsed_host.hostname, parsed_host.port, params={ "token": self._auth.get('token') }, Namespace=SocketNamespace, hurry_interval_in_seconds=10) - print 'ESTABLISHED SOCKET CONNECTION' + while not self._is_connected: + time.sleep(1) + + def _main_event_loop (self): + while not self._do_stop: + try: + auth = authenticator.get_auth() + except NotAuthenticatedError: + auth = None + return + if auth != self._auth: + self._auth = auth + if self._socket: + self._socket.disconnect() + parsed_host = urlparse.urlparse(self._auth.get('network', {}).get('host')) + self._socket = SocketIO(parsed_host.hostname, parsed_host.port, params={ "token": self._auth.get('token') }, Namespace=SocketNamespace, hurry_interval_in_seconds=10) + if self._socket: + self._socket.wait_for_callbacks(1) + else: + time.sleep(1) + + def stop (self): + self._do_stop = True + + + def _reconnect(self): + - def digest (self): - if self._is_connecting: - return self._attempt_connect() network = Network() diff --git a/exp/runtime.py b/exp/runtime.py index 184e22b..89e5383 100644 --- a/exp/runtime.py +++ b/exp/runtime.py @@ -7,6 +7,7 @@ import requests import threading +from .logger import logger from .network import network from .authenticator import authenticator from .exceptions import RuntimeError, OptionsError @@ -49,15 +50,22 @@ def start (self, enable_events=True, host='https://api.goexp.io/', **options): self._is_started = True authenticator_thread = threading.Thread(target=lambda: authenticator.start(**options)) - authenticator_thread.daemon = True authenticator_thread.start() - authenticator.wait() + + try: + authenticator.wait() + except Exception as exception: + self.stop() + raise if enable_events: network_thread = threading.Thread(target=lambda: network.start(**options)) - network_thread.daemon = True network_thread.start() network.wait() + def stop (self): + authenticator.stop() + network.stop() + runtime = _Runtime() diff --git a/test2.py b/test2.py new file mode 100644 index 0000000..6b3b791 --- /dev/null +++ b/test2.py @@ -0,0 +1,23 @@ +import time +import exp +from collections import OrderedDict + +exp.runtime.start(enableEvents=False, host="https://api.exp.scala.com/", consumerAppUuid="503bc281-3fc3-4f70-b22e-a7a9288e05eb", apiKey="28f2eebf745fb84b855d0619dc853351b9139e83cdd509f2968308be21a3bfeb2c232c58dc1ffbe7168a80151c83b3f9") + +time.sleep(5) +while True: + data = exp.api.get_feed('2aea09ff-e014-4fc3-8e57-d374f577e05d').get_data() + mydict = data['items'] + d_sorted = sorted(mydict, key=lambda d: d.get('raw', {}).get('closed_at'), reverse=True) + for order in d_sorted[:4]: + print 'order id ',order['id'] + print 'create date ',order['date'] + print 'closed date ',order['raw']['closed_at'] + print order['raw']['note'] + for item in order['raw']['line_items']: + print item['name'] + print item['quantity'] + print item['product_id'] + print item['price'] + time.sleep(5) +exp.runtime.stop() From b4740b2ce2d635dc3fdd07d830f3a8876b996bb3 Mon Sep 17 00:00:00 2001 From: James Dalessio Date: Mon, 29 Feb 2016 18:06:32 -0500 Subject: [PATCH 021/104] wip --- check.py | 2 + exp/__init__.py | 2 + exp/network.py | 106 ++++++++++++++++++++++++++++++++---------------- 3 files changed, 74 insertions(+), 36 deletions(-) diff --git a/check.py b/check.py index d80f609..f08df01 100644 --- a/check.py +++ b/check.py @@ -6,5 +6,7 @@ exp.start( username='email@email.com', password='Password12321', organization='scala', host='http://localhost:9000/') + exp.get_channel('hi!').listen('hello') + print 'HELLO!' time.sleep(5) exp.stop() \ No newline at end of file diff --git a/exp/__init__.py b/exp/__init__.py index 4a3c5f4..f125f4a 100644 --- a/exp/__init__.py +++ b/exp/__init__.py @@ -19,6 +19,8 @@ def stop (): def get_auth (): return _authenticator.get_auth() +def get_channel(*args, **kwargs): + return _network.get_channel(*args, **kwargs) # Terminate the SDK when Ctrl-C is pressed. signal.signal(signal.SIGINT, lambda signal, frame: stop()) diff --git a/exp/network.py b/exp/network.py index 62c923a..8758778 100644 --- a/exp/network.py +++ b/exp/network.py @@ -1,4 +1,6 @@ import time +import threading +import uuid from .authenticator import authenticator from .exceptions import NotAuthenticatedError @@ -71,9 +73,8 @@ def cancel (self, id): def listen (self): id = str(uuid.uuid4()) - listener = Listener(id, self) - self._listeners.append(listener) - return listener + self._listeners[id] = Listener() + return self._listeners[id] @property def has_active_listeners(self): @@ -86,62 +87,86 @@ class Channel (object): def __init__(self, id): self._id = id self._namespaces = {} + self._subscription = threading.Event() def broadcast (self, name, payload): # Emit and return API response. pass def listen (self, name): - - return self.get_channel(channel_id).listen(name) + if not self._namespaces.get(name): + self._namespaces[name] = Namespace() + listener = self._namespaces[name].listen() + if not self._subscription.is_set(): + network.subscribe(self._id) + self._subscription.wait() + return listener @property def has_listeners (self): - return all([namepsace.has_listeners for key, namepsace in self._namespaces.iteritems()]) - + return all([namepsace.has_listeners for key, namepsace in self._namespaces.iteritems()]) -class Subscription (object): - pass - -class SocketHandler (object): - pass import urlparse from socketIO_client import SocketIO, BaseNamespace +import json +import base64 -"""class SocketNamespace(BaseNamespace): - - def on_message(self, message): - print 'BRO' - - def on_connect(self): - print 'CON' - - def on_disconnect(self): - print 'DIS' - - def on_subscribed(self, message): - print 'SUB' - - def on_channels(self, message): - print 'CHAN'""" class Network (object): def __init__(self): + self._time_since_sync = 0 self._options = None self._socket = None - self._is_connected = False self._auth = None self._do_stop = False + self._channels = {} + self._subscriptions = {} + + def get_channel(self, name, system=False, consumer=False): + id = self._generate_channel_id(name, system, consumer) + if not self._channels.get(id): + self._channels[id] = Channel(id) + return self._channels[id] + + def _generate_channel_id (self, name, system, consumer): + organization = authenticator.get_auth().get('identity', {}).get('organization') + raw_id = [name, organization, 1 if system else 0, 1 if consumer else 0] + json_id = json.dumps(raw_id) + return base64.b64encode(json_id) + + def on_disconnect(self): + for id, channel in self._channels.iteritems(): + channel._subscription.clear() + + def on_connect(self): + ids = [id for id, channel in self._channels.iteritems() if channel.has_listeners] + if ids: + self._socket.emit('subscribe', ids) + + def on_subscribed(self, ids): + for id in ids: + if not self._channels.get(id): + self._channels[id] = Channel(id) + self._channels[id]._subscription.set() + + def subscribe (self, id): + if self._socket and self._socket.connected: + print id + self._socket.emit('subscribe', [id]) + + @property + def is_connected(self): + return self._socket and self._socket.connected def start (self, **options): self._options = options self._main_event_loop() def wait (self): - while not self._is_connected: + while not self.is_connected: time.sleep(1) def _main_event_loop (self): @@ -157,18 +182,27 @@ def _main_event_loop (self): self._socket.disconnect() parsed_host = urlparse.urlparse(self._auth.get('network', {}).get('host')) self._socket = SocketIO(parsed_host.hostname, parsed_host.port, params={ "token": self._auth.get('token') }, Namespace=SocketNamespace, hurry_interval_in_seconds=10) - if self._socket: - self._socket.wait_for_callbacks(1) + if self.is_connected: + self._socket.wait(seconds=1) else: time.sleep(1) def stop (self): - self._do_stop = True + self._do_stop = True +network = Network() - def _reconnect(self): - +class SocketNamespace(BaseNamespace): + #def on_broadcast(self, message): + # network.on_broadcast(message) -network = Network() + def on_connect(self): + network.on_connect() + + #def on_disconnect(self): + # network.on_disconnect() + + def on_subscribed(self, ids): + network.on_subscribed(ids) From 59cc5227f6da02aae8363e09a7491b60bdcd231b Mon Sep 17 00:00:00 2001 From: James Dalessio Date: Wed, 2 Mar 2016 14:54:01 -0500 Subject: [PATCH 022/104] Skeleton complete. --- check.py | 10 ++- exp/__init__.py | 90 +++++++++++++++++++++- exp/{api/__init__.py => api.py} | 52 +++++++++++++ exp/api_utils.py | 46 +++++++++++ exp/authenticator.py | 2 +- exp/lib/network/Channel.py | 32 -------- exp/lib/network/Listener.py | 12 --- exp/lib/network/Namespace.py | 22 ------ exp/lib/network/Network.py | 13 ---- exp/network.py | 132 +++++++++++++++++--------------- exp/old/__init__.py | 0 exp/old/api_utils.py | 71 ----------------- exp/old/channel.py | 129 ------------------------------- exp/old/config.py | 10 --- exp/old/credentials.py | 89 --------------------- exp/old/event_node.py | 24 ------ exp/old/models/__init__.py | 0 exp/old/models/content.py | 41 ---------- exp/old/models/data.py | 24 ------ exp/old/models/device.py | 22 ------ exp/old/models/experience.py | 19 ----- exp/old/models/feed.py | 26 ------- exp/old/models/location.py | 22 ------ exp/old/models/thing.py | 22 ------ exp/old/network/__init__.py | 12 --- exp/old/network/channel.py | 68 ---------------- exp/old/network/listener.py | 7 -- exp/old/network/network.py | 0 exp/old/socket.py | 110 -------------------------- 29 files changed, 267 insertions(+), 840 deletions(-) rename exp/{api/__init__.py => api.py} (70%) create mode 100644 exp/api_utils.py delete mode 100644 exp/lib/network/Channel.py delete mode 100644 exp/lib/network/Listener.py delete mode 100644 exp/lib/network/Namespace.py delete mode 100644 exp/lib/network/Network.py delete mode 100644 exp/old/__init__.py delete mode 100644 exp/old/api_utils.py delete mode 100644 exp/old/channel.py delete mode 100644 exp/old/config.py delete mode 100644 exp/old/credentials.py delete mode 100644 exp/old/event_node.py delete mode 100644 exp/old/models/__init__.py delete mode 100644 exp/old/models/content.py delete mode 100644 exp/old/models/data.py delete mode 100644 exp/old/models/device.py delete mode 100644 exp/old/models/experience.py delete mode 100644 exp/old/models/feed.py delete mode 100644 exp/old/models/location.py delete mode 100644 exp/old/models/thing.py delete mode 100644 exp/old/network/__init__.py delete mode 100644 exp/old/network/channel.py delete mode 100644 exp/old/network/listener.py delete mode 100644 exp/old/network/network.py delete mode 100644 exp/old/socket.py diff --git a/check.py b/check.py index f08df01..78c2535 100644 --- a/check.py +++ b/check.py @@ -6,7 +6,11 @@ exp.start( username='email@email.com', password='Password12321', organization='scala', host='http://localhost:9000/') - exp.get_channel('hi!').listen('hello') - print 'HELLO!' - time.sleep(5) + channel = exp.get_channel('test_channel') + listener = channel.listen('test_message') + broadcast = listener.wait(15) + if broadcast: + print 'Got Broadcast' + broadcast.respond('I got your message!') + print 'Finsihed!' exp.stop() \ No newline at end of file diff --git a/exp/__init__.py b/exp/__init__.py index f125f4a..0fd45e4 100644 --- a/exp/__init__.py +++ b/exp/__init__.py @@ -7,6 +7,9 @@ from .network import network as _network from .authenticator import authenticator as _authenticator from .exceptions import * +from . import api +from . import api_utils + # Start the SDK. def start (*args, **kwargs): @@ -14,7 +17,8 @@ def start (*args, **kwargs): def stop (): _runtime.stop() - return sys.exit(1) + return sys.exit(1) + def get_auth (): return _authenticator.get_auth() @@ -22,6 +26,90 @@ def get_auth (): def get_channel(*args, **kwargs): return _network.get_channel(*args, **kwargs) + +def get (*args, **kwargs): + return api_utils.get(*args, **kwargs) + +def post (*args, **kwargs): + return api_utils.post(*args, **kwargs) + +def patch (*args, **kwargs): + return api_utils.patch(*args, **kwargs) + +def put (*args, **kwargs): + return api_utils.put(*args, **kwargs) + +def delete (*args, **kwargs): + return api_utils.delete(*args, **kwargs) + + +def get_device (*args, **kwargs): + return api.Device.get(*args, **kwargs) + +def find_device (*args, **kwargs): + return api.Device.find(*args, **kwargs) + +def create_device (*args, **kwargs): + return api.Device.create(*args, **kwargs) + + +def get_thing (*args, **kwargs): + return api.Thing.get(*args, **kwargs) + +def find_thing (*args, **kwargs): + return api.Thing.find(*args, **kwargs) + +def create_thing (*args, **kwargs): + return api.Thing.create(*args, **kwargs) + + +def get_experience (*args, **kwargs): + return api.Experience.get(*args, **kwargs) + +def find_experience (*args, **kwargs): + return api.Experience.find(*args, **kwargs) + +def create_experience (*args, **kwargs): + return api.Experience.create(*args, **kwargs) + + +def get_location (*args, **kwargs): + return api.Location.get(*args, **kwargs) + +def find_location (*args, **kwargs): + return api.Location.find(*args, **kwargs) + +def create_location (*args, **kwargs): + return api.Location.create(*args, **kwargs) + + +def get_data (*args, **kwargs): + return api.Data.get(*args, **kwargs) + +def find_data (*args, **kwargs): + return api.Data.find(*args, **kwargs) + +def create_data (*args, **kwargs): + return api.Data.create(*args, **kwargs) + + +def get_feed (*args, **kwargs): + return api.Feed.get(*args, **kwargs) + +def find_feed (*args, **kwargs): + return api.Feed.find(*args, **kwargs) + +def create_feed (*args, **kwargs): + return api.Feed.create(*args, **kwargs) + + +def get_content (*args, **kwargs): + return api.Content.get(*args, **kwargs) + +def find_content (*args, **kwargs): + return api.Content.find(*args, **kwargs) + + # Terminate the SDK when Ctrl-C is pressed. signal.signal(signal.SIGINT, lambda signal, frame: stop()) diff --git a/exp/api/__init__.py b/exp/api.py similarity index 70% rename from exp/api/__init__.py rename to exp/api.py index 76b6795..f9f6124 100644 --- a/exp/api/__init__.py +++ b/exp/api.py @@ -9,6 +9,58 @@ #from .. lib.models.thing import Thing #from .. lib.models.feed import Feed +from . import api_utils + + + +class Resource (object): + + def __init__ (self, document): + self.document = document + + @property + def uuid (self): + return self.document['uuid'] + + @classmethod + def _get_path (cls): + raise Exception('Not implemented.') + + @classmethod + def get (cls, uuid): + return cls(api_utils.get(self.get_path() + '/' + uuid)) + + @classmethod + def create (cls, document): + resource = cls(document) + resource.save() + return resource + + @classmethod + def find (cls, params): + response = api_utils.get(self._get_path(), params) + query = [cls(document) for document in response['results']] + query.total = response['total'] + return query + + def _get_path (self): + return self.__cls__.get_path() + '/' + self.uuid + + def save (self): + return api_utils.patch(self._get_path(), self.document) + + def refresh (self): + self.document = api_utils.get(this._get_path()) + + def get_channel (self, **kwargs): + return network.get_channel(self._get_channel_name(), **kwargs) + + def _get_channel_name (self): + return self.uuid + + def fling (payload, **kwargs): + return self.get_channel().broadcast('fling', payload, **kwargs) + """ Content """ diff --git a/exp/api_utils.py b/exp/api_utils.py new file mode 100644 index 0000000..56c3c9a --- /dev/null +++ b/exp/api_utils.py @@ -0,0 +1,46 @@ +import requests +import urllib + +from .authenticator import authenticator + + +def get(path, params=None): + url = "{0}{1}".format(authenticator.get_auth()['api']['host'], urllib.quote(path)) + headers = {} + headers["Authorization"] = "Bearer " + authenticator.get_auth()['token'] + response = requests.get(url, timeout=10, params=params, headers=headers) + response.raise_for_status() + return response.json() + +def post(path, payload=None, params=None): + url = "{0}{1}".format(authenticator.get_auth()['api']['host'], urllib.quote(path)) + headers = {} + headers["Authorization"] = "Bearer " + authenticator.get_auth()['token'] + response = requests.post(url, timeout=10, params=params, json=payload, headers=headers) + response.raise_for_status() + return response.json() + +def patch(path, payload=None, params=None): + url = "{0}{1}".format(authenticator.get_auth()['api']['host'], urllib.quote(path)) + headers = {} + headers["Authorization"] = "Bearer " + authenticator.get_auth()['token'] + response = requests.patch(url, timeout=10, params=params, json=payload, headers=headers) + response.raise_for_status() + return response.json() + +def put(path, payload=None, params=None): + url = "{0}{1}".format(authenticator.get_auth()['api']['host'], urllib.quote(path)) + headers = {} + headers["Authorization"] = "Bearer " + authenticator.get_auth()['token'] + response = requests.put(url, timeout=10, params=params, json=payload, headers=headers) + response.raise_for_status() + return response.json() + +def delete(path, payload=None, params=None): + url = "{0}{1}".format(authenticator.get_auth()['api']['host'], urllib.quote(path)) + headers = {} + headers["Authorization"] = "Bearer " + authenticator.get_auth()['token'] + response = requests.delete(url, timeout=10, params=params, json=payload, headers=headers) + response.raise_for_status() + return response.json() + diff --git a/exp/authenticator.py b/exp/authenticator.py index b2b3cf9..2153c14 100644 --- a/exp/authenticator.py +++ b/exp/authenticator.py @@ -116,7 +116,7 @@ def _get_refresh_url (self): return self._options.get('host') + '/api/auth/token' - def _get_login_response (self): + def _get_login_response (self): response = requests.request('POST', self._get_login_url(), json=self._get_login_payload()) if response.status_code == 200: return response.json() diff --git a/exp/lib/network/Channel.py b/exp/lib/network/Channel.py deleted file mode 100644 index 3f9f73c..0000000 --- a/exp/lib/network/Channel.py +++ /dev/null @@ -1,32 +0,0 @@ - -class Channel: - - def __init__(self, id): - this._id = id - this._namespaces = {} - - def recieve (self, message): - namespace = this._namespaces.get(message.get('name')) - if not namespace: return - return namespace.receive(message) - - def broadcast (self, **kwargs): - kwargs['id'] = self._id - return Network.broadcast(**kwargs) - - def listen (self, name): - Network._subscribe(self._id) - if not self._namespaces[name]: - self._namespaces[name] = Namespace() - return self._namespaces[name].listen() - - def _subscribe (self): - return Network.subscribe(self._id) - - def _unsubscribe (self): - return Network.unsubscribe(self._id) - - def _unlisten (self, namespace): - self._namespaces.pop(namespace, None) - if not bool(self._namespaces): - self._unsubscribe() diff --git a/exp/lib/network/Listener.py b/exp/lib/network/Listener.py deleted file mode 100644 index ea4a676..0000000 --- a/exp/lib/network/Listener.py +++ /dev/null @@ -1,12 +0,0 @@ - -class Listener: - - def __init__(self, namespace): - this._namespace = namespace - this._messages = [] - - def wait (message): - pass - - def cancel (self): - pass diff --git a/exp/lib/network/Namespace.py b/exp/lib/network/Namespace.py deleted file mode 100644 index 3a7ed3f..0000000 --- a/exp/lib/network/Namespace.py +++ /dev/null @@ -1,22 +0,0 @@ - -class Namespace: - - def __init__(self, channel): - this._listeners = [] - this._channel = channel - - def unlisten (self, listener): - try: - this._listeners.remove(listener) - except ValueError: - pass - if not this._listeners: - this._channel.unlisten(self) - - def listen (self): - this._listeners.append(Listener()) - return this._listeners[-1] - - def receive (message): - return [listener.receive(message) for listener in this._listeners] - diff --git a/exp/lib/network/Network.py b/exp/lib/network/Network.py deleted file mode 100644 index 46e6bd9..0000000 --- a/exp/lib/network/Network.py +++ /dev/null @@ -1,13 +0,0 @@ - - -class Network (object): - - def __init__(self): - pass - - - def broadcast (message): - pass - - def listen(): - pass diff --git a/exp/network.py b/exp/network.py index 8758778..2e8d30c 100644 --- a/exp/network.py +++ b/exp/network.py @@ -2,6 +2,17 @@ import threading import uuid + + +import urlparse +from socketIO_client import SocketIO, BaseNamespace +import json +import base64 + +from . import api_utils + + + from .authenticator import authenticator from .exceptions import NotAuthenticatedError @@ -9,36 +20,31 @@ class Broadcast (object): def __init__(self, message): self._message = message - self._time = int(time.time()) - - @property - def time(self): - return self._time + self.time = int(time.time()) @property def payload(self): return self._message.get('payload', None) def respond(self, payload): - try: - # TODO: Make API call to respond to message. - pass - except Exception as exception: - # TODO: Warn about failure to send response. - # TODO: Rethrow a network exception. - pass - + message = {} + message['id'] = self._message['id'] + message['channel'] = self._message['channel'] + message['payload'] = payload + return api_utils.post('/api/networks/current/responses', payload=message) class Listener (object): - def __init__(self, max_age=60): + def __init__(self, id, namespace, max_age=60): + self._id = id + self._namespace = namespace self._event = threading.Event() self._broadcasts = [] self._max_age = max_age - self._is_active = True - def receive (self, broadcast): + def receive (self, message): + broadcast = Broadcast(message) now = int(time.time()) self._broadcasts = [broadcast for broadcast in self._broadcasts if now - broadcast.time < self._max_age] self._broadcasts.append(broadcast) @@ -55,12 +61,8 @@ def wait (self, timeout=None): except IndexError: return None - @property - def is_active(self): - return self._is_active - def cancel(self): - self._is_active = False + self._namespace.cancel(self._id) class Namespace (object): @@ -68,17 +70,21 @@ class Namespace (object): def __init__(self): self._listeners = {} + def receive (self, message): + for id, listener in self._listeners.iteritems(): + listener.receive(message) + def cancel (self, id): del self._listeners[id] - def listen (self): + def listen (self, **kwargs): id = str(uuid.uuid4()) - self._listeners[id] = Listener() + self._listeners[id] = Listener(id, self, **kwargs) return self._listeners[id] @property - def has_active_listeners(self): - return any([listener.is_active for id, listener in self._listeners.iteritems()]) + def has_listeners(self): + return bool(self._listeners) @@ -89,14 +95,21 @@ def __init__(self, id): self._namespaces = {} self._subscription = threading.Event() - def broadcast (self, name, payload): - # Emit and return API response. - pass + def receive (self, message): + if message.get('name') in self._namespaces: + self._namespaces[message['name']].receive(message) - def listen (self, name): + def broadcast (self, name, payload, timeout=5): + message = {} + message['channel'] = self._id + message['name'] = name + message['payload'] = payload + return api_utils.post('/api/networks/current/broadcasts', payload=message, params={ 'timeout': timeout }) + + def listen (self, name, **kwargs): if not self._namespaces.get(name): self._namespaces[name] = Namespace() - listener = self._namespaces[name].listen() + listener = self._namespaces[name].listen(**kwargs) if not self._subscription.is_set(): network.subscribe(self._id) self._subscription.wait() @@ -104,26 +117,18 @@ def listen (self, name): @property def has_listeners (self): - return all([namepsace.has_listeners for key, namepsace in self._namespaces.iteritems()]) + return any([namepsace.has_listeners for key, namepspace in self._namespaces.iteritems()]) -import urlparse -from socketIO_client import SocketIO, BaseNamespace -import json -import base64 - - class Network (object): def __init__(self): - self._time_since_sync = 0 self._options = None self._socket = None self._auth = None self._do_stop = False self._channels = {} - self._subscriptions = {} def get_channel(self, name, system=False, consumer=False): id = self._generate_channel_id(name, system, consumer) @@ -132,35 +137,37 @@ def get_channel(self, name, system=False, consumer=False): return self._channels[id] def _generate_channel_id (self, name, system, consumer): - organization = authenticator.get_auth().get('identity', {}).get('organization') - raw_id = [name, organization, 1 if system else 0, 1 if consumer else 0] - json_id = json.dumps(raw_id) + organization = authenticator.get_auth()['identity']['organization'] + raw_id = [organization, name, 1 if system else 0, 1 if consumer else 0] + json_id = json.dumps(raw_id, separators=(',', ':')) return base64.b64encode(json_id) + def on_connect(self): + ids = [] + for id, channel in self._channels.iteritems(): + if channel.has_listeners: + ids.append(id) + if ids and self.is_connected: + self._socket.emit('subscribe', ids) + def on_disconnect(self): for id, channel in self._channels.iteritems(): channel._subscription.clear() - def on_connect(self): - ids = [id for id, channel in self._channels.iteritems() if channel.has_listeners] - if ids: - self._socket.emit('subscribe', ids) - def on_subscribed(self, ids): for id in ids: if not self._channels.get(id): self._channels[id] = Channel(id) self._channels[id]._subscription.set() + def on_broadcast (self, message): + if message.get('channel') in self._channels: + self._channels[message['channel']].receive(message) + def subscribe (self, id): - if self._socket and self._socket.connected: - print id + if self.is_connected: self._socket.emit('subscribe', [id]) - @property - def is_connected(self): - return self._socket and self._socket.connected - def start (self, **options): self._options = options self._main_event_loop() @@ -169,6 +176,10 @@ def wait (self): while not self.is_connected: time.sleep(1) + @property + def is_connected(self): + return self._socket and self._socket.connected + def _main_event_loop (self): while not self._do_stop: try: @@ -188,21 +199,22 @@ def _main_event_loop (self): time.sleep(1) def stop (self): - self._do_stop = True - -network = Network() + self._do_stop = True class SocketNamespace(BaseNamespace): - #def on_broadcast(self, message): - # network.on_broadcast(message) + def on_broadcast(self, message): + network.on_broadcast(message) def on_connect(self): network.on_connect() - #def on_disconnect(self): - # network.on_disconnect() + def on_disconnect(self): + network.on_disconnect() def on_subscribed(self, ids): network.on_subscribed(ids) + + +network = Network() \ No newline at end of file diff --git a/exp/old/__init__.py b/exp/old/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/exp/old/api_utils.py b/exp/old/api_utils.py deleted file mode 100644 index 40970c1..0000000 --- a/exp/old/api_utils.py +++ /dev/null @@ -1,71 +0,0 @@ -import requests -import urllib - -from . import config -from . import credentials - -def get_timeout(): - return config.get('timeout') or 10 - -def generate_url(https://github.com/path): - base = config.get("host") - if config.get("port"): - base = "{0}:{1}".format(base, config.get("port")) - return "{0}{1}".format(base, urllib.quote(path)) - -def authenticate(username, password, organization): - url = generate_url("https://github.com/api/auth/login") - payload = {} - payload["username"] = username - payload["password"] = password - payload["org"] = organization - response = requests.post(url, json=payload) - response.raise_for_status() - body = response.json() - return body["token"] - -def get(path, params=None): - url = generate_url(https://github.com/path) - headers = {} - timeout = get_timeout() - headers["Authorization"] = "Bearer " + credentials.get_token() - response = requests.get(url, timeout=timeout, params=params, headers=headers) - response.raise_for_status() - return response.json() - -def post(path, payload=None, params=None): - url = generate_url(https://github.com/path) - timeout = get_timeout() - headers = {} - headers["Authorization"] = "Bearer " + credentials.get_token() - response = requests.post(url, timeout=timeout, params=params, json=payload, headers=headers) - response.raise_for_status() - return response.json() - -def patch(path, payload=None, params=None): - url = generate_url(https://github.com/path) - timeout = get_timeout() - headers = {} - headers["Authorization"] = "Bearer " + credentials.get_token() - response = requests.patch(url, timeout=timeout, params=params, json=payload, headers=headers) - response.raise_for_status() - return response.json() - -def put(path, payload=None, params=None): - url = generate_url(https://github.com/path) - timeout = get_timeout() - headers = {} - headers["Authorization"] = "Bearer " + credentials.get_token() - response = requests.put(url, timeout=timeout, params=params, json=payload, headers=headers) - response.raise_for_status() - return response.json() - -def delete(path, payload=None, params=None): - url = generate_url(https://github.com/path) - timeout = get_timeout() - headers = {} - headers["Authorization"] = "Bearer " + credentials.get_token() - response = requests.delete(url, timeout=timeout, params=params, json=payload, headers=headers) - response.raise_for_status() - return response.json() - diff --git a/exp/old/channel.py b/exp/old/channel.py deleted file mode 100644 index 412f30e..0000000 --- a/exp/old/channel.py +++ /dev/null @@ -1,129 +0,0 @@ -import random -import time -import threading -from . import socket - -class Timeout(Exception): - pass - -class Error(Exception): - pass - -class Channel(object): - - Timeout = Timeout - Error = Error - - def __init__(self, name): - self._name = name - self._listeners = {} - self._responses = {} - self._responders = {} - self._lock = threading.Lock() - socket.on('message', self._on_message) - - def _clean_responses(self): - """ Clean up responses that have not been processed """ - self._lock.acquire() - now = time.time() - for id, response in self._responses.iteritems(): - if now - response["time"] > 10: - self._responses.pop(id, None) - self._lock.release() - - def _on_response(self, message): - self._lock.acquire() - message["time"] = time.time() - self._responses[message["id"]] = message - self._lock.release() - - def _on_broadcast(self, message): - self._lock.acquire() - callbacks = self._listeners.get(message["name"], None) - if not callbacks: - return self._lock.release() - for callback in callbacks: - try: - callback(message['payload']) - except: - pass - self._lock.release() - - def _on_request(self, message): - self._lock.acquire() - if not self._responders.get(message["name"]): - return self._lock.release() - callback = self._responders.get(message["name"]) - try: - payload = callback(message['payload']) - except: - socket.send({ - "type": "response", - "id": message["id"], - "error": "An error occured" - }) - else: - socket.send({ - "type": "response", - "id": message["id"], - "payload": payload - }) - self._lock.release() - - def _on_message(self, message): - if message["type"] == "response": - self._clean_responses() - return self._on_response(message) - if message["channel"] != self._name: - return - if message["type"] == "broadcast": - self._on_broadcast(message) - elif message["type"] == "request": - self._on_request(message) - - def request(self, name=None, target=None, payload=None): - message = {} - message["type"] = "request" - message["id"] = str(random.random()) - message["channel"] = self._name - message["name"] = name - message["payload"] = payload - message["device"] = {} # This will be deprecated - message["device"]["target"] = target - socket.send(message) - start = time.time() - while time.time() - start < 10: - self._lock.acquire() - response = self._responses.get(message["id"], None) - if response: - self._responses.pop(message["id"], None) - self._lock.release() - if response.get('error', None): - raise Error(response.get('error')) - return response.get('payload', None) - self._lock.release() - time.sleep(.1) - self._lock.acquire() - self._responses.pop(message["id"], None) - self._lock.release() - raise self.Timeout() - - - def broadcast(self, name=None, payload=None): - socket.send({ - 'type': 'broadcast', - 'channel': self._name, - 'name': name, - 'payload': payload - }) - - def listen(self, name=None, callback=None): - if not self._listeners.get(name): - self._listeners[name] = [] - self._listeners[name].append(callback) - - def fling(self, uuid=None): - return self.broadcast(name='fling', payload={'uuid': uuid}) - - def respond(self, name=None, callback=None): - self._responders[name] = callback diff --git a/exp/old/config.py b/exp/old/config.py deleted file mode 100644 index c6b1d24..0000000 --- a/exp/old/config.py +++ /dev/null @@ -1,10 +0,0 @@ -_vars = {} - -def set(**kwargs): - for key, value in kwargs.iteritems(): - _vars[key] = value - -def get(name): - return _vars.get(name, None) - - diff --git a/exp/old/credentials.py b/exp/old/credentials.py deleted file mode 100644 index 7e3cfb1..0000000 --- a/exp/old/credentials.py +++ /dev/null @@ -1,89 +0,0 @@ -import time -import requests -import json -import hmac -from hashlib import sha256 -from base64 import urlsafe_b64encode - - -_vars = {} -_vars["uuid"] = None -_vars["secret"] = None -_vars["username"] = None -_vars["password"] = None -_vars["token"] = None -_vars["time"] = 0 -_vars["apiKey"] = None - -def _reset(): - _vars["uuid"] = None - _vars["secret"] = None - _vars["username"] = None - _vars["password"] = None - _vars["organization"] = None - _vars["token"] = None - _vars["time"] = 0 - _vars["apiKey"] = None - -def _jwtHS256encode(payload, secret): - alg = urlsafe_b64encode('{"alg":"HS256","typ":"JWT"}') - if not isinstance(payload, basestring): - payload = json.dumps(payload, separators=(',', ':')) - payload = urlsafe_b64encode(payload.encode("utf-8")).rstrip('=') - sign = urlsafe_b64encode(hmac.new(secret.encode("utf-8"), '.'.join([alg, payload]), sha256).digest()).rstrip('=') - return '.'.join([alg, payload, sign]) - -def set_user_credentials(username, password, organization): - _reset() - _vars["username"] = username - _vars["password"] = password - _vars["organization"] = organization - -def set_device_credentials(uuid, secret): - _reset() - _vars["uuid"] = uuid - _vars["secret"] = secret - -def set_consumer_app_credentials(uuid, apiKey): - _reset() - _vars["uuid"] = uuid - _vars["apiKey"] = apiKey - -def set_token(token): - _reset() - _vars["token"] = token - _vars["time"] = float("inf") - -def get_token(): - if not _vars["token"] or time.time() - _vars["time"] < 120: - _generate_token() - return _vars["token"] - -def _generate_token(): - if _vars["uuid"] and _vars["secret"]: - _generate_device_token() - elif _vars["username"] and _vars["password"]: - _generate_user_token() - elif _vars["uuid"] and _vars["apiKey"]: - _generate_consumer_app_token() - else: - _vars["token"] = '' - _vars["time"] = 0 - -def _generate_user_token(): - from . import api_utils # Avoid circular import. - _vars["token"] = api_utils.authenticate(_vars["username"], _vars["password"], _vars["organization"]) - _vars["time"] = time.time() - -def _generate_device_token(): - payload = {} - payload["deviceUuid"] = _vars["uuid"] - _vars["token"] = _jwtHS256encode(payload, _vars["secret"]) - _vars["time"] = time.time() - -def _generate_consumer_app_token(): - payload = {} - payload["consumerAppUuid"] = _vars["uuid"] - _vars["token"] = _jwtHS256encode(payload, _vars["apiKey"]) - _vars["time"] = time.time() - diff --git a/exp/old/event_node.py b/exp/old/event_node.py deleted file mode 100644 index 98e115f..0000000 --- a/exp/old/event_node.py +++ /dev/null @@ -1,24 +0,0 @@ -import threading -import inspect - -class EventNode(object): - - def __init__(self): - self.callbacks = {} - - def on(self, name, callback): - if not self.callbacks.get(name): - self.callbacks[name] = [] - self.callbacks[name].append(callback) - - def trigger(self, name, payload=None): - if not self.callbacks.get(name): return - for callback in self.callbacks.get(name): - spec = inspect.getargspec(callback) - if len(spec.args) == 0: - thread = threading.Thread(target=callback) - else: - thread = threading.Thread(target=callback, args=[payload]) - thread.daemon = True - thread.start() - diff --git a/exp/old/models/__init__.py b/exp/old/models/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/exp/old/models/content.py b/exp/old/models/content.py deleted file mode 100644 index 7154ab5..0000000 --- a/exp/old/models/content.py +++ /dev/null @@ -1,41 +0,0 @@ -import urllib - -from .. import api_utils - -class Content(object): - - def __init__(self, document, _is_children_populated=False): - self.document = document - self._is_children_populated = _is_children_populated - - def get_url(https://github.com/self): - subtype = self.document['subtype'] - if subtype == 'scala:content:file': - path = urllib.quote(self.document['path']) - return api_utils.generate_url('https://github.com/api/delivery') + path - elif subtype == 'scala:content:app': - path = urllib.quote(self.document['path'] + '/index.html') - return api_utils.generate_url('https://github.com/api/delivery') + path - elif subtype == 'scala:content:url': - return self.document['url'] - raise NotImplementedError('Cannot get url for this subtype.') - - def get_variant_url(https://github.com/self,%20variant_name): - subtype = self.document['subtype'] - path = self.document['path'] - variants = self.document.get('variants', {}).get(name, None) - if not variants: - raise NameError('Variant not found.') - if subtype == 'scala:content:file': - query = '?' + urllib.urlencode({ 'variant' : variant_name }) - return api_utils.generate_url('https://github.com/api/delivery'%20+%20path) + query - raise NameError('Variant not found.') - - def get_children(self): - if not self._is_children_populated: - path = '{0}{1}{2}'.format('/api/content/', self.document.get("uuid"), '/children') - self.document = api_utils.get(path) - self._is_children_populated = True - return [Content(x) for x in self.document.get("children")] - - diff --git a/exp/old/models/data.py b/exp/old/models/data.py deleted file mode 100644 index af3d456..0000000 --- a/exp/old/models/data.py +++ /dev/null @@ -1,24 +0,0 @@ -import urllib - -from .. import api_utils - -class Data(object): - - def __init__(self, key=None, group=None, value=None, **kwargs): - self.key = key - self.group = group - self.value = value - encoded_key = urllib.quote_plus(key) - encoded_group = urllib.quote_plus(group) - self._path = '/api/data/{0}/{1}'.format(encoded_group, encoded_key) - - def save(self): - api_utils.put(self._path, payload=self.value) - return self - - def delete(self): - api_utils.delete(self._path) - return self - - - diff --git a/exp/old/models/device.py b/exp/old/models/device.py deleted file mode 100644 index ac20d18..0000000 --- a/exp/old/models/device.py +++ /dev/null @@ -1,22 +0,0 @@ -from .. import api_utils - -class Device(object): - - def __init__(self, document, _new=True): - self.document = document - self._new = _new - - def save(self): - if self._new: - self.document = api_utils.post("/api/devices", payload=self.document) - self._new = False - else: - self.document = api_utils.patch("/api/devices/" + self.document["uuid"], payload=self.document) - return self - - def delete(self): - api_utils.delete("/api/devices/" + self.document["uuid"]) - return self - - - diff --git a/exp/old/models/experience.py b/exp/old/models/experience.py deleted file mode 100644 index ac0aec4..0000000 --- a/exp/old/models/experience.py +++ /dev/null @@ -1,19 +0,0 @@ -from .. import api_utils - -class Experience(object): - - def __init__(self, document, _new=True): - self.document = document - self._new = _new - - def save(self): - if self._new: - self.experience = api_utils.post("/api/experiences", payload=self.document) - self._new = False - else: - self.document = api_utils.patch("/api/experiences/" + self.document["uuid"], payload=self.document) - return self - - def delete(self): - api_utils.delete("/api/experiences/" + self.document["uuid"]) - return self diff --git a/exp/old/models/feed.py b/exp/old/models/feed.py deleted file mode 100644 index 4213efc..0000000 --- a/exp/old/models/feed.py +++ /dev/null @@ -1,26 +0,0 @@ -from .. import api_utils - -class Feed(object): - - def __init__(self, document, _new=True): - self.document = document - self._new = _new - - def save(self): - if self._new: - self.document = api_utils.post('/api/connectors/feeds', payload=self.document) - self._new = False - else: - self.document = api_utils.patch('/api/connectors/feeds/' + self.document['uuid'], payload=self.document) - return self - - def get_data (self): - return api_utils.get('/api/connectors/feeds/' + self.document['uuid'] + '/data') - - - def delete(self): - api_utils.delete('/api/locations/' + self.document['uuid']) - return self - - - diff --git a/exp/old/models/location.py b/exp/old/models/location.py deleted file mode 100644 index 6fd2895..0000000 --- a/exp/old/models/location.py +++ /dev/null @@ -1,22 +0,0 @@ -from .. import api_utils - -class Location(object): - - def __init__(self, document, _new=True): - self.document = document - self._new = _new - - def save(self): - if self._new: - self.document = api_utils.post("/api/locations", payload=self.document) - self._new = False - else: - self.document = api_utils.patch("/api/locations/" + self.document["uuid"], payload=self.document) - return self - - def delete(self): - api_utils.delete("/api/locations/" + self.document["uuid"]) - return self - - def get_layout_url(https://github.com/self): - return api_utils.generate_url('https://github.com/api/locations/'%20+%20self.document['uuid']%20+%20'/layout') \ No newline at end of file diff --git a/exp/old/models/thing.py b/exp/old/models/thing.py deleted file mode 100644 index e9eadc3..0000000 --- a/exp/old/models/thing.py +++ /dev/null @@ -1,22 +0,0 @@ -from .. import api_utils - -class Thing(object): - - def __init__(self, document, _new=True): - self.document = document - self._new = _new - - def save(self): - if self._new: - self.document = api_utils.post("/api/things", payload=self.document) - self._new = False - else: - self.document = api_utils.patch("/api/things/" + self.document["uuid"], payload=self.document) - return self - - def delete(self): - api_utils.delete("/api/things/" + self.document["uuid"]) - return self - - - diff --git a/exp/old/network/__init__.py b/exp/old/network/__init__.py deleted file mode 100644 index 4ebdf0a..0000000 --- a/exp/old/network/__init__.py +++ /dev/null @@ -1,12 +0,0 @@ - - -class Network: - pass - - -class Channel: - pass - -class Subscription: - pass - diff --git a/exp/old/network/channel.py b/exp/old/network/channel.py deleted file mode 100644 index 510f46d..0000000 --- a/exp/old/network/channel.py +++ /dev/null @@ -1,68 +0,0 @@ - -class Listener: - - def __init__(self, namespace): - this._namespace = namespace - this._messages = [] - - def receive (message): - pass - - def cancel (self): - pass - - - -class Namespace: - - def __init__(self, channel): - this._listeners = [] - this._channel = channel - - def unlisten (self, listener): - try: - this._listeners.remove(listener) - except ValueError: - pass - if not this._listeners: - this._channel.unlisten(self) - - def listen (self): - this._listeners.append(Listener()) - return this._listeners[-1] - - def receive (message): - return [listener.receive(message) for listener in this._listeners] - - -class Channel: - - def __init__(self, id): - this._id = id - this._namespaces = {} - - def recieve (self, message): - namespace = this._namespaces.get(message.get('name')) - if not namespace: return - return namespace.receive(message) - - def broadcast (self, **kwargs): - kwargs['id'] = self._id - return Network.broadcast(**kwargs) - - def listen (self, name): - Network._subscribe(self._id) - if not self._namespaces[name]: - self._namespaces[name] = Namespace() - return self._namespaces[name].listen() - - def _subscribe (self): - return Network.subscribe(self._id) - - def _unsubscribe (self): - return Network.unsubscribe(self._id) - - def _unlisten (self, namespace): - self._namespaces.pop(namespace, None) - if not bool(self._namespaces): - self._unsubscribe() diff --git a/exp/old/network/listener.py b/exp/old/network/listener.py deleted file mode 100644 index 8149f07..0000000 --- a/exp/old/network/listener.py +++ /dev/null @@ -1,7 +0,0 @@ - - - -class Listener: - - def __init__(self): - pass diff --git a/exp/old/network/network.py b/exp/old/network/network.py deleted file mode 100644 index e69de29..0000000 diff --git a/exp/old/socket.py b/exp/old/socket.py deleted file mode 100644 index 0a5d231..0000000 --- a/exp/old/socket.py +++ /dev/null @@ -1,110 +0,0 @@ -import Queue -import threading -import time -import logging -import random - -from socketIO_client import SocketIO, BaseNamespace - -from . import event_node - - -logging.getLogger('request').setLevel(logging.DEBUG) -logging.basicConfig(level=logging.DEBUG) - -_events = event_node.EventNode() - -_vars = {} -_vars["io"] = None -_vars["threadId"] = None - -_outgoing = Queue.Queue() -_lock = threading.Lock() - -on = _events.on - -class _Namespace(BaseNamespace): - - def on_message(self, message): - if type(message) != dict: return - _events.trigger('message', message) - - def on_connect(self): - _events.trigger('connected') - - def on_disconnect(self): - _events.trigger('disconnected') - - -class Timeout(Exception): - pass - - -def _disconnect(): - _lock.acquire() - if _vars["io"]: - _vars["io"].disconnect() - _vars["io"] = None - _lock.release() - -def _connect(host, port, token): - _disconnect() - _lock.acquire() - start = time.time() - while time.time() - start < 5: - if not _vars["io"]: - try: - _vars["io"] = SocketIO(host, port, params={ "token": token }, Namespace=_Namespace, hurry_interval_in_seconds=10) - except: - _vars["io"] = None - time.sleep(1) - continue - if _vars["io"].connected: - return _lock.release() - time.sleep(.1) - _lock.release() - _disconnect() - -def send(message): - _outgoing.put(message) - -def _process_incoming(): - if not _vars["io"]: - return - try: - _vars["io"].wait(.1) - except: - time.sleep(1) - -def _process_outgoing(): - try: - message = _outgoing.get_nowait() - except Queue.Empty: - return - if not _vars["io"]: - return - _vars["io"].emit('message', message) - -def _loop(): - threadId = _vars["threadId"] - _lock.release() - while threadId == _vars["threadId"]: - _lock.acquire() - _process_incoming() - _process_outgoing() - _lock.release() - time.sleep(.1) - -def start(*args): - _lock.acquire() - _vars["threadId"] = random.random() - threading.Thread(target=_loop).start() - _connect(*args) - -def stop(): - _lock.acquire() - _vars["threadId"] = None - _lock.release() - _disconnect() - - From f4a99c204f7500e3e94acc5bfdcea5a192a82107 Mon Sep 17 00:00:00 2001 From: James Dalessio Date: Wed, 2 Mar 2016 23:43:09 -0500 Subject: [PATCH 023/104] Better threading flow. --- check.py | 17 +- exp/__init__.py | 17 +- exp/api.py | 179 ++++++++++-------- exp/authenticator.py | 163 +++++----------- exp/exceptions.py | 11 +- exp/network.py | 33 +++- exp/runtime.py | 27 +-- test2.py | 23 --- tests/network_integration_test.py | 52 +++++ ...art_test.py => runtime_start_unit_test.py} | 1 + 10 files changed, 256 insertions(+), 267 deletions(-) delete mode 100644 test2.py create mode 100644 tests/network_integration_test.py rename tests/{runtime_start_test.py => runtime_start_unit_test.py} (99%) diff --git a/check.py b/check.py index 78c2535..bf326d6 100644 --- a/check.py +++ b/check.py @@ -6,11 +6,14 @@ exp.start( username='email@email.com', password='Password12321', organization='scala', host='http://localhost:9000/') - channel = exp.get_channel('test_channel') - listener = channel.listen('test_message') - broadcast = listener.wait(15) - if broadcast: - print 'Got Broadcast' - broadcast.respond('I got your message!') + + devices = exp.find_devices() + print devices + #channel = exp.get_channel('test_channel') + #listener = channel.listen('test_message') + #broadcast = listener.wait(15) + #if broadcast: + # print 'Got Broadcast' + # broadcast.respond('I got your message!') print 'Finsihed!' - exp.stop() \ No newline at end of file + #exp.stop() \ No newline at end of file diff --git a/exp/__init__.py b/exp/__init__.py index 0fd45e4..44d064c 100644 --- a/exp/__init__.py +++ b/exp/__init__.py @@ -15,11 +15,6 @@ def start (*args, **kwargs): return _runtime.start(*args, **kwargs) -def stop (): - _runtime.stop() - return sys.exit(1) - - def get_auth (): return _authenticator.get_auth() @@ -46,7 +41,7 @@ def delete (*args, **kwargs): def get_device (*args, **kwargs): return api.Device.get(*args, **kwargs) -def find_device (*args, **kwargs): +def find_devices (*args, **kwargs): return api.Device.find(*args, **kwargs) def create_device (*args, **kwargs): @@ -56,7 +51,7 @@ def create_device (*args, **kwargs): def get_thing (*args, **kwargs): return api.Thing.get(*args, **kwargs) -def find_thing (*args, **kwargs): +def find_things (*args, **kwargs): return api.Thing.find(*args, **kwargs) def create_thing (*args, **kwargs): @@ -66,7 +61,7 @@ def create_thing (*args, **kwargs): def get_experience (*args, **kwargs): return api.Experience.get(*args, **kwargs) -def find_experience (*args, **kwargs): +def find_experiences (*args, **kwargs): return api.Experience.find(*args, **kwargs) def create_experience (*args, **kwargs): @@ -76,7 +71,7 @@ def create_experience (*args, **kwargs): def get_location (*args, **kwargs): return api.Location.get(*args, **kwargs) -def find_location (*args, **kwargs): +def find_locations (*args, **kwargs): return api.Location.find(*args, **kwargs) def create_location (*args, **kwargs): @@ -96,7 +91,7 @@ def create_data (*args, **kwargs): def get_feed (*args, **kwargs): return api.Feed.get(*args, **kwargs) -def find_feed (*args, **kwargs): +def find_feeds (*args, **kwargs): return api.Feed.find(*args, **kwargs) def create_feed (*args, **kwargs): @@ -111,5 +106,5 @@ def find_content (*args, **kwargs): # Terminate the SDK when Ctrl-C is pressed. -signal.signal(signal.SIGINT, lambda signal, frame: stop()) +signal.signal(signal.SIGINT, lambda signal, frame: sys.exit(1)) diff --git a/exp/api.py b/exp/api.py index f9f6124..46e285e 100644 --- a/exp/api.py +++ b/exp/api.py @@ -1,20 +1,26 @@ import urllib -#from .. lib import api_utils -#from .. lib.models.device import Device -#from .. lib.models.location import Location -#from .. lib.models.experience import Experience -#from .. lib.models.content import Content -#from .. lib.models.data import Data -#from .. lib.models.thing import Thing -#from .. lib.models.feed import Feed - from . import api_utils +class QueryResult (object): + + def __init__(self, results=None, total=None): + self.results = results + self.total = total + + def __iter__(self): + for result in self.results: + yield result + + def __str__(self): + return str([result for result in self.results]) + class Resource (object): + path = None + def __init__ (self, document): self.document = document @@ -37,14 +43,13 @@ def create (cls, document): return resource @classmethod - def find (cls, params): - response = api_utils.get(self._get_path(), params) - query = [cls(document) for document in response['results']] - query.total = response['total'] - return query + def find (cls, params=None): + response = api_utils.get(cls.path, params) + response['results'] = [cls(document) for document in response['results']] + return QueryResult(**response) def _get_path (self): - return self.__cls__.get_path() + '/' + self.uuid + return self.__cls__.path + '/' + self.uuid def save (self): return api_utils.patch(self._get_path(), self.document) @@ -62,99 +67,113 @@ def fling (payload, **kwargs): return self.get_channel().broadcast('fling', payload, **kwargs) -""" Content """ +class Device (Resource): + + path = '/api/devices' + + def identify (self): + return self.get_channel().broadcast('identify', null, 500) + + +class Thing (Resource): -def get_content(uuid): - return Content( - api_utils.get("/api/content/" + uuid + "/children"), - _is_children_populated=True) + path = '/api/things' -def find_content(**params): - query = api_utils.get('/api/content', params=params) - empty = [] - return [Content(x, _is_children_populated=False) for x in query.get("results", empty)] +class Feed (Resource): -""" Devices """ + path = '/api/connectors/feeds' -def find_devices(**params): - query = api_utils.get('/api/devices', params=params) - empty = [] - return [Device(x, _new=False) for x in query.get("results", empty)] + def get_data (self): + return api_utils(self._gat_path() + '/data') -def get_device(uuid): - return Device(api_utils.get('/api/devices/' + uuid), _new=False) -def create_device(document): - return Device(document).save() +class Experience (Resource): + path = '/api/experiences' -""" Things """ -def find_things(**params): - query = api_utils.get('/api/things', params=params) - empty = [] - return [Thing(x, _new=False) for x in query.get("results", empty)] +class Location (Resource): -def get_thing(uuid): - return Thing(api_utils.get('/api/things/' + uuid), _new=False) + path = '/api/locations' -def create_thing(document): - return Thing(document).save() + def get_zones (self): + return [Zone(document, self) for document in self.document['zones']] + def get_layout_url (self): + return self._get_path() + '/layout?_rt=' + authenticator.get_auth()['restrictedToken'] -""" Experiences """ -def find_experiences(**params): - query = api_utils.get('/api/experiences', params=params) - empty = [] - return [Experience(x, _new=False) for x in query.get("results", empty)] +class Zone (Resource): -def get_experience(uuid): - return Experience(api_utils.get('/api/experiences/' + uuid), _new=False) + def __init__ (self, document, location): + self._location = location + super(Zone, self).__init__(document) -def create_experience(document): - return Experience(document).save() + def save (self): + return self._location.save() + + def _get_channel_name (self): + return self._location.uuid + ':zone:' + self.document['key'] -""" Locations """ +class Data (Resource): -def find_locations(**params): - query = api_utils.get('/api/locations', params=params) - empty = [] - return [Location(x, _new=False) for x in query.get("results", empty)] + path = '/api/data' -def get_location(uuid): - return Location(api_utils.get('/api/locations/' + uuid), _new=False) + def get_path (self): + return self.__cls__.path + '/' + urllib.quote(this.document['group']) + '/' + urllib.quote(this.document['key']) -def create_location(document): - return Location(document).save() + @classmethod + def get (cls, group, key): + resource = cls({ 'group': group, 'key': key }) + resource.refresh() + return resource + @classmethod + def create (cls, group, key, value): + resource = cls({ 'group': group, 'key': key, 'value': value }) + resource.save() + return resource -""" Data """ + def get_channel_name (self): + return 'data' + ':' + this.document['key'] + ':' + this.document['group'] -def get_data(key, group): - key = urllib.quote(key, safe='') - group = urllib.quote(group, safe='') - return Data(**api_utils.get('/api/data/' + group + '/' + key)) -def find_data(**params): - query = api_utils.get('/api/data', params=params) - return [Data(**x) for x in query.get('results', [])] +class Content (Resource): -def create_data(**params): - return Data(**params).save() - + path = '/api/content' -""" Feeds """ + @property + def children (self): + if self._children is None: + if self.document['children']: + return [self.__cls__(document) for document in self.document['children']] + else: + self.refresh() + return self.children + return [] -def find_feeds(**params): - query = api_utils.get('/api/connectors/feeds', params=params) - empty = [] - return [Feed(x, _new=False) for x in query.get("results", empty)] + @property + def subtype (self): + return self.document['subtype'] + + def get_url (self): + auth = authenticator.get_auth() + delivery_url = auth['api']['host'] + '/api/delivery' + rt_string = '?_rt=' + auth['restrictedToken'] + if self.subtype == 'scala:content:file': + return delivery_url + this.document.path + rt_string + elif self.subtype == 'scala:content:app': + return delivery_url + this.document.path + rt_string + elif self.subtype == 'scala:content:url': + return self.document['url'] + + def get_variant_url (name): + auth = authenticator.get_auth() + delivery_url = auth['api']['host'] + '/api/delivery' + rt_string = '?_rt=' + auth['restrictedToken'] + if self.subtype == 'scala:content:file' and name in this.document['variants']: + return delivery_url + '?variant=' + urllib.quote(name) + rt_string -def get_feed(uuid): - return Feed(api_utils.get('/api/connectors/feeds/' + uuid), _new=False) -def create_feed(document): - return Feed(document).save() diff --git a/exp/authenticator.py b/exp/authenticator.py index 2153c14..6771e00 100644 --- a/exp/authenticator.py +++ b/exp/authenticator.py @@ -9,136 +9,77 @@ class _Authenticator (object): def __init__(self): self._auth = None - self._do_stop = False - self._exception = None self._options = None + self._time = None def get_auth(self): - if not self._auth: - raise NotAuthenticatedError() + if not self._options: + raise AuthenticationError('Attempted to authenticate without credentials.') + elif not self._auth: + self._login() + elif self._time < int(time.time()): + self._refresh() return self._auth - - def start (self, **options): + def set_options (self, **options): self._options = options - self._main_event_loop() - - - def stop (self): - self._do_stop = True - - - def wait (self): - while not self._auth and not self._do_stop and not self._exception: - time.sleep(.5) - if self._exception: - raise self._exception - - - def _main_event_loop (self): - while not self._do_stop: - if self._auth and self._is_refresh_needed: - self._auth = self._refresh() - elif not self._auth: - self._auth = self._login() - time.sleep(1) - - @property - def _is_refresh_needed (self): - return self._auth.get('expiration') / 1000.0 - int(time.time()) < 3600 - + self._auth = None def _login (self): - logger.info('Logging into EXP.') - try: - auth = self._get_login_response() - except AuthenticationError as exception: - logger.critical('Authentication failed. Credentials are invalid.') - self._exception = exception - except Exception as exception: - logger.warning('Authentication failed for unexpected reason. See debug log for more information.') - logger.debug(traceback.format_exc()) - else: - logger.info('Login successful.') - return auth - - def _refresh (self): - logger.info('Refreshing authentication token.') - try: - auth = self._get_refresh_response() - except Exception as exception: - logger.warning('Authentication token refresh failed.') - logger.debug(traceback.format_exc()) - else: - logger.info('Authentication token refresh successful.') - return auth - - - def _get_login_payload (self): + payload = {} if self._options.get('type') is 'user': - return self._get_user_login_payload() + logger.debug('Login started as a user.') + payload['username'] = self._options.get('username') + payload['password'] = self._options.get('password') + payload['organization'] = self._options.get('organization') elif self._options.get('type') is 'device': - return self._get_device_login_payload() + logger.debug('Login started as a device.') + token_payload = {} + token_payload['uuid'] = self._options.get('uuid') or '_' + token_payload['allowPairing'] = self._options.get('allow_pairing') + payload['token'] = self.generate_jwt(token_payload, self._options.get('secret') or '_') elif self._options.get('type') is 'consumer_app': - return self._get_consumer_app_login_payload() - - - def _get_user_login_payload (self): - payload = {} - payload['username'] = self._options.get('username') - payload['password'] = self._options.get('password') - payload['organization'] = self._options.get('organization') - return payload - - - def _get_device_login_payload (self): - payload = {} - token_payload = {} - token_payload['uuid'] = self._options.get('uuid') or '_' - token_payload['allowPairing'] = self._options.get('allow_pairing') - payload['token'] = self.generate_jwt(token_payload, self._options.get('secret') or '_') - return payload - - - def _get_consumer_app_login_payload (self): - payload = {} - token_payload = {} - token_payload['uuid'] = self._options.get('uuid') or '_' - payload['token'] = self.generate_jwt(token_payload, self._options.get('api_key')) - return payload - - - def _get_login_url (self): - return self._options.get('host') + '/api/auth/login' - - - def _get_refresh_url (self): - return self._options.get('host') + '/api/auth/token' - - - def _get_login_response (self): - response = requests.request('POST', self._get_login_url(), json=self._get_login_payload()) + logger.debug('Login started as a consumer app.') + token_payload = {} + token_payload['uuid'] = self._options.get('uuid') or '_' + payload['token'] = self.generate_jwt(token_payload, self._options.get('api_key')) + url = self._options.get('host') + '/api/auth/login' + response = requests.request('POST', url, json=payload) if response.status_code == 200: - return response.json() + self._on_success(response) + logger.debug('Login successful.') elif response.status_code == 401: - raise AuthenticationError('Authentiation failed.') + raise AuthenticationError('Login failed due to invalid credentials.') else: - raise UnexpectedError('Authentication failed due to an unexpected status code: %s.' % response.status_code) - + raise UnexpectedError('Login failed due to an unexpected HTTP status code: %s.' % response.status_code) - def _get_refresh_headers (self): - return { 'Authorization': 'Bearer ' + self._auth.get('token') } - - - def _get_refresh_response (self): - response = requests.request('POST', self._get_refresh_url(), headers=self._get_refresh_headers()) + def _refresh (self): + logger.debug('Authentication token refresh starting.') + url = self._options.get('host') + '/api/auth/token' + headers = { 'Authorization': 'Bearer ' + self._auth.get('token') } + try: + response = requests.request('POST', url, headers=headers) + except Exception as exception: + raise UnexpectedError('Authentication token refresh encountered an unexpected error.') if response.status_code is 200: - return response.json() + self._on_success(response) + logger.debug('Authentication token refresh successful.') elif response.status_code is 401: - raise AuthenticationError('Authentication failed.') + logger.debug('Authentication token refresh failed due to expired or invalid token.') + self._login() else: - raise UnexpectedError('Authentication token refresh failed due to an unexpected status code: %d.' % response.status_code) + raise UnexpectedError('Authentication token refresh request failed due to an unexpected HTTP status code: %d' % response.status_code) + def _on_success (self, response): + logger.debug('Authentication update starting.') + try: + auth = response.json() + except Exception as exception: + raise UnexpectedError('An unexpected error has occured parsing authentication response.') + else: + self._auth = auth + self._time = (int(time.time()) + auth['expiration'] * 3.0 / 1000.0 ) / 4.0 + logger.debug('Authentication update successful: %s' % auth) @staticmethod def generate_jwt (payload, secret): diff --git a/exp/exceptions.py b/exp/exceptions.py index 150c93e..af436a9 100644 --- a/exp/exceptions.py +++ b/exp/exceptions.py @@ -1,14 +1,21 @@ +from .logger import logger class ExpError (Exception): - pass + def __init__(self, message): + logger.error('') + class AuthenticationError (ExpError): pass class UnexpectedError (ExpError): - pass + + def __init__ (self, *arg, **kwargs): + logger.debug('An unexpected error occured:') + logger.debug(traceback.format_exc()) + super(UnexpectedError, self).__init__(*args, **kwargs) class NotAuthenticatedError (ExpError): pass diff --git a/exp/network.py b/exp/network.py index 2e8d30c..f6e3094 100644 --- a/exp/network.py +++ b/exp/network.py @@ -2,7 +2,7 @@ import threading import uuid - +from .logger import logger import urlparse from socketIO_client import SocketIO, BaseNamespace @@ -117,7 +117,7 @@ def listen (self, name, **kwargs): @property def has_listeners (self): - return any([namepsace.has_listeners for key, namepspace in self._namespaces.iteritems()]) + return any([namespace.has_listeners for key, namespace in self._namespaces.iteritems()]) @@ -129,6 +129,13 @@ def __init__(self): self._auth = None self._do_stop = False self._channels = {} + self._parent_thread = threading.currentThread() + + network_thread = threading.Thread(target=lambda: self._main_event_loop()) + network_thread.start() + + def set_options (self, **options): + self._options = options def get_channel(self, name, system=False, consumer=False): id = self._generate_channel_id(name, system, consumer) @@ -168,20 +175,31 @@ def subscribe (self, id): if self.is_connected: self._socket.emit('subscribe', [id]) - def start (self, **options): - self._options = options - self._main_event_loop() def wait (self): while not self.is_connected: time.sleep(1) + def _disconnect (self): + if self._socket: + self._socket.disconnect() + self._socket = None + @property def is_connected(self): return self._socket and self._socket.connected def _main_event_loop (self): - while not self._do_stop: + while True: + if not self._parent_thread.is_alive(): + self._disconnect() + break; + elif not self._options: + time.sleep(1) + elif not self._options.get('enable_events'): + self._disconnect() + time.sleep(1) + try: auth = authenticator.get_auth() except NotAuthenticatedError: @@ -198,9 +216,6 @@ def _main_event_loop (self): else: time.sleep(1) - def stop (self): - self._do_stop = True - class SocketNamespace(BaseNamespace): diff --git a/exp/runtime.py b/exp/runtime.py index 89e5383..23c8d95 100644 --- a/exp/runtime.py +++ b/exp/runtime.py @@ -14,9 +14,6 @@ class _Runtime (object): - def __init__ (self): - self._is_started = False - def start (self, enable_events=True, host='https://api.goexp.io/', **options): options['host'] = host @@ -45,27 +42,9 @@ def start (self, enable_events=True, host='https://api.goexp.io/', **options): else: raise OptionsError('Please specify authentication type.') - if self._is_started: - raise RuntimeError('Runtime already started.') - self._is_started = True - - authenticator_thread = threading.Thread(target=lambda: authenticator.start(**options)) - authenticator_thread.start() - - try: - authenticator.wait() - except Exception as exception: - self.stop() - raise - - if enable_events: - network_thread = threading.Thread(target=lambda: network.start(**options)) - network_thread.start() - network.wait() - + network.set_options(**options) + authenticator.set_options(**options) + authenticator.get_auth() - def stop (self): - authenticator.stop() - network.stop() runtime = _Runtime() diff --git a/test2.py b/test2.py deleted file mode 100644 index 6b3b791..0000000 --- a/test2.py +++ /dev/null @@ -1,23 +0,0 @@ -import time -import exp -from collections import OrderedDict - -exp.runtime.start(enableEvents=False, host="https://api.exp.scala.com/", consumerAppUuid="503bc281-3fc3-4f70-b22e-a7a9288e05eb", apiKey="28f2eebf745fb84b855d0619dc853351b9139e83cdd509f2968308be21a3bfeb2c232c58dc1ffbe7168a80151c83b3f9") - -time.sleep(5) -while True: - data = exp.api.get_feed('2aea09ff-e014-4fc3-8e57-d374f577e05d').get_data() - mydict = data['items'] - d_sorted = sorted(mydict, key=lambda d: d.get('raw', {}).get('closed_at'), reverse=True) - for order in d_sorted[:4]: - print 'order id ',order['id'] - print 'create date ',order['date'] - print 'closed date ',order['raw']['closed_at'] - print order['raw']['note'] - for item in order['raw']['line_items']: - print item['name'] - print item['quantity'] - print item['product_id'] - print item['price'] - time.sleep(5) -exp.runtime.stop() diff --git a/tests/network_integration_test.py b/tests/network_integration_test.py new file mode 100644 index 0000000..c3e45c3 --- /dev/null +++ b/tests/network_integration_test.py @@ -0,0 +1,52 @@ +import unittest +import exp +import os + +class TestException (Exception): + pass + + + +user_credentials = {} +user_credentials['username'] = os.environ['EXP_USERNAME'] +user_credentials['password'] = os.environ['EXP_PASSWORD'] +user_credentials['organization'] = os.environ['EXP_ORGANIZATION'] +user_credentials['host'] = os.environ['EXP_HOST'] + + + + + + +class AutoReloader (object): + + def setUp (self): + #exp.stop() + reload(exp) + exp.start(**self.credentials) + + def tearDown (self): + exp.stop() + + +class UserAuthenticator (AutoReloader): + + credentials = user_credentials + + + + +class Broadcasting (object): + pass + +class Responding (object): + pass + +class Listening (object): + pass + + +class Test1 (UserAuthenticator, unittest.TestCase): + + def test_something (self): + pass \ No newline at end of file diff --git a/tests/runtime_start_test.py b/tests/runtime_start_unit_test.py similarity index 99% rename from tests/runtime_start_test.py rename to tests/runtime_start_unit_test.py index 02d931c..2bc5a1e 100644 --- a/tests/runtime_start_test.py +++ b/tests/runtime_start_unit_test.py @@ -45,6 +45,7 @@ def on_authenticator_wait(self): self.did_authenticator_wait = True def setUp (self): + exp._runtime._is_started = False self.stub() def tearDown (self): From 8bda428fd19a2d204e0811f00997cd04dd3d4e46 Mon Sep 17 00:00:00 2001 From: James Dalessio Date: Fri, 4 Mar 2016 17:40:13 -0500 Subject: [PATCH 024/104] Moving on to testing. --- exp/__init__.py | 4 + exp/api.py | 31 ++-- exp/api_utils.py | 88 ++++++---- exp/authenticator.py | 30 ++-- exp/exceptions.py | 24 +-- exp/logger.py | 2 +- exp/network.py | 263 ++++++++++++++++-------------- exp/runtime.py | 49 ++++-- tests/api_integration_test.py | 185 +++++++++++++++++++++ tests/network_integration_test.py | 53 ++++-- tests/runtime_start_unit_test.py | 234 -------------------------- tests/runtime_unit_test.py | 142 ++++++++++++++++ 12 files changed, 658 insertions(+), 447 deletions(-) create mode 100644 tests/api_integration_test.py delete mode 100644 tests/runtime_start_unit_test.py create mode 100644 tests/runtime_unit_test.py diff --git a/exp/__init__.py b/exp/__init__.py index 44d064c..1be085b 100644 --- a/exp/__init__.py +++ b/exp/__init__.py @@ -5,6 +5,7 @@ from .runtime import runtime as _runtime from .network import network as _network +from .network import socket as _socket from .authenticator import authenticator as _authenticator from .exceptions import * from . import api @@ -21,6 +22,9 @@ def get_auth (): def get_channel(*args, **kwargs): return _network.get_channel(*args, **kwargs) +def get_connection_status(*args, **kwargs): + return _socket.is_connected + def get (*args, **kwargs): return api_utils.get(*args, **kwargs) diff --git a/exp/api.py b/exp/api.py index 46e285e..6e3ecc0 100644 --- a/exp/api.py +++ b/exp/api.py @@ -1,6 +1,7 @@ import urllib from . import api_utils +from .network import network class QueryResult (object): @@ -16,6 +17,12 @@ def __iter__(self): def __str__(self): return str([result for result in self.results]) + def __contains__ (self, item): + return item in self.results + + def __len__ (self): + return len(self.results) + class Resource (object): @@ -34,13 +41,13 @@ def _get_path (cls): @classmethod def get (cls, uuid): - return cls(api_utils.get(self.get_path() + '/' + uuid)) + return cls(api_utils.get(cls.path + '/' + uuid)) @classmethod - def create (cls, document): - resource = cls(document) - resource.save() - return resource + def create (cls, document=None): + if not document: document = {} + document = api_utils.post(cls.path, document) + return cls(document) @classmethod def find (cls, params=None): @@ -49,10 +56,14 @@ def find (cls, params=None): return QueryResult(**response) def _get_path (self): - return self.__cls__.path + '/' + self.uuid + return self.__class__.path + '/' + self.uuid + + def delete (self): + return api_utils.delete(self._get_path()) def save (self): - return api_utils.patch(self._get_path(), self.document) + document = api_utils.patch(self._get_path(), self.document) + self.document = document def refresh (self): self.document = api_utils.get(this._get_path()) @@ -72,7 +83,7 @@ class Device (Resource): path = '/api/devices' def identify (self): - return self.get_channel().broadcast('identify', null, 500) + return self.get_channel().broadcast('identify', None, 500) class Thing (Resource): @@ -122,7 +133,7 @@ class Data (Resource): path = '/api/data' def get_path (self): - return self.__cls__.path + '/' + urllib.quote(this.document['group']) + '/' + urllib.quote(this.document['key']) + return self.__class__.path + '/' + urllib.quote(this.document['group']) + '/' + urllib.quote(this.document['key']) @classmethod def get (cls, group, key): @@ -148,7 +159,7 @@ class Content (Resource): def children (self): if self._children is None: if self.document['children']: - return [self.__cls__(document) for document in self.document['children']] + return [self.__class__(document) for document in self.document['children']] else: self.refresh() return self.children diff --git a/exp/api_utils.py b/exp/api_utils.py index 56c3c9a..2ed3aa1 100644 --- a/exp/api_utils.py +++ b/exp/api_utils.py @@ -1,46 +1,74 @@ import requests import urllib +from .logger import logger +from .exceptions import ApiError, UnexpectedError from .authenticator import authenticator +def _on_error(exception): + if hasattr(exception, 'response'): + try: + payload = exception.response.json() + except Exception: + raise UnexpectedError() + else: + raise ApiError(payload) + else: + raise UnexpectedError() + def get(path, params=None): - url = "{0}{1}".format(authenticator.get_auth()['api']['host'], urllib.quote(path)) - headers = {} - headers["Authorization"] = "Bearer " + authenticator.get_auth()['token'] - response = requests.get(url, timeout=10, params=params, headers=headers) - response.raise_for_status() - return response.json() + try: + url = "{0}{1}".format(authenticator.get_auth()['api']['host'], urllib.quote(path)) + headers = {} + headers["Authorization"] = "Bearer " + authenticator.get_auth()['token'] + response = requests.get(url, timeout=10, params=params, headers=headers) + response.raise_for_status() + return response.json() + except Exception as exception: + return _on_error(exception) def post(path, payload=None, params=None): - url = "{0}{1}".format(authenticator.get_auth()['api']['host'], urllib.quote(path)) - headers = {} - headers["Authorization"] = "Bearer " + authenticator.get_auth()['token'] - response = requests.post(url, timeout=10, params=params, json=payload, headers=headers) - response.raise_for_status() - return response.json() + try: + url = "{0}{1}".format(authenticator.get_auth()['api']['host'], urllib.quote(path)) + headers = {} + headers["Authorization"] = "Bearer " + authenticator.get_auth()['token'] + response = requests.post(url, timeout=10, params=params, json=payload, headers=headers) + response.raise_for_status() + return response.json() + except Exception as exception: + return _on_error(exception) def patch(path, payload=None, params=None): - url = "{0}{1}".format(authenticator.get_auth()['api']['host'], urllib.quote(path)) - headers = {} - headers["Authorization"] = "Bearer " + authenticator.get_auth()['token'] - response = requests.patch(url, timeout=10, params=params, json=payload, headers=headers) - response.raise_for_status() - return response.json() + try: + url = "{0}{1}".format(authenticator.get_auth()['api']['host'], urllib.quote(path)) + headers = {} + headers["Authorization"] = "Bearer " + authenticator.get_auth()['token'] + response = requests.patch(url, timeout=10, params=params, json=payload, headers=headers) + response.raise_for_status() + return response.json() + except Exception as exception: + return _on_error(exception) def put(path, payload=None, params=None): - url = "{0}{1}".format(authenticator.get_auth()['api']['host'], urllib.quote(path)) - headers = {} - headers["Authorization"] = "Bearer " + authenticator.get_auth()['token'] - response = requests.put(url, timeout=10, params=params, json=payload, headers=headers) - response.raise_for_status() - return response.json() + try: + url = "{0}{1}".format(authenticator.get_auth()['api']['host'], urllib.quote(path)) + headers = {} + headers["Authorization"] = "Bearer " + authenticator.get_auth()['token'] + response = requests.put(url, timeout=10, params=params, json=payload, headers=headers) + response.raise_for_status() + return response.json() + except Exception as exception: + return _on_error(exception) def delete(path, payload=None, params=None): - url = "{0}{1}".format(authenticator.get_auth()['api']['host'], urllib.quote(path)) - headers = {} - headers["Authorization"] = "Bearer " + authenticator.get_auth()['token'] - response = requests.delete(url, timeout=10, params=params, json=payload, headers=headers) - response.raise_for_status() - return response.json() + try: + url = "{0}{1}".format(authenticator.get_auth()['api']['host'], urllib.quote(path)) + headers = {} + headers["Authorization"] = "Bearer " + authenticator.get_auth()['token'] + response = requests.delete(url, timeout=10, params=params, json=payload, headers=headers) + response.raise_for_status() + return None + except Exception as exception: + return _on_error(exception) diff --git a/exp/authenticator.py b/exp/authenticator.py index 6771e00..ae68902 100644 --- a/exp/authenticator.py +++ b/exp/authenticator.py @@ -1,9 +1,13 @@ import time import traceback import requests +import json +import base64 +import hmac +import hashlib from .logger import logger -from .exceptions import AuthenticationError, UnexpectedError, NotAuthenticatedError +from .exceptions import AuthenticationError, UnexpectedError, RuntimeError class _Authenticator (object): @@ -12,19 +16,19 @@ def __init__(self): self._options = None self._time = None + def configure (self, **options): + self._options = options + self._auth = None + def get_auth(self): if not self._options: - raise AuthenticationError('Attempted to authenticate without credentials.') + raise RuntimeError('SDK has not been configured.') elif not self._auth: self._login() elif self._time < int(time.time()): self._refresh() return self._auth - def set_options (self, **options): - self._options = options - self._auth = None - def _login (self): payload = {} if self._options.get('type') is 'user': @@ -77,6 +81,8 @@ def _on_success (self, response): except Exception as exception: raise UnexpectedError('An unexpected error has occured parsing authentication response.') else: + logger.critical(auth) + logger.critical(response.status_code) self._auth = auth self._time = (int(time.time()) + auth['expiration'] * 3.0 / 1000.0 ) / 4.0 logger.debug('Authentication update successful: %s' % auth) @@ -84,16 +90,16 @@ def _on_success (self, response): @staticmethod def generate_jwt (payload, secret): - algorithm = { 'alg': 'hs256', 'typ': 'JWT' } - algorithm_json = json.dumps(algorithm, separators(',', ':')).encode('utf-8') - algorithm_b64 = base64.urlsafe_b64encode(algorithm_json) + algorithm = { 'alg': 'HS256', 'typ': 'JWT' } + algorithm_json = json.dumps(algorithm, separators=(',', ':')).decode('utf-8') + algorithm_b64 = base64.urlsafe_b64encode(algorithm_json).rstrip('=') payload['exp'] = (int(time.time()) + 30) * 1000 - payload_json = json.dumps(payload, separators(',', ':')).encode('utf-8') + payload_json = json.dumps(payload, separators=(',', ':')) payload_b64 = base64.urlsafe_b64encode(payload_json).rstrip('=') - signature = hmac.new(secret.encode('utf-8'), '.'.join([algorithm_b64, payload_b64]), hashlib.sha256).digest() - signature_b64 = urlsafe_b64encode(signature).rstrip('=') + signature = hmac.new(secret, '.'.join([algorithm_b64, payload_b64]), hashlib.sha256).digest() + signature_b64 = base64.urlsafe_b64encode(signature).rstrip('=') return '.'.join([algorithm_b64, payload_b64, signature_b64]) diff --git a/exp/exceptions.py b/exp/exceptions.py index af436a9..10fed04 100644 --- a/exp/exceptions.py +++ b/exp/exceptions.py @@ -1,10 +1,9 @@ from .logger import logger +import traceback class ExpError (Exception): - def __init__(self, message): - logger.error('') - + pass class AuthenticationError (ExpError): @@ -17,13 +16,18 @@ def __init__ (self, *arg, **kwargs): logger.debug(traceback.format_exc()) super(UnexpectedError, self).__init__(*args, **kwargs) -class NotAuthenticatedError (ExpError): - pass - -# Options to exp.start were invalid or incomplete. -class OptionsError(ExpError): - pass - # Cannot execute desired action. class RuntimeError(ExpError): pass + +class ApiError(ExpError): + + def __init__(self, payload): + logger.debug('An api error has occured.') + logger.debug(traceback.format_exc()) + self.message = payload.get('message') + self.code = payload.get('code') + + def __str__(self): + return '%s: %s' % (self.code, self.message) + diff --git a/exp/logger.py b/exp/logger.py index 8336c51..775b1c5 100644 --- a/exp/logger.py +++ b/exp/logger.py @@ -16,4 +16,4 @@ logger = logging.getLogger('exp') logger.addHandler(_file_handler) logger.addHandler(_stream_handler) -logger.setLevel(logging.DEBUG) \ No newline at end of file +logger.setLevel(logging.DEBUG) diff --git a/exp/network.py b/exp/network.py index f6e3094..c27b6c6 100644 --- a/exp/network.py +++ b/exp/network.py @@ -11,213 +11,234 @@ from . import api_utils - +import traceback from .authenticator import authenticator -from .exceptions import NotAuthenticatedError -class Broadcast (object): + + +class _Broadcast (object): + + _path = '/api/networks/current/response' def __init__(self, message): self._message = message - self.time = int(time.time()) + self._time = int(time.time()) @property def payload(self): - return self._message.get('payload', None) + return self._message['payload'] def respond(self, payload): - message = {} - message['id'] = self._message['id'] - message['channel'] = self._message['channel'] - message['payload'] = payload - return api_utils.post('/api/networks/current/responses', payload=message) + response = { 'id': self._message['id'], 'channel': self._message['channel'], 'payload': payload } + return api_utils.post(self._path, response) -class Listener (object): +class _Listener (object): - def __init__(self, id, namespace, max_age=60): - self._id = id + def __init__(self, namespace, max_age=60): self._namespace = namespace + self._max_age = max_age self._event = threading.Event() self._broadcasts = [] - self._max_age = max_age - def receive (self, message): - broadcast = Broadcast(message) + def _receive (self, message): + broadcast = _Broadcast(message) now = int(time.time()) - self._broadcasts = [broadcast for broadcast in self._broadcasts if now - broadcast.time < self._max_age] + self._broadcasts = [broadcast for broadcast in self._broadcasts if now - broadcast._time < self._max_age] self._broadcasts.append(broadcast) self._event.set() def wait (self, timeout=None): - try: - return self._broadcasts.pop(0) - except IndexError: + if not self._broadcasts: self._event.clear() self._event.wait(timeout) - try: + if self._broadcasts: return self._broadcasts.pop(0) - except IndexError: - return None def cancel(self): - self._namespace.cancel(self._id) + self._namespace.cancel_listener(self) -class Namespace (object): +class _Namespace (object): def __init__(self): - self._listeners = {} + self._listeners = [] - def receive (self, message): - for id, listener in self._listeners.iteritems(): - listener.receive(message) + def listen (self, **kwargs): + listener = _Listener(self, **kwargs) + self._listeners.append(listener) + return listener - def cancel (self, id): - del self._listeners[id] + def cancel_listener(self, listener): + if listener in self._listeners: + self._listeners.remove(listener) - def listen (self, **kwargs): - id = str(uuid.uuid4()) - self._listeners[id] = Listener(id, self, **kwargs) - return self._listeners[id] + def receive (self, message): + [listener._receive(message) for listener in self._listeners] @property - def has_listeners(self): + def has_listeners (self): return bool(self._listeners) +class _Channel (object): -class Channel (object): - - def __init__(self, id): + def __init__ (self, id): self._id = id self._namespaces = {} self._subscription = threading.Event() - def receive (self, message): - if message.get('name') in self._namespaces: - self._namespaces[message['name']].receive(message) - - def broadcast (self, name, payload, timeout=5): - message = {} - message['channel'] = self._id - message['name'] = name - message['payload'] = payload - return api_utils.post('/api/networks/current/broadcasts', payload=message, params={ 'timeout': timeout }) + def broadcast (self, name, payload=None, timeout=5): + path = '/api/networks/current/broadcasts' + message = {'channel': self._id, 'name': name, 'payload': payload} + params = {'timeout': timeout} + return api_utils.post(path, message, params) def listen (self, name, **kwargs): if not self._namespaces.get(name): - self._namespaces[name] = Namespace() + self._namespaces[name] = _Namespace() listener = self._namespaces[name].listen(**kwargs) if not self._subscription.is_set(): - network.subscribe(self._id) - self._subscription.wait() + socket.emit('subscribe', [self._id]) + self._subscription.wait() return listener + def _receive (self, message): + if message['name'] in self._namespaces: + self._namespaces[message['name']].receive(message) + @property - def has_listeners (self): - return any([namespace.has_listeners for key, namespace in self._namespaces.iteritems()]) + def _has_listeners (self): + return any([namespace.has_listeners for name, namespace in self._namespaces.iteritems()]) + + + -class Network (object): + + + +class _Network (object): def __init__(self): - self._options = None - self._socket = None - self._auth = None - self._do_stop = False self._channels = {} - self._parent_thread = threading.currentThread() - - network_thread = threading.Thread(target=lambda: self._main_event_loop()) - network_thread.start() + self._auth = None + self._options = None + self._parent = threading.currentThread() + self._thread = None - def set_options (self, **options): + def configure (self, **options): + self._auth = None self._options = options + self._parent = threading.currentThread() + self._thread = threading.Thread(target=lambda: self._main_event_loop()) + self._thread.start() def get_channel(self, name, system=False, consumer=False): - id = self._generate_channel_id(name, system, consumer) - if not self._channels.get(id): - self._channels[id] = Channel(id) - return self._channels[id] + channel_id = self._generate_channel_id(name, system, consumer) + return self.get_channel_by_id(channel_id) + + def get_channel_by_id (self, channel_id): + if channel_id not in self._channels: + self._channels[channel_id] = _Channel(channel_id) + return self._channels[channel_id] - def _generate_channel_id (self, name, system, consumer): + @staticmethod + def _generate_channel_id (name, system=False, consumer=False): organization = authenticator.get_auth()['identity']['organization'] raw_id = [organization, name, 1 if system else 0, 1 if consumer else 0] json_id = json.dumps(raw_id, separators=(',', ':')) return base64.b64encode(json_id) + """ Callbacks on socket events """ + def on_connect(self): - ids = [] - for id, channel in self._channels.iteritems(): - if channel.has_listeners: - ids.append(id) - if ids and self.is_connected: - self._socket.emit('subscribe', ids) + ids = [id for id, channel in self._channels.iteritems() if channel._has_listeners] + socket.emit('subscribe', ids) def on_disconnect(self): - for id, channel in self._channels.iteritems(): - channel._subscription.clear() + [channel._subscription.clear() for id, channel in self._channels.iteritems()] def on_subscribed(self, ids): - for id in ids: - if not self._channels.get(id): - self._channels[id] = Channel(id) - self._channels[id]._subscription.set() + [self.get_channel_by_id(id)._subscription.set() for id in ids] def on_broadcast (self, message): - if message.get('channel') in self._channels: - self._channels[message['channel']].receive(message) + self.get_channel_by_id(message['channel'])._receive(message) - def subscribe (self, id): - if self.is_connected: - self._socket.emit('subscribe', [id]) + def _main_event_loop (self): - def wait (self): - while not self.is_connected: - time.sleep(1) + while True: - def _disconnect (self): - if self._socket: - self._socket.disconnect() - self._socket = None + """ Break if parent thread is dead. """ + if not self._options['enable_network'] or not self._parent.is_alive(): + socket.disconnect() + break; + + """ Attempt to retrieve current auth. If auth has changed, reconnect. """ + try: + auth = authenticator.get_auth() + if auth != self._auth: + self._auth = auth + socket.connect(**self._auth) + except Exception: + time.sleep(1) + continue + + """ Listen for socket events. """ + socket.wait(1) + + + +class _Socket (object): + + def __init__(self): + self._socket = None @property - def is_connected(self): + def is_connected (self): return self._socket and self._socket.connected - def _main_event_loop (self): - while True: - if not self._parent_thread.is_alive(): - self._disconnect() - break; - elif not self._options: - time.sleep(1) - elif not self._options.get('enable_events'): - self._disconnect() - time.sleep(1) + def connect (self, **auth): + self.disconnect() + params = { 'token': auth['token'] } + parsed_host = urlparse.urlparse(auth['network']['host']) + self._socket = SocketIO(parsed_host.hostname, + parsed_host.port, + params=params, + Namespace=_SocketHandler, + hurry_interval_in_seconds=10) + + def disconnect (self): + if self._socket: + network.on_disconnect() + try: + self._socket.disconnect() + self._socket = None + except: + pass + network.on_disconnect() + def wait (self, seconds): + if self.is_connected: try: - auth = authenticator.get_auth() - except NotAuthenticatedError: - auth = None - return - if auth != self._auth: - self._auth = auth - if self._socket: - self._socket.disconnect() - parsed_host = urlparse.urlparse(self._auth.get('network', {}).get('host')) - self._socket = SocketIO(parsed_host.hostname, parsed_host.port, params={ "token": self._auth.get('token') }, Namespace=SocketNamespace, hurry_interval_in_seconds=10) - if self.is_connected: - self._socket.wait(seconds=1) - else: - time.sleep(1) + self._socket.wait(seconds=seconds) + except Exception: + logger.warning('Error in network thread.') + logger.debug('Error in network thread: %s', traceback.format_exc()) + time.sleep(seconds) + else: + time.sleep(seconds) + + def emit (self, name, payload): + if self.is_connected: + self._socket.emit(name, payload) -class SocketNamespace(BaseNamespace): + +class _SocketHandler(BaseNamespace): def on_broadcast(self, message): network.on_broadcast(message) @@ -232,4 +253,6 @@ def on_subscribed(self, ids): network.on_subscribed(ids) -network = Network() \ No newline at end of file + +socket = _Socket() +network = _Network() \ No newline at end of file diff --git a/exp/runtime.py b/exp/runtime.py index 23c8d95..1aeb428 100644 --- a/exp/runtime.py +++ b/exp/runtime.py @@ -10,41 +10,58 @@ from .logger import logger from .network import network from .authenticator import authenticator -from .exceptions import RuntimeError, OptionsError +from .exceptions import RuntimeError, ExpError +import traceback -class _Runtime (object): +class Runtime (object): - def start (self, enable_events=True, host='https://api.goexp.io/', **options): + is_started = False + + def start (self, *args, **kwargs): + try: + self._start(*args, **kwargs) + except ExpError: + logger.debug('An error has occured:') + logger.debug(traceback.format_exc()) + raise + except Exception: + logger.debug('An unexpected error has occured:') + logger.debug(traceback.format_exc()) + raise UnexpectedError('An unexpected error has occured.') + else: + logger.info('EXP SDK started successfully.') + + def _start (self, enable_network=True, host='https://api.goexp.io/', **options): options['host'] = host - options['enable_events'] = enable_events + options['enable_network'] = enable_network if options.get('type') is 'user' or ((options.get('username') or options.get('password') or options.get('organization')) and not options.get('type')): options['type'] = 'user' if not options.get('username'): - raise OptionsError('Please specify the username.') + raise RuntimeError('Please specify the username.') if not options.get('password'): - raise OptionsError('Please specify the password.') + raise RuntimeError('Please specify the password.') if not options.get('organization'): - raise OptionsError('Please specify the organization.') + raise RuntimeError('Please specify the organization.') elif options.get('type') is 'device' or ((options.get('secret') or options.get('allow_pairing')) and not options.get('type')): options['type'] = 'device' if not options.get('uuid') and not options.get('allow_pairing'): - raise OptionsError('Please specify the device uuid.') + raise RuntimeError('Please specify the device uuid.') if not options.get('secret') and not options.get('allow_pairing'): - raise OptionsError('Please specify the device secret.') + raise RuntimeError('Please specify the device secret.') elif options.get('type') is 'consumer_app' or (options.get('api_key') and not options.get('type')): options['type'] = 'consumer_app' if not options.get('uuid'): - raise OptionsError('Please specify the consumer app uuid.') + raise RuntimeError('Please specify the consumer app uuid.') if not options.get('api_key'): - raise OptionsError('Please specify the consumer app api key.') + raise RuntimeError('Please specify the consumer app api key.') else: - raise OptionsError('Please specify authentication type.') + raise RuntimeError('Please specify authentication type.') - network.set_options(**options) - authenticator.set_options(**options) - authenticator.get_auth() + network.configure(**options) + authenticator.configure(**options) + authenticator.get_auth() # Block until authentication has been received. -runtime = _Runtime() +runtime = Runtime() diff --git a/tests/api_integration_test.py b/tests/api_integration_test.py new file mode 100644 index 0000000..6ac2e21 --- /dev/null +++ b/tests/api_integration_test.py @@ -0,0 +1,185 @@ +import unittest +import exp +import os +from exp.network import network +from exp.runtime import runtime +import time + +import random +import string + +user_credentials = {} +user_credentials['username'] = os.environ['EXP_USERNAME'] +user_credentials['password'] = os.environ['EXP_PASSWORD'] +user_credentials['organization'] = os.environ['EXP_ORGANIZATION'] +user_credentials['host'] = os.environ['EXP_HOST'] +user_credentials['enable_network'] = False + + + +device_credentials = {} +device_credentials['uuid'] = os.environ['EXP_DEVICE_UUID'] +device_credentials['secret'] = os.environ['EXP_SECRET'] +device_credentials['host'] = os.environ['EXP_HOST'] +device_credentials['enable_network'] = False + + + + +class AutoReloader (object): + + def setUp (self): + exp.start(**self.credentials) + + def tearDown (self): + pass#time.sleep(1) + + + +class UserAuthenticator (AutoReloader): + + credentials = device_credentials # Temp because stormpath is slow. + + +def random_word (): + return ''.join(random.choice(string.lowercase) for i in range(20)) + + + +class Devices (object): + + + def test_create (self): + device = exp.create_device({ 'name': random_word() }) + device.delete() + + def test_get(self): + device = exp.create_device({ 'name': random_word() }) + exp.get_device(device.document['uuid']) + device.delete() + + def test_save (self): + device = exp.create_device({ 'name': random_word() }) + device.save() + device.delete() + + def test_identify (self): + device = exp.create_device({ 'name': random_word() }) + device.identify() + + def test_find (self): + devices = exp.find_devices() + + +class Experiences (object): + + def test_create (self): + experience = exp.create_experience({ 'name': random_word() }) + experience.delete() + + def test_get(self): + experience = exp.create_experience({ 'name': random_word() }) + exp.get_experience(experience.document['uuid']) + experience.delete() + + def test_save (self): + experience = exp.create_experience({ 'name': random_word() }) + experience.save() + experience.delete() + + def test_find (self): + experiences = exp.find_experiences() + + + +class Feeds (object): + + @unittest.skip + def test_create (self): + feed = exp.create_feed({ 'name': random_word() }) + feed.delete() + + @unittest.skip + def test_get(self): + feed = exp.create_feed({ 'name': random_word() }) + exp.get_feed(feed.document['uuid']) + feed.delete() + + @unittest.skip + def test_save (self): + feed = exp.create_feed({ 'name': random_word() }) + feed.save() + feed.delete() + + def test_find (self): + feeds = exp.find_feeds() + + + +class Locations (object): + + def test_create (self): + location = exp.create_location({ 'name': random_word() }) + location.delete() + + def test_get(self): + location = exp.create_location({ 'name': random_word() }) + exp.get_location(location.document['uuid']) + location.delete() + + def test_save (self): + location = exp.create_location({ 'name': random_word() }) + location.save() + location.delete() + + def test_find (self): + locations = exp.find_locations() + + + +class Things (object): + + @unittest.skip + def test_create (self): + thing = exp.create_thing({ 'name': random_word() }) + thing.delete() + + @unittest.skip + def test_get(self): + thing = exp.create_thing({ 'name': random_word() }) + exp.get_thing(thing.document['uuid']) + thing.delete() + + @unittest.skip + def test_save (self): + thing = exp.create_thing({ 'name': random_word() }) + thing.save() + thing.delete() + + def test_find (self): + things = exp.find_things() + + + +class Content (object): + pass + + + +class Data (object): + pass + + + +class Zones (object): + pass + + +class TestUserDevices (Devices, UserAuthenticator, AutoReloader): pass +class TestUserExperiences (Experiences, UserAuthenticator, AutoReloader): pass +class TestUserLocations (Locations, UserAuthenticator, AutoReloader): pass +class TestUserFeeds (Feeds, UserAuthenticator, AutoReloader): pass +class TestUserThings (Things, UserAuthenticator, AutoReloader): pass +class TestUserContent (Content, UserAuthenticator, AutoReloader): pass +class TestUserData (Data, UserAuthenticator, AutoReloader): pass +class TestUserZones (Zones, UserAuthenticator, AutoReloader): pass \ No newline at end of file diff --git a/tests/network_integration_test.py b/tests/network_integration_test.py index c3e45c3..9432282 100644 --- a/tests/network_integration_test.py +++ b/tests/network_integration_test.py @@ -1,10 +1,9 @@ import unittest import exp import os - -class TestException (Exception): - pass - +from exp.network import network +from exp.runtime import runtime +import time user_credentials = {} @@ -16,17 +15,14 @@ class TestException (Exception): - - class AutoReloader (object): def setUp (self): - #exp.stop() - reload(exp) exp.start(**self.credentials) def tearDown (self): - exp.stop() + pass#time.sleep(1) + class UserAuthenticator (AutoReloader): @@ -37,16 +33,45 @@ class UserAuthenticator (AutoReloader): class Broadcasting (object): - pass + + def test_broadcasting(self): + channel = exp.get_channel('test1') + channel.broadcast('test2') + channel.broadcast('test2', None) + channel.broadcast('test2', payload=None) + channel.broadcast('test2', timeout=1) class Responding (object): pass class Listening (object): - pass + + def test_listening (self): + channel = exp.get_channel('test3') + listener = channel.listen('test4') + channel.broadcast('test4') + broadcast = listener.wait(3) + if not broadcast: + raise Exception() + + +class Cancelling (object): + + def test_listener_cancel (self): + channel = exp.get_channel('test51') + listener = channel.listen('test4') + listener.cancel() + channel.broadcast('test4') + broadcast = listener.wait(1) + if broadcast: + raise Exception() + + + + +class TestUserBroadcasting (Broadcasting, UserAuthenticator, unittest.TestCase): pass +class TestUserListening (Listening, UserAuthenticator, unittest.TestCase): pass +class TestUserCancelling (Cancelling, UserAuthenticator, unittest.TestCase): pass -class Test1 (UserAuthenticator, unittest.TestCase): - def test_something (self): - pass \ No newline at end of file diff --git a/tests/runtime_start_unit_test.py b/tests/runtime_start_unit_test.py deleted file mode 100644 index 2bc5a1e..0000000 --- a/tests/runtime_start_unit_test.py +++ /dev/null @@ -1,234 +0,0 @@ - - -import unittest -import exp - -class TestException (Exception): - pass - - -def fail_stub(): - raise TestException() - -def noop(*args, **kwargs): - pass - -original_network_start = exp._network.start -original_network_wait = exp._network.wait -original_authenticator_start = exp._authenticator.start -original_authenticator_wait = exp._authenticator.wait - - - -class Base(object): - - type_ = None - - username = None - password = None - organization = None - - uuid = None - secret = None - api_key = None - - allow_pairing = None - enable_events = True - - did_network_wait = False - did_authenticator_wait = False - - def on_network_wait (self): - self.did_network_wait = True - - def on_authenticator_wait(self): - self.did_authenticator_wait = True - - def setUp (self): - exp._runtime._is_started = False - self.stub() - - def tearDown (self): - exp._runtime._is_started = False - self.unstub() - - def stub (self): - exp._network.start = noop - exp._network.wait = lambda: self.on_network_wait() - exp._authenticator.start = noop - exp._authenticator.wait = lambda: self.on_authenticator_wait() - - def unstub (self): - exp._network.start = original_network_start - exp._network.wait = original_network_wait - exp._authenticator.start = original_authenticator_start - exp._authenticator.wait = original_authenticator_wait - - def start (self): - return exp.start(type=self.type_, - username=self.username, password=self.password, organization=self.organization, - uuid=self.uuid, secret=self.secret, api_key=self.api_key, - allow_pairing=self.allow_pairing, enable_events=self.enable_events) - - -class ErrorBase(Base): - - exception = Exception - - def test (self): - self.assertRaises(self.exception, lambda: self.start()) - - -class RuntimeErrorBase(ErrorBase): - - exception = exp.RuntimeError - - -class OptionsErrorBase(ErrorBase): - - exception = exp.OptionsError - - -class StartTwice (RuntimeErrorBase, unittest.TestCase): - - username = '_' - password = '_' - organization = '_' - - def test (self): - try: - self.start() - except: - pass - super(StartTwice, self).test() - - -class NoOptions (OptionsErrorBase, unittest.TestCase): - pass - - -class NoUsername (OptionsErrorBase, unittest.TestCase): - password = '_' - organization = '_' - - -class NoPassword (OptionsErrorBase, unittest.TestCase): - username = '_' - organization = '_' - - -class NoOrganization (OptionsErrorBase, unittest.TestCase): - username = '_' - password = '_' - - -class NoDeviceUuid (OptionsErrorBase, unittest.TestCase): - secret = '_' - - -class NoConsumerAppUuid (OptionsErrorBase, unittest.TestCase): - api_key = '_' - - -class NoDeviceSecret (OptionsErrorBase, unittest.TestCase): - type_ = 'device' - uuid = '_' - -class NoConsumerAppApiKey (OptionsErrorBase, unittest.TestCase): - type_ = 'consumer_app' - uuid = '_' - -class NoSecretOrApiKey (OptionsErrorBase, unittest.TestCase): - uuid = '_' - - -class SuccessBase (Base): - - should_network_start = True - - def test (self): - self.start() - self.assertEquals(self.did_authenticator_wait, True) - self.assertEquals(self.did_network_wait, self.should_network_start) - -class UserCredentials (SuccessBase, unittest.TestCase): - username = '_' - password = '_' - organization = '_' - -class UserTypedCredentials (SuccessBase, unittest.TestCase): - type_ = 'user' - username = '_' - password = '_' - organization = '_' - -class DeviceCredentials (SuccessBase, unittest.TestCase): - uuid = '_' - secret = '_' - -class DeviceTypedCredentials (SuccessBase, unittest.TestCase): - type_ = 'device' - uuid = '_' - secret = '_' - -class PairingCredentials (SuccessBase, unittest.TestCase): - allow_pairing = True - -class ConsumerAppCredentials (SuccessBase, unittest.TestCase): - uuid = '_' - api_key = '_' - -class ConsumerAppTypedCredentials (SuccessBase, unittest.TestCase): - type_ = 'consumer_app' - uuid = '_' - api_key = '_' - -""" - -class RuntimeExceptionsTestCase(BaseTestCase): - - def setUp (self): - exp._network.start = noop - exp._network.wait = noop - exp._authenticator.start = noop - exp._authenticator.wait = noop - - def tearDown (self): - exp._runtime._is_started = False - unstub() - - def test_start_with_no_options(self): - self.assertRaises(exp.OptionsError, start_with_no_options) - - def test_double_start (self): - self.assertRaises(exp.OptionsError, start_with_no_options) - self.assertRaises(exp.RuntimeError, start_with_no_options) - - def test_start_with_no_username(self): - self.assertRaises(exp.OptionsError, start_with_no_username) - - def test_start_with_no_password(self): - self.assertRaises(exp.OptionsError, start_with_no_password) - - def test_start_with_no_organization(self): - self.assertRaises(exp.OptionsError, start_with_no_password) - - def test_start_as_user (self): - start_as_user() - - def test_starts_network (self): - pass - - def test_starts_runtime (self): - pass - -class RuntimeSuccessTestCase(unittest.TestCase): - - def tearDown (self): - exp._runtime._is_started = False - - -""" - -if __name__ == '__main__': - unittest.main() diff --git a/tests/runtime_unit_test.py b/tests/runtime_unit_test.py new file mode 100644 index 0000000..b4109d5 --- /dev/null +++ b/tests/runtime_unit_test.py @@ -0,0 +1,142 @@ +import unittest +import exp +from exp.network import network +from exp.runtime import runtime +from exp.authenticator import authenticator + +def noop(*args, **kwargs): + pass + +original_network_configure = network.configure +original_authenticator_configure = authenticator.configure +original_authenticator_get_auth = authenticator.get_auth + +class Base(object): + + type_ = None + + username = None + password = None + organization = None + + uuid = None + secret = None + api_key = None + + allow_pairing = None + enable_network = True + + def setUp (self): + runtime.is_started = False + self.stub() + + def tearDown (self): + runtime.is_started = False + self.unstub() + + def stub (self): + network.configure = noop + authenticator.configure = noop + authenticator.get_auth = noop + + def unstub (self): + network.configure = original_network_configure + authenticator.configure = original_authenticator_configure + authenticator.get_auth = original_authenticator_get_auth + + def start (self): + return exp.start(type=self.type_, + username=self.username, password=self.password, organization=self.organization, + uuid=self.uuid, secret=self.secret, api_key=self.api_key, + allow_pairing=self.allow_pairing, enable_network=self.enable_network) + + +class ErrorBase(Base): + + exception = exp.RuntimeError + + def test (self): + self.assertRaises(self.exception, lambda: self.start()) + + +class NoOptions (ErrorBase, unittest.TestCase): + pass + + +class NoUsername (ErrorBase, unittest.TestCase): + password = '_' + organization = '_' + + +class NoPassword (ErrorBase, unittest.TestCase): + username = '_' + organization = '_' + + +class NoOrganization (ErrorBase, unittest.TestCase): + username = '_' + password = '_' + + +class NoDeviceUuid (ErrorBase, unittest.TestCase): + secret = '_' + + +class NoConsumerAppUuid (ErrorBase, unittest.TestCase): + api_key = '_' + + +class NoDeviceSecret (ErrorBase, unittest.TestCase): + type_ = 'device' + uuid = '_' + +class NoConsumerAppApiKey (ErrorBase, unittest.TestCase): + type_ = 'consumer_app' + uuid = '_' + +class NoSecretOrApiKey (ErrorBase, unittest.TestCase): + uuid = '_' + + +class SuccessBase (Base): + + def test (self): + self.start() + + +class UserCredentials (SuccessBase, unittest.TestCase): + username = '_' + password = '_' + organization = '_' + +class UserTypedCredentials (SuccessBase, unittest.TestCase): + type_ = 'user' + username = '_' + password = '_' + organization = '_' + +class DeviceCredentials (SuccessBase, unittest.TestCase): + uuid = '_' + secret = '_' + +class DeviceTypedCredentials (SuccessBase, unittest.TestCase): + type_ = 'device' + uuid = '_' + secret = '_' + +class PairingCredentials (SuccessBase, unittest.TestCase): + allow_pairing = True + +class ConsumerAppCredentials (SuccessBase, unittest.TestCase): + uuid = '_' + api_key = '_' + +class ConsumerAppTypedCredentials (SuccessBase, unittest.TestCase): + type_ = 'consumer_app' + uuid = '_' + api_key = '_' + + + +if __name__ == '__main__': + unittest.main() From c4fa25e2ad2ea87958d9316d747d8b2ac9b27a48 Mon Sep 17 00:00:00 2001 From: James Dalessio Date: Fri, 4 Mar 2016 17:49:13 -0500 Subject: [PATCH 025/104] Some work on the readme. --- README.md | 42 ++++++++++++++++++++++++++---------------- 1 file changed, 26 insertions(+), 16 deletions(-) diff --git a/README.md b/README.md index ee69f34..18bb87c 100644 --- a/README.md +++ b/README.md @@ -153,43 +153,53 @@ The EXP network facilitates real time communication between entities connected t All messages on the EXP network are sent over a channel. Channels have a name, and two flags: ```system``` and ```consumer```. ```python -channel = exp.get_channel("my_channel", system=False, consumer=False) +channel = exp.get_channel('my_channel', system=False, consumer=False) ``` Use ```system=True``` to get a system channel. You cannot send messages on a system channels but can listen for system notifications, such as updates to API resources. Use ```consumer=True``` to get a consumer channel. Consumer devices can only listen or broadcast on consumer channels. When ```consumer=False``` you will not receive consumer device broadcasts and consumer devices will not be able to hear your broadcasts. -Both ```system``` and ```consumer``` default to ```False``` except for consumer devices, where ```consumer``` will always be ```True``` for all channels. +Both ```system``` and ```consumer``` default to ```False```. Consumer devices will be unable to broadcast or listen to messages on non-consumer channels. ### Broadcasting -Use the broadcast method of a channel object to send a named message with a JSON serializable payload to other entities on the EXP network. You can optionally include a timeout to wait for responses to the broadcast. The broadcast will block for approximately the given timeout and return a list of response payloads. Each response payload can any JSON serializable type. +Use the broadcast method of a channel object to send a named message containing an optional JSON serializable payload to other entities on the EXP network. You can optionally include a timeout to wait for responses to the broadcast. The broadcast will block for approximately the given timeout and return a ```list``` of response payloads. Each response payload can any JSON serializable type. ```python -channel = exp.get_channel("my_channel") -responses = channel.broadcast(name='Hello!', timeout=5, payload=[1, 2, 3]) + +channel = exp.get_channel('my_channel') +responses = channel.broadcast(name='my_event', timeout=5, payload='hello') [print response for response in responses] + ``` ### Listening -To listen for broadcasts, call the listen method of a channel object. +To listen for broadcasts, call the listen method of a channel object and pass in the name of the event you wish to listen for. When EXP registers you to listen on the desired channel, a ```listener``` object will be returned. + +Call the ```wait``` method of a listener to block until a broadcast is received. + + ```python -channel = exp.get_channel("my_channel") -listener = channel.listen("my_event") + +channel = exp.get_channel('my_channel') +listener = channel.listen('my_event') while True: broadcast = listener.wait(5) - if broadcast: - print "Message received!" + if broadcast: + print 'Message received!' print broadcast.payload -``` + listener.cancel() + break +``` +Broadcasts that come in while not waiting on the listener will be queued. ### Responding @@ -198,14 +208,14 @@ To respond to broadcast, call the respond method on the broadcast object, option ```python -channel = exp.get_channel("my_channel") -listener = channel.listen(name="my_custom_event") +channel = exp.get_channel('my_channel') +listener = channel.listen(name='my_event') while True: broadcast = listener.wait(5) - if broadcast and broadcast.payload is "hello!": - print "Responding to broadcast." - broadcast.respond("Nice to meet you!") + if broadcast and broadcast.payload is 'hello': + print 'Responding to broadcast.' + broadcast.respond('Nice to meet you!') ``` From d098daa74d0494a95ced36f6a85d8481e87468e1 Mon Sep 17 00:00:00 2001 From: James Dalessio Date: Sun, 6 Mar 2016 22:33:08 -0500 Subject: [PATCH 026/104] Changing some structure for testing. --- exp/api.py | 4 +++ exp/authenticator.py | 1 + exp/exceptions.py | 15 +++++++-- exp/network.py | 42 +++++++++++++++++++++---- exp/runtime.py | 35 ++++----------------- tests/device_unit_test.py | 66 +++++++++++++++++++++++++++++++++++++++ 6 files changed, 126 insertions(+), 37 deletions(-) create mode 100644 tests/device_unit_test.py diff --git a/exp/api.py b/exp/api.py index 6e3ecc0..2267cf4 100644 --- a/exp/api.py +++ b/exp/api.py @@ -23,6 +23,10 @@ def __contains__ (self, item): def __len__ (self): return len(self.results) + def __getitem__ (self, index): + return self.results[index] + + class Resource (object): diff --git a/exp/authenticator.py b/exp/authenticator.py index ae68902..fd3f518 100644 --- a/exp/authenticator.py +++ b/exp/authenticator.py @@ -6,6 +6,7 @@ import hmac import hashlib + from .logger import logger from .exceptions import AuthenticationError, UnexpectedError, RuntimeError diff --git a/exp/exceptions.py b/exp/exceptions.py index 10fed04..fa7d8b9 100644 --- a/exp/exceptions.py +++ b/exp/exceptions.py @@ -3,7 +3,12 @@ import traceback class ExpError (Exception): - pass + + def __init__ (self, message): + self.message = message + + def __str__ (self): + return self.message class AuthenticationError (ExpError): @@ -18,7 +23,13 @@ def __init__ (self, *arg, **kwargs): # Cannot execute desired action. class RuntimeError(ExpError): - pass + + def __init__ (self, message): + logger.debug('A runtime error has occured: %s' % message) + + def __str__ (self): + return self.message + class ApiError(ExpError): diff --git a/exp/network.py b/exp/network.py index c27b6c6..586f753 100644 --- a/exp/network.py +++ b/exp/network.py @@ -128,13 +128,24 @@ def __init__(self): self._options = None self._parent = threading.currentThread() self._thread = None + self._lock = threading.Lock() + self._is_started = False + + def start (self): + self._lock.aquire() + if self._is_started: + return + self._is_started = True + self._parent = threading.currentThread() + self._thread = threading.Thread(target=lambda: self._main_event_loop()) + self._thread.start() + self._lock.release() def configure (self, **options): + self._lock.acquire() self._auth = None self._options = options - self._parent = threading.currentThread() - self._thread = threading.Thread(target=lambda: self._main_event_loop()) - self._thread.start() + self._lock.release() def get_channel(self, name, system=False, consumer=False): channel_id = self._generate_channel_id(name, system, consumer) @@ -172,9 +183,19 @@ def _main_event_loop (self): while True: - """ Break if parent thread is dead. """ - if not self._options['enable_network'] or not self._parent.is_alive(): + self._lock.acquire() + + """ If network is not enabled or options aren't set, disconnect and wait. """ + if not self._options or not self._options['enable_network']: + socket.disconnect() + self._lock.release() + time.sleep(1) + continue + + """ If parent thread is dead, disconnect and break event loop. """ + if self._parent and not self._parent.is_alive(): socket.disconnect() + self._lock.release() break; """ Attempt to retrieve current auth. If auth has changed, reconnect. """ @@ -184,11 +205,20 @@ def _main_event_loop (self): self._auth = auth socket.connect(**self._auth) except Exception: + socket.disconnect() + self._lock.release() time.sleep(1) continue """ Listen for socket events. """ - socket.wait(1) + try: + socket.wait(1) + except Exception: + self._lock.release() + time.sleep(1) + continue + else: + self._lock.release() diff --git a/exp/runtime.py b/exp/runtime.py index 1aeb428..f8249c8 100644 --- a/exp/runtime.py +++ b/exp/runtime.py @@ -1,37 +1,12 @@ -import time -import json -import hmac -import base64 -import hashlib -import requests -import threading - from .logger import logger from .network import network from .authenticator import authenticator -from .exceptions import RuntimeError, ExpError -import traceback +from .exceptions import RuntimeError class Runtime (object): - is_started = False - - def start (self, *args, **kwargs): - try: - self._start(*args, **kwargs) - except ExpError: - logger.debug('An error has occured:') - logger.debug(traceback.format_exc()) - raise - except Exception: - logger.debug('An unexpected error has occured:') - logger.debug(traceback.format_exc()) - raise UnexpectedError('An unexpected error has occured.') - else: - logger.info('EXP SDK started successfully.') - - def _start (self, enable_network=True, host='https://api.goexp.io/', **options): + def start (self, enable_network=True, host='https://api.goexp.io/', **options): options['host'] = host options['enable_network'] = enable_network @@ -59,9 +34,11 @@ def _start (self, enable_network=True, host='https://api.goexp.io/', **options): else: raise RuntimeError('Please specify authentication type.') - network.configure(**options) - authenticator.configure(**options) + authenticator.configure(**options) # Configure the authenticator. + network.configure(**options) # Configure the network. + network.start() authenticator.get_auth() # Block until authentication has been received. + logger.info('EXP SDK started successfully.') runtime = Runtime() diff --git a/tests/device_unit_test.py b/tests/device_unit_test.py new file mode 100644 index 0000000..66bdbc9 --- /dev/null +++ b/tests/device_unit_test.py @@ -0,0 +1,66 @@ + +import unittest + + +from exp.api import Device, QueryResult +import exp.api_utils + +class TestException (Exception): pass + + +class TestDeviceFind (unittest.TestCase): + + mock = { + 'total': 2, + 'results': [ + { 'name': 'device 1', 'uuid': 'fake uuid 1' }, + { 'name': 'device 2', 'uuid': 'fake uuid 2' } + ] + } + + def api_utils_get_mock (self, path, params=None): + if path != '/api/devices': + raise Exception() + return self.mock + + def raise_exception (self): + raise TestException() + + def tearDown (self): + exp.api_utils.get = self.original_api_utils_get + + def setUp (self): + self.original_api_utils_get = exp.api_utils.get + + def test_error_passthrough (self): + exp.api_utils.get = lambda *args, **kwargs: self.raise_exception() + try: + exp.find_devices() + except TestException: + pass + else: + raise Exception() + + def test (self): + exp.api_utils.get = lambda *args, **kwargs: self.api_utils_get_mock(*args, **kwargs) + devices = exp.find_devices() + for device in devices: + if not isinstance(device, Device): + raise Exception + + def test_results_length (self): + devices = exp.find_devices() + if len(devices) != + pass + + def test_results_type (self): + pass + + def test_results_document (self): + pass + + + + + + From 4cce3525997a0dec11cf43a1240563d877a84567 Mon Sep 17 00:00:00 2001 From: James Dalessio Date: Mon, 7 Mar 2016 11:00:14 -0500 Subject: [PATCH 027/104] Update README.md --- README.md | 159 ++++++++++++++++++++++++------------------------------ 1 file changed, 70 insertions(+), 89 deletions(-) diff --git a/README.md b/README.md index 18bb87c..93229d0 100644 --- a/README.md +++ b/README.md @@ -1,15 +1,14 @@ +# Starting the SDK +The SDK is started by calling ```exp.start``` and specifying your credentials and configuration options as keyword arguments. ```exp.start``` will start additional threads to process network events. You may supply user, device, or consumer app credentials. You can also authenticate in pairing mode. +When ```exp.start``` returns, you are authenticated and can begin using the SDK. -# [Starting the SDK](#runtime) -The SDK is started by calling ```exp.start``` and specifying your credentials and configuration options as keyword arguments. ```exp.start``` will start some background processes that keep you authenticated and process network events. You must supply credentials to ```exp.start```. You can supply user, device, or consumer app credentials. You can also authenticate in pairing mode. -When ```exp.start``` returns, you are authenticated and (optionally) connected to the EXP network. The SDK is non blocking and will stop when your main script finishes. - -## Using User Credentials +## User Credentials Users must specify their ```username```, ```password```, and ```organization``` as keyword arguments to ```exp.start```. @@ -20,7 +19,7 @@ exp.start(username='joe@joemail.com', password='JoeRocks42', organization='joeso ``` -### Using Device Credentials +## Device Credentials Devices must specify their ```uuid``` and ```secret``` as keyword arguments. @@ -31,7 +30,7 @@ exp.start(uuid='[uuid]', secret='[secret]') ``` -### Using Consumer App Credentials +## Consumer App Credentials Consumer apps must specify their ```uuid``` and ```api_key``` as keyword arguments. @@ -42,7 +41,7 @@ exp.start(uuid='[uuid]', api_key='[api key]') ``` -### Pairing Mode +## Pairing Mode Advanced users can authenticate in pairing mode by setting ```allow_pairing``` to ```False```. @@ -63,82 +62,7 @@ enable_network | ```True``` | Whether to enable real time network communication. ### Exceptions -If the SDK is already running an ```exp.RuntimeError``` will be raised. If the arguments specified to ```exp.start``` are invalid an ```exp.OptionsError``` will be raised. - - - -# exp.runtime - -## exp.start() -The SDK must be initialized by calling ```exp.start()``` and passing in configuration options. This starts the event bus and automatically authenticates API calls. The start command will block until a connection is first established. - -```python -# Authenticate with username and password. -exp.start( - username="joe@exp.com", - password="joesmoe25", - organization="exp") -# Authenticate with device uuid and secret. -exp.start(uuid="[uuid]", secret="[secret]") -# Authenticate with consumer app uuid and api key. -exp.start(uuid="[uuid]", apiKey="[apiKey]") -``` - -## exp.runtime.stop() -A socket connection is made to EXP that is non-blocking. To end the connection and to stop threads spawned by the SDK call ```exp.runtime.stop()```. - -## exp.runtime.on() - -Can listen for when the event bus is online/offline. Triggers an asynchronous callback. - -```python -def on_online(): - print "Online!" -def on_offline(): - print "Offline!" -exp.runtime.on("online", callback=on_online) -exp.runtime.on("offline", callback=on_offline) -``` - -# exp.api -API abstraction layer. - -## Example -```python -devices = exp.api.find_devices(**params) # Query for device objects (url params). -device = exp.api.get_device(uuid) # Get device by UUID. -device = exp.api.create_device(document) # Create a device from a dictionary -``` -Other available namespaces: experiences, locations, content, data. content does not currently support creation, only "get_content(uuid) and find_content(params)". - -# Interacting with API Resources - -## API Resources -Each resource object contains a "document" field which is a dictionary representation of the raw resource, along with "save" and "delete" methods. -```python -device = exp.api.create_device({ "field": value }) -device.document["field"] = 123 -device.save() -print device.document["field"] -device.delete() -``` - - -```python -data = exp.api.get_data("key1", "group0") -print data.value -data.value = { "generic": 1111 } -data.save() -data.delete() - -data = exp.api.create_data(key="4", group="cats", { "name": "fluffy" }) - -``` - -The "content" resource has a ```get_children()``` method that returns the content's children (a list of content objects). Every content object also has a ```get_url()``` and ```get_variant_url(https://github.com/name)``` method that returns a delivery url for the content. - -The "feed" resource has a ```get_data()``` method that returns a the feed's decoded JSON document. - +If the arguments specified to ```exp.start``` are invalid or incomplete an ```exp.RuntimeError``` will be raised. @@ -178,11 +102,11 @@ responses = channel.broadcast(name='my_event', timeout=5, payload='hello') ### Listening -To listen for broadcasts, call the listen method of a channel object and pass in the name of the event you wish to listen for. When EXP registers you to listen on the desired channel, a ```listener``` object will be returned. - -Call the ```wait``` method of a listener to block until a broadcast is received. +To listen for broadcasts, call the listen method of a channel object and pass in the name of the event you wish to listen for. When EXP listener has been registered and can start receiving events, a ```listener``` object will be returned. +Call the ```wait``` method of a listener to block until a broadcast is received, passing in a timeout in seconds. If ```timeout``` elaspes and no broadcasts have been received, ```wait``` will return ```None```. +Once a listener is created, it will receive broadcasts in a background thread even when not waiting. Calling ```wait``` will first attempt to return the oldest broadcast in the queue. Queued broadcasts will be discarded after ~60s if not retrieved during a ```wait```. ```python @@ -199,8 +123,6 @@ while True: ``` -Broadcasts that come in while not waiting on the listener will be queued. - ### Responding @@ -220,6 +142,65 @@ while True: ``` +# SDK Resources + +EXP API resources are represented by abstract objects. + + + + +# SDK Reference + +exp.start(**kwargs) +exp.get_channel(name, system=False, consumer=False) + +exp.get_device(uuid) +exp.create_device(document) +exp.find_devices(**params) + +exp.get_experience(uuid) + + + + +# exp.api +API abstraction layer. + +## Example +```python +devices = exp.api.find_devices(**params) # Query for device objects (url params). +device = exp.api.get_device(uuid) # Get device by UUID. +device = exp.api.create_device(document) # Create a device from a dictionary +``` +Other available namespaces: experiences, locations, content, data. content does not currently support creation, only "get_content(uuid) and find_content(params)". + +# Interacting with API Resources + +## API Resources +Each resource object contains a "document" field which is a dictionary representation of the raw resource, along with "save" and "delete" methods. +```python +device = exp.api.create_device({ "field": value }) +device.document["field"] = 123 +device.save() +print device.document["field"] +device.delete() +``` + + +```python +data = exp.api.get_data("key1", "group0") +print data.value +data.value = { "generic": 1111 } +data.save() +data.delete() + +data = exp.api.create_data(key="4", group="cats", { "name": "fluffy" }) + +``` + +The "content" resource has a ```get_children()``` method that returns the content's children (a list of content objects). Every content object also has a ```get_url()``` and ```get_variant_url(https://github.com/name)``` method that returns a delivery url for the content. + +The "feed" resource has a ```get_data()``` method that returns a the feed's decoded JSON document. From 0213f68701bbcd92207ec7fb114a11a2fc96bead Mon Sep 17 00:00:00 2001 From: James Dalessio Date: Mon, 7 Mar 2016 14:53:55 -0500 Subject: [PATCH 028/104] Update README.md --- README.md | 97 ++++++++++++++++++++++++++++++++----------------------- 1 file changed, 57 insertions(+), 40 deletions(-) diff --git a/README.md b/README.md index 93229d0..d3b55e9 100644 --- a/README.md +++ b/README.md @@ -141,66 +141,83 @@ while True: ``` +# SDK Reference -# SDK Resources - -EXP API resources are represented by abstract objects. +## Startup +```exp.start(username=None, password=None, uuid=None, secret=None, api_key=None, enable_network=True, host='https://api.goexp.io/', allow_pairing=False)```: Start the SDK with the given set of options. See [Starting the SDK](#starting-the-sdk). +## Exceptions +- ```exp.ExpError```: Base class for errors raised by the SDK. +- ```exp.AuthenticationError```: Raised when SDK cannot authenticate. +- ```exp.RuntimeError```: Raised when options pass to ```exp.start``` are invalid or inconsistent. +- ```exp.HttpError```: Raised when an API request encouters and error. This exception has the following attributes: + - ```code```: The API REST code of the error. + - ```status```: The HTTP status code received. + - ```message```: A human readable description of the encountered error. +- ```exp.UnexpectedError```: Raised when an SDK method encounters an unexpected exception. -# SDK Reference +## Devices -exp.start(**kwargs) -exp.get_channel(name, system=False, consumer=False) +- ```device = exp.get_device(uuid)```: Get a device by uuid. +- ```device = exp.create_device(document)```: Create (and save) a device given a device document, a dictionary. +- ```devices = exp.find_devices(params)```: Get a list of devices given a dictionary of query parameters. See the API docs. +- ```device.uuid```: The device's uuid. +- ```device.document```: The underlying device's document, a dictionary. +- ```device.get_channel(system=False, consumer=False)```: Get a [channel](#Channels) for communication about this device. -exp.get_device(uuid) -exp.create_device(document) -exp.find_devices(**params) +## Things +- ```thing = exp.get_thing(uuid)```: Get a thing by uuid. +- ```thing = exp.create_thing(document)```: Create (and save) a thing given a thing document, a dictionary. +- ```thing = exp.find_things(params)```: Get a list of things given a dictionary of query parameters. See the API docs. +- ```thing.uuid```: The thing's uuid. +- ```thing.document```: The underlying thing's document, a dictionary. +- ```thing.get_channel(system=False, consumer=False)```: Get a [channel](#Channels) for communication about this thing. -exp.get_experience(uuid) +# Experiences +- ```experience = exp.get_experience(uuid)```: Get an experience by uuid. +- ```experience = exp.create_experience(document)```: Create and save new experience from document. +- ```experiences = exp.find_experiences(params)```: Get a list of experiences given a dictionary of query params. See the API docs. +- ```experience.uuid```: The experience's uuid. +- ```experience.document```: The underlying experience's document, a dictionary. +- ```experience.get_channel(system=False, consumer=False)```: Get a [channel](#Channels) for communication about this experience. +- +# Locations +- ```location = exp.get_location(uuid)```: Get a location by uuid. +- ```location = exp.create_location(document)```: Create and save new experience from document. +- ```locations = exp.find_locations(params)```: Get a list of locations given a dictionary of query params. See the API docs. +- ```location.uuid```: The locations's uuid. +- ```location.document```: The underlying experience document, a dictionary. +- ```location.get_channel(system=False, consumer=False)```: Get a [channel](#Channels) for communication about this location. +- ```location.get_zones()```: Get a list of [zones](#Zones) that are part of this location. +# Zones +- ```zone.document```: The underlying zone's document, a dictionary. +- ```zone.get_channel(system=False, consumer=False)```: Get a [channel](#Channel) for communication about this zone. +# Content -# exp.api -API abstraction layer. - -## Example -```python -devices = exp.api.find_devices(**params) # Query for device objects (url params). -device = exp.api.get_device(uuid) # Get device by UUID. -device = exp.api.create_device(document) # Create a device from a dictionary -``` -Other available namespaces: experiences, locations, content, data. content does not currently support creation, only "get_content(uuid) and find_content(params)". +# Feeds -# Interacting with API Resources +# Channel +- ```channel = exp.get_channel(name, system=False, consumer=False)```: Get a channel by name. +- ```responses = channel.broadcast(name, payload=None, timeout=0):``` Send a broadcast on this channel, with name ```name``` and JSON serializable payload ```payload```. Wait for ```timeout``` seconds for responses. ```responses``` will be a list of JSON serializable objects, one item per response is order the response was received. +- ```listener = channel.listen(name)```: Listen for messages with name ```name``` on the given channel. Returns a [Listener](#listener) after the listener has been registered internally and can receive events. -## API Resources -Each resource object contains a "document" field which is a dictionary representation of the raw resource, along with "save" and "delete" methods. -```python -device = exp.api.create_device({ "field": value }) -device.document["field"] = 123 -device.save() -print device.document["field"] -device.delete() -``` -```python -data = exp.api.get_data("key1", "group0") -print data.value -data.value = { "generic": 1111 } -data.save() -data.delete() +##Listener +- ```listener.cancel()```: Permanently detach the listener. Cannot be undone. +- ```broadcast = listener.wait(timeout=0)```: Block for broadcasts for ```timeout``` seconds. Any broadcasts received by the listener when not waiting are queued. If there is a broadcast in the queue, the oldest broadcast will be consumed and returned immediately when ```wait``` is called. If no broadcasts are queued and none are received in ```timeout``` seconds, ```wait``` returns ```None```. -data = exp.api.create_data(key="4", group="cats", { "name": "fluffy" }) +# Broadcast +- ```broadcast.payload```: The payload of the broadcast. Always JSON serializable type. +- ```broadcast.respond(payload)```: Respond to the broadcast. ```payload``` is a JSON serializable response to send back to the broadcaster. -``` -The "content" resource has a ```get_children()``` method that returns the content's children (a list of content objects). Every content object also has a ```get_url()``` and ```get_variant_url(https://github.com/name)``` method that returns a delivery url for the content. -The "feed" resource has a ```get_data()``` method that returns a the feed's decoded JSON document. From 5a2d6a0ce988e560c33ac87cd646e9229f304df4 Mon Sep 17 00:00:00 2001 From: James Dalessio Date: Mon, 7 Mar 2016 15:07:51 -0500 Subject: [PATCH 029/104] Update README.md --- README.md | 36 ++++++++++++++++++++++-------------- 1 file changed, 22 insertions(+), 14 deletions(-) diff --git a/README.md b/README.md index d3b55e9..b5d4056 100644 --- a/README.md +++ b/README.md @@ -144,8 +144,10 @@ while True: # SDK Reference -## Startup -```exp.start(username=None, password=None, uuid=None, secret=None, api_key=None, enable_network=True, host='https://api.goexp.io/', allow_pairing=False)```: Start the SDK with the given set of options. See [Starting the SDK](#starting-the-sdk). +## Runtime +- ```exp.start(username=None, password=None, uuid=None, secret=None, api_key=None, enable_network=True, host='https://api.goexp.io/', allow_pairing=False)```: Start the SDK with the given set of options. See [Starting the SDK](#starting-the-sdk). +- ```exp.get_auth()```: Returns the raw dictionary returned by the server during authentication. +- ```exp.get_connectction_status()```: Returns ```True``` or ```False``` dependending on whether the network connection is active. ## Exceptions @@ -175,15 +177,16 @@ while True: - ```thing.document```: The underlying thing's document, a dictionary. - ```thing.get_channel(system=False, consumer=False)```: Get a [channel](#Channels) for communication about this thing. -# Experiences +## Experiences - ```experience = exp.get_experience(uuid)```: Get an experience by uuid. - ```experience = exp.create_experience(document)```: Create and save new experience from document. - ```experiences = exp.find_experiences(params)```: Get a list of experiences given a dictionary of query params. See the API docs. - ```experience.uuid```: The experience's uuid. - ```experience.document```: The underlying experience's document, a dictionary. - ```experience.get_channel(system=False, consumer=False)```: Get a [channel](#Channels) for communication about this experience. -- -# Locations + + +## Locations - ```location = exp.get_location(uuid)```: Get a location by uuid. - ```location = exp.create_location(document)```: Create and save new experience from document. - ```locations = exp.find_locations(params)```: Get a list of locations given a dictionary of query params. See the API docs. @@ -193,32 +196,37 @@ while True: - ```location.get_zones()```: Get a list of [zones](#Zones) that are part of this location. -# Zones +## Zones - ```zone.document```: The underlying zone's document, a dictionary. -- ```zone.get_channel(system=False, consumer=False)```: Get a [channel](#Channel) for communication about this zone. +- ```zone.get_channel(system=False, consumer=False)```: Get a [channel](#Channels) for communication about this zone. -# Content +## Content -# Feeds +## Feeds -# Channel +## Channels - ```channel = exp.get_channel(name, system=False, consumer=False)```: Get a channel by name. - ```responses = channel.broadcast(name, payload=None, timeout=0):``` Send a broadcast on this channel, with name ```name``` and JSON serializable payload ```payload```. Wait for ```timeout``` seconds for responses. ```responses``` will be a list of JSON serializable objects, one item per response is order the response was received. - ```listener = channel.listen(name)```: Listen for messages with name ```name``` on the given channel. Returns a [Listener](#listener) after the listener has been registered internally and can receive events. -##Listener +## Listener - ```listener.cancel()```: Permanently detach the listener. Cannot be undone. - ```broadcast = listener.wait(timeout=0)```: Block for broadcasts for ```timeout``` seconds. Any broadcasts received by the listener when not waiting are queued. If there is a broadcast in the queue, the oldest broadcast will be consumed and returned immediately when ```wait``` is called. If no broadcasts are queued and none are received in ```timeout``` seconds, ```wait``` returns ```None```. -# Broadcast +## Broadcast - ```broadcast.payload```: The payload of the broadcast. Always JSON serializable type. - ```broadcast.respond(payload)```: Respond to the broadcast. ```payload``` is a JSON serializable response to send back to the broadcaster. - - +## Custom API Calls +The following methods make custom API calls that include authentication. Use for API calls that aren't supported by the SDK. ```params``` is specified as a dictionary of query params and ```payload``` must be a JSON serializable type. With the exception of DELETE, these requests will return the parsed JSON response. +- ```document = exp.get(path, params=None)``` +- ```document = exp.post(path, payload=None, params=None)``` +- ```document = exp.patch(path, payload=None, params=None)``` +- ```document = exp.put(path, payload=None, params=None)``` +- ```exp.delete(path, params)``` From ea0e1be83ef4db198e086f8bc7e7a2ff91980205 Mon Sep 17 00:00:00 2001 From: James Dalessio Date: Mon, 7 Mar 2016 15:14:56 -0500 Subject: [PATCH 030/104] Update README.md --- README.md | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/README.md b/README.md index b5d4056..3b92947 100644 --- a/README.md +++ b/README.md @@ -201,8 +201,19 @@ while True: - ```zone.get_channel(system=False, consumer=False)```: Get a [channel](#Channels) for communication about this zone. ## Content +- ```content = exp.get_content(uuid)```: Retrieves a content resource by uuid. +- ```content_list = exp.find_content(params)```: Returns a list of content using the given query params. +- ```content.get_url()```: Returns a delivery URL for content retrieval. +- ```content.get_variant_url()```: Returns a delivery URL for a variant of the content. +- ```content.children```: A list of child content resources. +- ```content.subtype```: The content subtype. See the API docs. ## Feeds +- ```feed = exp.get_feed(uuid)```: Retrieves a feed resource by uuid. +- ```feeds = exp.find_feeds(params)```: Get a list of feeds given a dictionary of query params. See the API docs. +- ```feed = exp.create_feed(document)```: Create and save a new feed from a feed document. +- ```feed.get_data()```: Get the feeds data. + ## Channels - ```channel = exp.get_channel(name, system=False, consumer=False)```: Get a channel by name. From 4dee0683657963d346fd0a9a66cb7d1cfc208c2c Mon Sep 17 00:00:00 2001 From: James Dalessio Date: Mon, 7 Mar 2016 16:06:55 -0500 Subject: [PATCH 031/104] Update README.md --- README.md | 79 ++++++++++++++++++++++++++----------------------------- 1 file changed, 37 insertions(+), 42 deletions(-) diff --git a/README.md b/README.md index 3b92947..0b20bae 100644 --- a/README.md +++ b/README.md @@ -1,14 +1,8 @@ -# Starting the SDK +# Getting Started -The SDK is started by calling ```exp.start``` and specifying your credentials and configuration options as keyword arguments. ```exp.start``` will start additional threads to process network events. You may supply user, device, or consumer app credentials. You can also authenticate in pairing mode. - -When ```exp.start``` returns, you are authenticated and can begin using the SDK. - - - -## User Credentials +The SDK is started by calling ```exp.start``` and specifying your credentials and configuration options as keyword arguments. ```exp.start``` will start additional threads to process network events. You may supply user, device, or consumer app credentials. You can also authenticate in pairing mode. When ```exp.start``` returns, you are authenticated and can begin using the SDK. Users must specify their ```username```, ```password```, and ```organization``` as keyword arguments to ```exp.start```. @@ -19,8 +13,6 @@ exp.start(username='joe@joemail.com', password='JoeRocks42', organization='joeso ``` -## Device Credentials - Devices must specify their ```uuid``` and ```secret``` as keyword arguments. ```python @@ -30,8 +22,6 @@ exp.start(uuid='[uuid]', secret='[secret]') ``` -## Consumer App Credentials - Consumer apps must specify their ```uuid``` and ```api_key``` as keyword arguments. ```python @@ -41,8 +31,6 @@ exp.start(uuid='[uuid]', api_key='[api key]') ``` -## Pairing Mode - Advanced users can authenticate in pairing mode by setting ```allow_pairing``` to ```False```. ```python @@ -52,20 +40,13 @@ exp.start(allow_pairing=False) ``` - -## Additional Options +Additional options: Name | Default | Description --- | --- | --- host | ```'https://api.goexp.io/'``` | The api server to authenticate with. enable_network | ```True``` | Whether to enable real time network communication. If set to ```False``` you will be unable to listen on the [EXP network](# Communicating on the EXP Network). -### Exceptions - -If the arguments specified to ```exp.start``` are invalid or incomplete an ```exp.RuntimeError``` will be raised. - - - # Communicating on the EXP Network @@ -162,36 +143,39 @@ while True: ## Devices -- ```device = exp.get_device(uuid)```: Get a device by uuid. -- ```device = exp.create_device(document)```: Create (and save) a device given a device document, a dictionary. -- ```devices = exp.find_devices(params)```: Get a list of devices given a dictionary of query parameters. See the API docs. +- ```device = exp.get_device(uuid)```: Retrieves a device by uuid. +- ```device = exp.create_device(document)```: Creates a device from a dictionary. +- ```devices = exp.find_devices(params)```: Retrieves a list of devices given a dictionary of query parameters. See the API docs. - ```device.uuid```: The device's uuid. -- ```device.document```: The underlying device's document, a dictionary. -- ```device.get_channel(system=False, consumer=False)```: Get a [channel](#Channels) for communication about this device. +- ```device.document```: The device's underlying document, a dictionary. +- ```device.save()```: Saves the device to EXP. +- ```device.get_channel(system=False, consumer=False)```: Retrieves a [channel](#Channels) for communication about the device. ## Things -- ```thing = exp.get_thing(uuid)```: Get a thing by uuid. -- ```thing = exp.create_thing(document)```: Create (and save) a thing given a thing document, a dictionary. -- ```thing = exp.find_things(params)```: Get a list of things given a dictionary of query parameters. See the API docs. +- ```thing = exp.get_thing(uuid)```: Retrieves a thing by uuid. +- ```thing = exp.create_thing(document)```: Creates a thing from a dictionary. +- ```thing = exp.find_things(params)```: Retrieves a list of things given a dictionary of query parameters. See the API docs. - ```thing.uuid```: The thing's uuid. -- ```thing.document```: The underlying thing's document, a dictionary. -- ```thing.get_channel(system=False, consumer=False)```: Get a [channel](#Channels) for communication about this thing. +- ```thing.document```: The thing's underying document, a dictionary. +- ```thing.save()```: Saves the thing to EXP. +- ```thing.get_channel(system=False, consumer=False)```: Retrieves a [channel](#Channels) for communication about the thing. ## Experiences -- ```experience = exp.get_experience(uuid)```: Get an experience by uuid. -- ```experience = exp.create_experience(document)```: Create and save new experience from document. -- ```experiences = exp.find_experiences(params)```: Get a list of experiences given a dictionary of query params. See the API docs. +- ```experience = exp.get_experience(uuid)```: Retrieves an experience by uuid. +- ```experience = exp.create_experience(document)```: Creates an experience from a dictionary. +- ```experiences = exp.find_experiences(params)```: Retrieves a list of experiences given a dictionary of query params. See the API docs. - ```experience.uuid```: The experience's uuid. -- ```experience.document```: The underlying experience's document, a dictionary. +- ```experience.document```: The experience's underlying document, a dictionary. +- ```experience.save()```: Saves the experience to EXP. - ```experience.get_channel(system=False, consumer=False)```: Get a [channel](#Channels) for communication about this experience. ## Locations -- ```location = exp.get_location(uuid)```: Get a location by uuid. -- ```location = exp.create_location(document)```: Create and save new experience from document. -- ```locations = exp.find_locations(params)```: Get a list of locations given a dictionary of query params. See the API docs. +- ```location = exp.get_location(uuid)```: Retrieves a location by uuid. +- ```location = exp.create_location(document)```: Creates a location from a dictionary. +- ```locations = exp.find_locations(params)```: Retrieves a list of locations given a dictionary of query params. See the API docs. - ```location.uuid```: The locations's uuid. -- ```location.document```: The underlying experience document, a dictionary. +- ```location.document```: The location's underlying document, a dictionary. - ```location.get_channel(system=False, consumer=False)```: Get a [channel](#Channels) for communication about this location. - ```location.get_zones()```: Get a list of [zones](#Zones) that are part of this location. @@ -206,14 +190,25 @@ while True: - ```content.get_url()```: Returns a delivery URL for content retrieval. - ```content.get_variant_url()```: Returns a delivery URL for a variant of the content. - ```content.children```: A list of child content resources. +- ```content.document```: The underlying zone's document, a dictionary. - ```content.subtype```: The content subtype. See the API docs. ## Feeds - ```feed = exp.get_feed(uuid)```: Retrieves a feed resource by uuid. - ```feeds = exp.find_feeds(params)```: Get a list of feeds given a dictionary of query params. See the API docs. - ```feed = exp.create_feed(document)```: Create and save a new feed from a feed document. -- ```feed.get_data()```: Get the feeds data. - +- ```feed.document```: The underlying feed's document, a dictionary. +- ```feed.get_data()```: Get the feed's data. + + +## Data +- ```data = exp.get_data(key, group='default')```: Retrieves data by key and group. +- ```data = exp.find_data(params)```: Retrieves a list of data given a dictionary of query params. See the API docs. +- ```data = exp.create_data(key, value, group='default')```: +- ```data.save()```: Saves the data. +- ```data.value```: The value of the data, a JSON serializable type. +- ```data.key```: The data's key. +- ```data.group```: The data's group. ## Channels - ```channel = exp.get_channel(name, system=False, consumer=False)```: Get a channel by name. From d389407dc75524022d0e9d202f15024b6be15766 Mon Sep 17 00:00:00 2001 From: James Dalessio Date: Mon, 7 Mar 2016 16:19:17 -0500 Subject: [PATCH 032/104] Update README.md --- README.md | 61 +++++++++++++++++++++++++++++++++++-------------------- 1 file changed, 39 insertions(+), 22 deletions(-) diff --git a/README.md b/README.md index 0b20bae..15aea02 100644 --- a/README.md +++ b/README.md @@ -7,37 +7,25 @@ The SDK is started by calling ```exp.start``` and specifying your credentials an Users must specify their ```username```, ```password```, and ```organization``` as keyword arguments to ```exp.start```. ```python -import exp - exp.start(username='joe@joemail.com', password='JoeRocks42', organization='joesorg') - ``` Devices must specify their ```uuid``` and ```secret``` as keyword arguments. ```python -import exp - exp.start(uuid='[uuid]', secret='[secret]') - ``` Consumer apps must specify their ```uuid``` and ```api_key``` as keyword arguments. ```python -import exp - exp.start(uuid='[uuid]', api_key='[api key]') - ``` Advanced users can authenticate in pairing mode by setting ```allow_pairing``` to ```False```. ```python -import exp - exp.start(allow_pairing=False) - ``` Additional options: @@ -48,8 +36,7 @@ host | ```'https://api.goexp.io/'``` | The api server to authenticate with. enable_network | ```True``` | Whether to enable real time network communication. If set to ```False``` you will be unable to listen on the [EXP network](# Communicating on the EXP Network). - -# Communicating on the EXP Network +# The EXP Network The EXP network facilitates real time communication between entities connected to EXP. A user or device can broadcast a JSON serializable payload to users and devices in your organization, and listeners to those broadcasts can respond to the broadcasters. @@ -212,18 +199,18 @@ while True: ## Channels - ```channel = exp.get_channel(name, system=False, consumer=False)```: Get a channel by name. -- ```responses = channel.broadcast(name, payload=None, timeout=0):``` Send a broadcast on this channel, with name ```name``` and JSON serializable payload ```payload```. Wait for ```timeout``` seconds for responses. ```responses``` will be a list of JSON serializable objects, one item per response is order the response was received. -- ```listener = channel.listen(name)```: Listen for messages with name ```name``` on the given channel. Returns a [Listener](#listener) after the listener has been registered internally and can receive events. - +- ```responses = channel.broadcast(name, payload=None, timeout=0):``` Send a broadcast on this channel, with string ```name``` and JSON serializable type ```payload```. Wait for ```timeout``` seconds for responses. Returns a list of the responses. +- ```listener = channel.listen(name)```: Listen for messages with name ```name``` on the given channel. Returns a [Listener](#listener). ## Listener -- ```listener.cancel()```: Permanently detach the listener. Cannot be undone. -- ```broadcast = listener.wait(timeout=0)```: Block for broadcasts for ```timeout``` seconds. Any broadcasts received by the listener when not waiting are queued. If there is a broadcast in the queue, the oldest broadcast will be consumed and returned immediately when ```wait``` is called. If no broadcasts are queued and none are received in ```timeout``` seconds, ```wait``` returns ```None```. +- ```listener.cancel()```: Permanently detach the listener. +- ```broadcast = listener.wait(timeout=0)```: Block for broadcasts for ```timeout``` seconds. Returns a [broadcast](#broadcasts) or ```None```. -## Broadcast -- ```broadcast.payload```: The payload of the broadcast. Always JSON serializable type. -- ```broadcast.respond(payload)```: Respond to the broadcast. ```payload``` is a JSON serializable response to send back to the broadcaster. + +## Broadcasts +- ```broadcast.payload```: The payload of the broadcast. +- ```broadcast.respond(payload)```: Respond to the broadcast. ```payload``` must be a JSON serializable. @@ -236,3 +223,33 @@ The following methods make custom API calls that include authentication. Use for - ```document = exp.put(path, payload=None, params=None)``` - ```exp.delete(path, params)``` + + + +# Examples + +## Creating a Device and Listening for Updates + +Updates to API resources are sent out over a system channel with the event name "update". + +```python + device = exp.create_device({ 'name': 'my_sweet_device' }) + device.save() + channel = device.get_channel(system=True) + listener = channel.listen('update') + while True: + if listener.wait(5): + print 'The device was updated!' + +``` + + +## Modifying a Resource in Place + +```python +experience = exp.get_experience('[uuid]') +experience.document['name'] = 'new name' +experience.save() +``` + + From 08ebbdd0a811c0403962c73d4d1cc75bd9ec8cd6 Mon Sep 17 00:00:00 2001 From: James Dalessio Date: Mon, 7 Mar 2016 16:22:02 -0500 Subject: [PATCH 033/104] Update README.md --- README.md | 152 +++++++++++++++++++++++++++--------------------------- 1 file changed, 76 insertions(+), 76 deletions(-) diff --git a/README.md b/README.md index 15aea02..dd6efd5 100644 --- a/README.md +++ b/README.md @@ -36,84 +36,11 @@ host | ```'https://api.goexp.io/'``` | The api server to authenticate with. enable_network | ```True``` | Whether to enable real time network communication. If set to ```False``` you will be unable to listen on the [EXP network](# Communicating on the EXP Network). -# The EXP Network - -The EXP network facilitates real time communication between entities connected to EXP. A user or device can broadcast a JSON serializable payload to users and devices in your organization, and listeners to those broadcasts can respond to the broadcasters. - -### Channels - -All messages on the EXP network are sent over a channel. Channels have a name, and two flags: ```system``` and ```consumer```. - -```python -channel = exp.get_channel('my_channel', system=False, consumer=False) -``` - -Use ```system=True``` to get a system channel. You cannot send messages on a system channels but can listen for system notifications, such as updates to API resources. - -Use ```consumer=True``` to get a consumer channel. Consumer devices can only listen or broadcast on consumer channels. When ```consumer=False``` you will not receive consumer device broadcasts and consumer devices will not be able to hear your broadcasts. - -Both ```system``` and ```consumer``` default to ```False```. Consumer devices will be unable to broadcast or listen to messages on non-consumer channels. - - -### Broadcasting - -Use the broadcast method of a channel object to send a named message containing an optional JSON serializable payload to other entities on the EXP network. You can optionally include a timeout to wait for responses to the broadcast. The broadcast will block for approximately the given timeout and return a ```list``` of response payloads. Each response payload can any JSON serializable type. - -```python - -channel = exp.get_channel('my_channel') -responses = channel.broadcast(name='my_event', timeout=5, payload='hello') -[print response for response in responses] - -``` - - -### Listening - -To listen for broadcasts, call the listen method of a channel object and pass in the name of the event you wish to listen for. When EXP listener has been registered and can start receiving events, a ```listener``` object will be returned. - -Call the ```wait``` method of a listener to block until a broadcast is received, passing in a timeout in seconds. If ```timeout``` elaspes and no broadcasts have been received, ```wait``` will return ```None```. - -Once a listener is created, it will receive broadcasts in a background thread even when not waiting. Calling ```wait``` will first attempt to return the oldest broadcast in the queue. Queued broadcasts will be discarded after ~60s if not retrieved during a ```wait```. - -```python - -channel = exp.get_channel('my_channel') -listener = channel.listen('my_event') - -while True: - broadcast = listener.wait(5) - if broadcast: - print 'Message received!' - print broadcast.payload - listener.cancel() - break - -``` - - -### Responding - -To respond to broadcast, call the respond method on the broadcast object, optionally passing in a JSON serializable response payload. - -```python - -channel = exp.get_channel('my_channel') -listener = channel.listen(name='my_event') - -while True: - broadcast = listener.wait(5) - if broadcast and broadcast.payload is 'hello': - print 'Responding to broadcast.' - broadcast.respond('Nice to meet you!') - -``` - # SDK Reference ## Runtime -- ```exp.start(username=None, password=None, uuid=None, secret=None, api_key=None, enable_network=True, host='https://api.goexp.io/', allow_pairing=False)```: Start the SDK with the given set of options. See [Starting the SDK](#starting-the-sdk). +- ```exp.start(username=None, password=None, uuid=None, secret=None, api_key=None, enable_network=True, host='https://api.goexp.io/', allow_pairing=False)```: Start the SDK with the given set of options. See [Getting Started](#getting-started). - ```exp.get_auth()```: Returns the raw dictionary returned by the server during authentication. - ```exp.get_connectction_status()```: Returns ```True``` or ```False``` dependending on whether the network connection is active. @@ -200,10 +127,10 @@ while True: ## Channels - ```channel = exp.get_channel(name, system=False, consumer=False)```: Get a channel by name. - ```responses = channel.broadcast(name, payload=None, timeout=0):``` Send a broadcast on this channel, with string ```name``` and JSON serializable type ```payload```. Wait for ```timeout``` seconds for responses. Returns a list of the responses. -- ```listener = channel.listen(name)```: Listen for messages with name ```name``` on the given channel. Returns a [Listener](#listener). +- ```listener = channel.listen(name)```: Listen for messages with name ```name``` on the given channel. Returns a [Listener](#listeners). -## Listener +## Listeners - ```listener.cancel()```: Permanently detach the listener. - ```broadcast = listener.wait(timeout=0)```: Block for broadcasts for ```timeout``` seconds. Returns a [broadcast](#broadcasts) or ```None```. @@ -253,3 +180,76 @@ experience.save() ``` +# Tutorial: The EXP Network + +The EXP network facilitates real time communication between entities connected to EXP. A user or device can broadcast a JSON serializable payload to users and devices in your organization, and listeners to those broadcasts can respond to the broadcasters. + +### Channels + +All messages on the EXP network are sent over a channel. Channels have a name, and two flags: ```system``` and ```consumer```. + +```python +channel = exp.get_channel('my_channel', system=False, consumer=False) +``` + +Use ```system=True``` to get a system channel. You cannot send messages on a system channels but can listen for system notifications, such as updates to API resources. + +Use ```consumer=True``` to get a consumer channel. Consumer devices can only listen or broadcast on consumer channels. When ```consumer=False``` you will not receive consumer device broadcasts and consumer devices will not be able to hear your broadcasts. + +Both ```system``` and ```consumer``` default to ```False```. Consumer devices will be unable to broadcast or listen to messages on non-consumer channels. + + +### Broadcasting + +Use the broadcast method of a channel object to send a named message containing an optional JSON serializable payload to other entities on the EXP network. You can optionally include a timeout to wait for responses to the broadcast. The broadcast will block for approximately the given timeout and return a ```list``` of response payloads. Each response payload can any JSON serializable type. + +```python + +channel = exp.get_channel('my_channel') +responses = channel.broadcast(name='my_event', timeout=5, payload='hello') +[print response for response in responses] + +``` + + +### Listening + +To listen for broadcasts, call the listen method of a channel object and pass in the name of the event you wish to listen for. When EXP listener has been registered and can start receiving events, a ```listener``` object will be returned. + +Call the ```wait``` method of a listener to block until a broadcast is received, passing in a timeout in seconds. If ```timeout``` elaspes and no broadcasts have been received, ```wait``` will return ```None```. + +Once a listener is created, it will receive broadcasts in a background thread even when not waiting. Calling ```wait``` will first attempt to return the oldest broadcast in the queue. Queued broadcasts will be discarded after ~60s if not retrieved during a ```wait```. + +```python + +channel = exp.get_channel('my_channel') +listener = channel.listen('my_event') + +while True: + broadcast = listener.wait(5) + if broadcast: + print 'Message received!' + print broadcast.payload + listener.cancel() + break + +``` + + +### Responding + +To respond to broadcast, call the respond method on the broadcast object, optionally passing in a JSON serializable response payload. + +```python + +channel = exp.get_channel('my_channel') +listener = channel.listen(name='my_event') + +while True: + broadcast = listener.wait(5) + if broadcast and broadcast.payload is 'hello': + print 'Responding to broadcast.' + broadcast.respond('Nice to meet you!') + +``` + From aadb3ffe0e8a12a27759686b8627386a4acf2a05 Mon Sep 17 00:00:00 2001 From: James Dalessio Date: Mon, 7 Mar 2016 16:23:15 -0500 Subject: [PATCH 034/104] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index dd6efd5..c0b7036 100644 --- a/README.md +++ b/README.md @@ -180,7 +180,7 @@ experience.save() ``` -# Tutorial: The EXP Network +## Using The EXP Network The EXP network facilitates real time communication between entities connected to EXP. A user or device can broadcast a JSON serializable payload to users and devices in your organization, and listeners to those broadcasts can respond to the broadcasters. From 021053c92231648922225b29c4300f2e18794aa4 Mon Sep 17 00:00:00 2001 From: James Dalessio Date: Thu, 24 Mar 2016 23:34:16 -0400 Subject: [PATCH 035/104] Adding tests. --- .configure | 6 + README.md | 50 +++++-- check.py | 16 +-- exp/__init__.py | 116 +---------------- exp/api.py | 265 ++++++++++++------------------------- exp/api_old.py | 203 +++++++++++++++++++++++++++++ exp/api_utils.py | 74 ----------- exp/authenticator.py | 141 ++++++++++++-------- exp/exceptions.py | 7 +- exp/exp.py | 216 ++++++++++++++++++++++++++++++ exp/logger.py | 19 --- exp/network.py | 297 ++++++++++++++++++++++-------------------- exp/runtime.py | 44 ------- setup.py | 2 +- tests/test_api.py | 35 +++++ tests/test_auth.py | 53 ++++++++ tests/test_network.py | 38 ++++++ tests/test_start.py | 101 ++++++++++++++ tests/test_stop.py | 29 +++++ tests/utils.py | 37 ++++++ 20 files changed, 1094 insertions(+), 655 deletions(-) create mode 100644 .configure create mode 100644 exp/api_old.py delete mode 100644 exp/api_utils.py create mode 100644 exp/exp.py delete mode 100644 exp/logger.py delete mode 100644 exp/runtime.py create mode 100644 tests/test_api.py create mode 100644 tests/test_auth.py create mode 100644 tests/test_network.py create mode 100644 tests/test_start.py create mode 100644 tests/test_stop.py create mode 100644 tests/utils.py diff --git a/.configure b/.configure new file mode 100644 index 0000000..3f9ed7a --- /dev/null +++ b/.configure @@ -0,0 +1,6 @@ +export EXP_USERNAME=email@email.com +export EXP_PASSWORD=Password12321 +export EXP_ORGANIZATION=scala +export EXP_HOST=http://localhost:9000 +export EXP_DEVICE_UUID=bbb91e7f-0869-46e6-8afd-e0161d5d35ca +export EXP_SECRET=d221547725fd41f0242c0b85c1e7cff514a9d51764a973e2f7abb027ea62bcf2e8b73499c5626cb5b20339998ea4f060 diff --git a/README.md b/README.md index c0b7036..44873a5 100644 --- a/README.md +++ b/README.md @@ -36,24 +36,48 @@ host | ```'https://api.goexp.io/'``` | The api server to authenticate with. enable_network | ```True``` | Whether to enable real time network communication. If set to ```False``` you will be unable to listen on the [EXP network](# Communicating on the EXP Network). -# SDK Reference - +# Reference ## Runtime -- ```exp.start(username=None, password=None, uuid=None, secret=None, api_key=None, enable_network=True, host='https://api.goexp.io/', allow_pairing=False)```: Start the SDK with the given set of options. See [Getting Started](#getting-started). -- ```exp.get_auth()```: Returns the raw dictionary returned by the server during authentication. -- ```exp.get_connectction_status()```: Returns ```True``` or ```False``` dependending on whether the network connection is active. + | Description +--- | --- +`exp_sdk.start(**options)` | Returns an instance of the SDK. See [startup options](#startup-options). +`exp_sdk.stop()` | Stops all running instances of the SDK. +`exp.stop()` | Stops the SDK instance. +`exp.is_connected` | Whether or not there is an active connection to the EXP network. +`exp.get_auth()` | Returns authentication payload. + ## Exceptions + | Description + --- | --- + `exp_sdk.ExpError` | Base class for all EXP exceptions. + `exp_sdk.UnexpectedError` | Raised when an unexpected error occurs. + `exp_sdk.RuntimeError` | Raised when [startup options](#startup-options) are incorrect or inconsistent. + `exp_sdk.AuthenticationError` | Raised when the sdk cannot authenticate due to bad credentials. + `exp_sdk.ApiError` | Raised when an API call fails. Has properties `message` and `code`. See the [API documentation](#https://docs.goexp.io). + + +## Channels + | Description + --- | --- + `exp.getChannel(name, consumer=False, system=False)` | Returns a channel with the given name and channel flags. + `channel.broadcast(name, payload, timeout)` | Returns a broadcast](#broadcasts) object. + `broadcast.response` + +## Broadcasts + +## Listeners + +## API + | Description + --- | --- + `exp.get(path, params=None, timeout=None)` | + `exp.post(path, payload=None, params=None, timeout=None)` | + `exp.patch(path, payload=None, params=None, timeout=None)` | + `exp.delete(path, payhload=None, params=None, timeout=None)` | + -- ```exp.ExpError```: Base class for errors raised by the SDK. -- ```exp.AuthenticationError```: Raised when SDK cannot authenticate. -- ```exp.RuntimeError```: Raised when options pass to ```exp.start``` are invalid or inconsistent. -- ```exp.HttpError```: Raised when an API request encouters and error. This exception has the following attributes: - - ```code```: The API REST code of the error. - - ```status```: The HTTP status code received. - - ```message```: A human readable description of the encountered error. -- ```exp.UnexpectedError```: Raised when an SDK method encounters an unexpected exception. ## Devices diff --git a/check.py b/check.py index bf326d6..6cdf8fc 100644 --- a/check.py +++ b/check.py @@ -4,16 +4,10 @@ if __name__ == '__main__': exp.start( - username='email@email.com', password='Password12321', + username='email@email.com', password='Password12321', organization='scala', host='http://localhost:9000/') - devices = exp.find_devices() - print devices - #channel = exp.get_channel('test_channel') - #listener = channel.listen('test_message') - #broadcast = listener.wait(15) - #if broadcast: - # print 'Got Broadcast' - # broadcast.respond('I got your message!') - print 'Finsihed!' - #exp.stop() \ No newline at end of file + device = exp.create_device() + print device.document['uuid'] + print device.document['secret'] + #device.delete() diff --git a/exp/__init__.py b/exp/__init__.py index 1be085b..ccd9e80 100644 --- a/exp/__init__.py +++ b/exp/__init__.py @@ -1,114 +1,4 @@ -""" Main module for Scala EXP SDK """ - -import signal -import sys - -from .runtime import runtime as _runtime -from .network import network as _network -from .network import socket as _socket -from .authenticator import authenticator as _authenticator -from .exceptions import * -from . import api -from . import api_utils - - -# Start the SDK. -def start (*args, **kwargs): - return _runtime.start(*args, **kwargs) - -def get_auth (): - return _authenticator.get_auth() - -def get_channel(*args, **kwargs): - return _network.get_channel(*args, **kwargs) - -def get_connection_status(*args, **kwargs): - return _socket.is_connected - - -def get (*args, **kwargs): - return api_utils.get(*args, **kwargs) - -def post (*args, **kwargs): - return api_utils.post(*args, **kwargs) - -def patch (*args, **kwargs): - return api_utils.patch(*args, **kwargs) - -def put (*args, **kwargs): - return api_utils.put(*args, **kwargs) - -def delete (*args, **kwargs): - return api_utils.delete(*args, **kwargs) - - -def get_device (*args, **kwargs): - return api.Device.get(*args, **kwargs) - -def find_devices (*args, **kwargs): - return api.Device.find(*args, **kwargs) - -def create_device (*args, **kwargs): - return api.Device.create(*args, **kwargs) - - -def get_thing (*args, **kwargs): - return api.Thing.get(*args, **kwargs) - -def find_things (*args, **kwargs): - return api.Thing.find(*args, **kwargs) - -def create_thing (*args, **kwargs): - return api.Thing.create(*args, **kwargs) - - -def get_experience (*args, **kwargs): - return api.Experience.get(*args, **kwargs) - -def find_experiences (*args, **kwargs): - return api.Experience.find(*args, **kwargs) - -def create_experience (*args, **kwargs): - return api.Experience.create(*args, **kwargs) - - -def get_location (*args, **kwargs): - return api.Location.get(*args, **kwargs) - -def find_locations (*args, **kwargs): - return api.Location.find(*args, **kwargs) - -def create_location (*args, **kwargs): - return api.Location.create(*args, **kwargs) - - -def get_data (*args, **kwargs): - return api.Data.get(*args, **kwargs) - -def find_data (*args, **kwargs): - return api.Data.find(*args, **kwargs) - -def create_data (*args, **kwargs): - return api.Data.create(*args, **kwargs) - - -def get_feed (*args, **kwargs): - return api.Feed.get(*args, **kwargs) - -def find_feeds (*args, **kwargs): - return api.Feed.find(*args, **kwargs) - -def create_feed (*args, **kwargs): - return api.Feed.create(*args, **kwargs) - - -def get_content (*args, **kwargs): - return api.Content.get(*args, **kwargs) - -def find_content (*args, **kwargs): - return api.Content.find(*args, **kwargs) - - -# Terminate the SDK when Ctrl-C is pressed. -signal.signal(signal.SIGINT, lambda signal, frame: sys.exit(1)) +""" Main module for EXP Python SDK """ +from .exp import start, stop +from .exceptions import * \ No newline at end of file diff --git a/exp/api.py b/exp/api.py index 2267cf4..21c25b0 100644 --- a/exp/api.py +++ b/exp/api.py @@ -1,194 +1,89 @@ import urllib +import requests +import traceback -from . import api_utils -from .network import network -class QueryResult (object): +class Api (object): - def __init__(self, results=None, total=None): - self.results = results - self.total = total + def __init__(self, sdk): + self._sdk = sdk - def __iter__(self): - for result in self.results: - yield result + def _get_url (self, path): + return '{0}{1}'.format(self._sdk.authenticator.get_auth()['api']['host'], urllib.quote(path)) - def __str__(self): - return str([result for result in self.results]) + def _get_headers (self): + return { 'Authorization': 'Bearer ' + self._sdk.authenticator.get_auth()['token'] } - def __contains__ (self, item): - return item in self.results - - def __len__ (self): - return len(self.results) - - def __getitem__ (self, index): - return self.results[index] - - - -class Resource (object): - - path = None - - def __init__ (self, document): - self.document = document - - @property - def uuid (self): - return self.document['uuid'] - - @classmethod - def _get_path (cls): - raise Exception('Not implemented.') - - @classmethod - def get (cls, uuid): - return cls(api_utils.get(cls.path + '/' + uuid)) - - @classmethod - def create (cls, document=None): - if not document: document = {} - document = api_utils.post(cls.path, document) - return cls(document) - - @classmethod - def find (cls, params=None): - response = api_utils.get(cls.path, params) - response['results'] = [cls(document) for document in response['results']] - return QueryResult(**response) - - def _get_path (self): - return self.__class__.path + '/' + self.uuid - - def delete (self): - return api_utils.delete(self._get_path()) - - def save (self): - document = api_utils.patch(self._get_path(), self.document) - self.document = document - - def refresh (self): - self.document = api_utils.get(this._get_path()) - - def get_channel (self, **kwargs): - return network.get_channel(self._get_channel_name(), **kwargs) - - def _get_channel_name (self): - return self.uuid - - def fling (payload, **kwargs): - return self.get_channel().broadcast('fling', payload, **kwargs) - - -class Device (Resource): - - path = '/api/devices' - - def identify (self): - return self.get_channel().broadcast('identify', None, 500) - - -class Thing (Resource): - - path = '/api/things' - - -class Feed (Resource): - - path = '/api/connectors/feeds' - - def get_data (self): - return api_utils(self._gat_path() + '/data') - - -class Experience (Resource): - - path = '/api/experiences' - - -class Location (Resource): - - path = '/api/locations' - - def get_zones (self): - return [Zone(document, self) for document in self.document['zones']] - - def get_layout_url (self): - return self._get_path() + '/layout?_rt=' + authenticator.get_auth()['restrictedToken'] - - -class Zone (Resource): - - def __init__ (self, document, location): - self._location = location - super(Zone, self).__init__(document) - - def save (self): - return self._location.save() - - def _get_channel_name (self): - return self._location.uuid + ':zone:' + self.document['key'] - - -class Data (Resource): - - path = '/api/data' - - def get_path (self): - return self.__class__.path + '/' + urllib.quote(this.document['group']) + '/' + urllib.quote(this.document['key']) - - @classmethod - def get (cls, group, key): - resource = cls({ 'group': group, 'key': key }) - resource.refresh() - return resource - - @classmethod - def create (cls, group, key, value): - resource = cls({ 'group': group, 'key': key, 'value': value }) - resource.save() - return resource - - def get_channel_name (self): - return 'data' + ':' + this.document['key'] + ':' + this.document['group'] - - -class Content (Resource): - - path = '/api/content' - - @property - def children (self): - if self._children is None: - if self.document['children']: - return [self.__class__(document) for document in self.document['children']] + def _on_error(self, exception): + if hasattr(exception, 'response'): + try: + payload = exception.response.json() + except: + self._sdk.logger.warn('API call encountered an unexpected error.') + self._sdk.logger.debug('API call encountered an unexpected error: %s' % traceback.format_exc()) + raise self._sdk.exceptions.UnexpectedError('API call encountered an unexpected error.') else: - self.refresh() - return self.children - return [] - - @property - def subtype (self): - return self.document['subtype'] - - def get_url (self): - auth = authenticator.get_auth() - delivery_url = auth['api']['host'] + '/api/delivery' - rt_string = '?_rt=' + auth['restrictedToken'] - if self.subtype == 'scala:content:file': - return delivery_url + this.document.path + rt_string - elif self.subtype == 'scala:content:app': - return delivery_url + this.document.path + rt_string - elif self.subtype == 'scala:content:url': - return self.document['url'] - - def get_variant_url (name): - auth = authenticator.get_auth() - delivery_url = auth['api']['host'] + '/api/delivery' - rt_string = '?_rt=' + auth['restrictedToken'] - if self.subtype == 'scala:content:file' and name in this.document['variants']: - return delivery_url + '?variant=' + urllib.quote(name) + rt_string - - + raise self._sdk.exceptions.ApiError(payload) + else: + self._sdk.logger.warn('API call encountered an unexpected error.') + self._sdk.logger.debug('API call encountered an unexpected error: %s' % traceback.format_exc()) + raise self._sdk.exceptions.UnexpectedError('API call encountered an unexpected error.') + + + def get(self, path, params=None, timeout=10): + try: + response = requests.get(self._get_url(https://github.com/path), timeout=timeout, params=params, headers=self._get_headers()) + if response.status_code == 404: + return None + response.raise_for_status() + try: + return response.json() + except ValueError: + return None + except Exception as exception: + return self._on_error(exception) + + def post(self, path, payload=None, params=None, timeout=10): + try: + response = requests.post(self._get_url(https://github.com/path), timeout=timeout, params=params, json=payload, headers=self._get_headers()) + response.raise_for_status() + try: + return response.json() + except ValueError: + return None + except Exception as exception: + return self._on_error(exception) + + def patch(self, path, payload=None, params=None, timeout=10): + try: + response = requests.patch(self._get_url(https://github.com/path), timeout=timeout, params=params, json=payload, headers=self._get_headers()) + response.raise_for_status() + try: + return response.json() + except ValueError: + return None + except Exception as exception: + return self._on_error(exception) + + def put(self, path, payload=None, params=None, timeout=10): + try: + response = requests.put(self._get_url(https://github.com/path), timeout=timeout, params=params, json=payload, headers=self._get_headers()) + response.raise_for_status() + try: + return response.json() + except ValueError: + return None + except Exception as exception: + return self._on_error(exception) + + def delete(self, path, payload=None, params=None, timeout=10): + try: + response = requests.delete(self._get_url(https://github.com/path), timeout=timeout, params=params, json=payload, headers=self._get_headers()) + response.raise_for_status() + try: + return response.json() + except ValueError: + return None + except Exception as exception: + return self._on_error(exception) diff --git a/exp/api_old.py b/exp/api_old.py new file mode 100644 index 0000000..c36ccf6 --- /dev/null +++ b/exp/api_old.py @@ -0,0 +1,203 @@ + + + + + +class QueryResult (object): + + def __init__(self, results=None, total=None): + self.results = results + self.total = total + + def __iter__(self): + for result in self.results: + yield result + + def __str__(self): + return str([result for result in self.results]) + + def __contains__ (self, item): + return item in self.results + + def __len__ (self): + return len(self.results) + + def __getitem__ (self, index): + return self.results[index] + + + +class Resource (object): + + path = None + + def __init__ (self, document): + self.document = document + + @classmethod + def get_resource_path (cls, uuid): + return cls.path + '/' + uuid + + def get_path (self): + return self.get_resource_path(self.uuid) + + @property + def uuid (self): + return self.document.get('uuid') + + + @classmethod + def get (cls, uuid, *args, **kwargs): + return cls(api_utils.get(cls.get_resource_path(uuid), *args, **kwargs) + + @classmethod + def create (cls, document=None, **kwargs): + return cls(api_utils.post(cls.collection_path, payload=document or {}, **kwargs) + + @classmethod + def find (cls, params=None): + response = api_utils.get(cls.path, params) + response['results'] = [cls(document) for document in response['results']] + return QueryResult(**response) + + + + def delete (self, **kwargs): + return api_utils.delete(self.get_path(), **kwargs) + + def save (self, **kwargs): + self.document = api_utils.patch(self.get_path(), payload=self.document, **kwargs) + + def refresh (self): + self.document = api_utils.get(self.get_path()) + + + + + def get_channel (self, **kwargs): + return network.get_channel(self._get_channel_name(), **kwargs) + + def get_channel_name (self): + return self.uuid + + def fling (payload, **kwargs): + return self.get_channel().broadcast('fling', payload, **kwargs) + + +class StandardResource (Resource): + + + + + + +class Device (Resource): + + path = '/api/devices' + + def identify (self): + return self.get_channel().broadcast('identify', None, 500) + + +class Thing (Resource): + + path = '/api/things' + + +class Feed (Resource): + + path = '/api/connectors/feeds' + + def get_data (self): + return api_utils(self._gat_path() + '/data') + + +class Experience (Resource): + + path = '/api/experiences' + + +class Location (Resource): + + path = '/api/locations' + + def get_zones (self): + return [Zone(document, self) for document in self.document['zones']] + + def get_layout_url (self): + return self._get_path() + '/layout?_rt=' + authenticator.get_auth()['restrictedToken'] + + +class Zone (Resource): + + def __init__ (self, document, location): + self._location = location + super(Zone, self).__init__(document) + + def save (self): + return self._location.save() + + def _get_channel_name (self): + return self._location.uuid + ':zone:' + self.document['key'] + + +class Data (Resource): + + path = '/api/data' + + def get_path (self): + return self.__class__.path + '/' + urllib.quote(this.document['group']) + '/' + urllib.quote(this.document['key']) + + @classmethod + def get (cls, group, key): + resource = cls({ 'group': group, 'key': key }) + resource.refresh() + return resource + + @classmethod + def create (cls, group, key, value): + resource = cls({ 'group': group, 'key': key, 'value': value }) + resource.save() + return resource + + def get_channel_name (self): + return 'data' + ':' + this.document['key'] + ':' + this.document['group'] + + +class Content (Resource): + + path = '/api/content' + + @property + def children (self): + if self._children is None: + if self.document['children']: + return [self.__class__(document) for document in self.document['children']] + else: + self.refresh() + return self.children + return [] + + @property + def subtype (self): + return self.document['subtype'] + + def get_url (self): + auth = authenticator.get_auth() + delivery_url = auth['api']['host'] + '/api/delivery' + rt_string = '?_rt=' + auth['restrictedToken'] + if self.subtype == 'scala:content:file': + return delivery_url + this.document.path + rt_string + elif self.subtype == 'scala:content:app': + return delivery_url + this.document.path + rt_string + elif self.subtype == 'scala:content:url': + return self.document['url'] + + def get_variant_url (name): + auth = authenticator.get_auth() + delivery_url = auth['api']['host'] + '/api/delivery' + rt_string = '?_rt=' + auth['restrictedToken'] + if self.subtype == 'scala:content:file' and name in this.document['variants']: + return delivery_url + '?variant=' + urllib.quote(name) + rt_string + + diff --git a/exp/api_utils.py b/exp/api_utils.py deleted file mode 100644 index 2ed3aa1..0000000 --- a/exp/api_utils.py +++ /dev/null @@ -1,74 +0,0 @@ -import requests -import urllib - -from .logger import logger -from .exceptions import ApiError, UnexpectedError -from .authenticator import authenticator - -def _on_error(exception): - if hasattr(exception, 'response'): - try: - payload = exception.response.json() - except Exception: - raise UnexpectedError() - else: - raise ApiError(payload) - else: - raise UnexpectedError() - - -def get(path, params=None): - try: - url = "{0}{1}".format(authenticator.get_auth()['api']['host'], urllib.quote(path)) - headers = {} - headers["Authorization"] = "Bearer " + authenticator.get_auth()['token'] - response = requests.get(url, timeout=10, params=params, headers=headers) - response.raise_for_status() - return response.json() - except Exception as exception: - return _on_error(exception) - -def post(path, payload=None, params=None): - try: - url = "{0}{1}".format(authenticator.get_auth()['api']['host'], urllib.quote(path)) - headers = {} - headers["Authorization"] = "Bearer " + authenticator.get_auth()['token'] - response = requests.post(url, timeout=10, params=params, json=payload, headers=headers) - response.raise_for_status() - return response.json() - except Exception as exception: - return _on_error(exception) - -def patch(path, payload=None, params=None): - try: - url = "{0}{1}".format(authenticator.get_auth()['api']['host'], urllib.quote(path)) - headers = {} - headers["Authorization"] = "Bearer " + authenticator.get_auth()['token'] - response = requests.patch(url, timeout=10, params=params, json=payload, headers=headers) - response.raise_for_status() - return response.json() - except Exception as exception: - return _on_error(exception) - -def put(path, payload=None, params=None): - try: - url = "{0}{1}".format(authenticator.get_auth()['api']['host'], urllib.quote(path)) - headers = {} - headers["Authorization"] = "Bearer " + authenticator.get_auth()['token'] - response = requests.put(url, timeout=10, params=params, json=payload, headers=headers) - response.raise_for_status() - return response.json() - except Exception as exception: - return _on_error(exception) - -def delete(path, payload=None, params=None): - try: - url = "{0}{1}".format(authenticator.get_auth()['api']['host'], urllib.quote(path)) - headers = {} - headers["Authorization"] = "Bearer " + authenticator.get_auth()['token'] - response = requests.delete(url, timeout=10, params=params, json=payload, headers=headers) - response.raise_for_status() - return None - except Exception as exception: - return _on_error(exception) - diff --git a/exp/authenticator.py b/exp/authenticator.py index fd3f518..5fb3248 100644 --- a/exp/authenticator.py +++ b/exp/authenticator.py @@ -5,88 +5,128 @@ import base64 import hmac import hashlib +import threading +from . import logger +from . import exceptions -from .logger import logger -from .exceptions import AuthenticationError, UnexpectedError, RuntimeError -class _Authenticator (object): +class Authenticator (object): - def __init__(self): + def __init__(self, sdk): + self._sdk = sdk self._auth = None - self._options = None self._time = None - - def configure (self, **options): - self._options = options - self._auth = None + self._failed = False + self._lock = threading.Lock() def get_auth(self): - if not self._options: - raise RuntimeError('SDK has not been configured.') - elif not self._auth: - self._login() - elif self._time < int(time.time()): - self._refresh() + self._lock.acquire() + if self._failed: + self._lock.release() + raise exceptions.AuthenticationError('Invalid credentials. Please restart the SDK with valid credentials.') + try: + if not self._auth: + self._login() + elif self._time < int(time.time()): + self._refresh() + except: + self._lock.release() + raise + self._lock.release() return self._auth def _login (self): payload = {} - if self._options.get('type') is 'user': - logger.debug('Login started as a user.') - payload['username'] = self._options.get('username') - payload['password'] = self._options.get('password') - payload['organization'] = self._options.get('organization') - elif self._options.get('type') is 'device': - logger.debug('Login started as a device.') + self._sdk.logger.debug('Login starting.') + if self._sdk.options.get('type') is 'user': + self._sdk.logger.debug('Login generating user payload.') + payload['username'] = self._sdk.options.get('username') + payload['password'] = self._sdk.options.get('password') + payload['organization'] = self._sdk.options.get('organization') + elif self._sdk.options.get('type') is 'device': + self._sdk.logger.debug('Login generating device payload.') token_payload = {} - token_payload['uuid'] = self._options.get('uuid') or '_' - token_payload['allowPairing'] = self._options.get('allow_pairing') - payload['token'] = self.generate_jwt(token_payload, self._options.get('secret') or '_') - elif self._options.get('type') is 'consumer_app': - logger.debug('Login started as a consumer app.') + token_payload['uuid'] = self._sdk.options.get('uuid') or '_' + token_payload['type'] = 'device' + token_payload['allowPairing'] = self._sdk.options.get('allow_pairing') + payload['token'] = self.generate_jwt(token_payload, self._sdk.options.get('secret') or '_') + elif self._sdk.options.get('type') is 'consumer_app': + self._sdk.logger.debug('Login generating consumer app payload.') token_payload = {} - token_payload['uuid'] = self._options.get('uuid') or '_' - payload['token'] = self.generate_jwt(token_payload, self._options.get('api_key')) - url = self._options.get('host') + '/api/auth/login' - response = requests.request('POST', url, json=payload) + token_payload['type'] = 'consumerApp' + token_payload['uuid'] = self._sdk.options.get('uuid') or '_' + payload['token'] = self.generate_jwt(token_payload, self._sdk.options.get('api_key')) + url = self._sdk.options.get('host') + '/api/auth/login' + self._sdk.logger.debug('Sending login payload: %s' % payload) + try: + response = requests.request('POST', url, json=payload) + except Exception as exception: + self._sdk.logger.warn('Login encountered an unexpected error.') + self._sdk.logger.debug('Login encountered an unexpected error: %s', traceback.format_exc()) + self._auth = None + self._time = None + raise exceptions.UnexpectedError('Login encountered an unexpected error.') if response.status_code == 200: + self._sdk.logger.debug('Login request successful.') self._on_success(response) - logger.debug('Login successful.') elif response.status_code == 401: - raise AuthenticationError('Login failed due to invalid credentials.') + self._sdk.logger.critical('Invalid credentials.') + self._sdk.logger.debug('Invalid credentials.') + self._sdk.network.stop() + self._auth = None + self._time = None + self._failed = True + raise exceptions.AuthenticationError('Invalid credentials.') else: - raise UnexpectedError('Login failed due to an unexpected HTTP status code: %s.' % response.status_code) + self._sdk.logger.warn('Login received an unexpected HTTP status code: %s.' % response.status_code) + self._sdk.logger.debug('Login received an unexpected HTTP status code: %s' % response.status_code) + self._auth = None + self._time = None + raise exceptions.UnexpectedError('Login received an unexpected HTTP status code: %s.' % response.status_code) def _refresh (self): - logger.debug('Authentication token refresh starting.') - url = self._options.get('host') + '/api/auth/token' + self._sdk.logger.debug('Authentication token refresh starting.') + url = self._sdk.options.get('host') + '/api/auth/token' headers = { 'Authorization': 'Bearer ' + self._auth.get('token') } + self._sdk.logger.debug('Sending token refresh request.') try: response = requests.request('POST', url, headers=headers) except Exception as exception: - raise UnexpectedError('Authentication token refresh encountered an unexpected error.') - if response.status_code is 200: + self._sdk.logger.warn('Token refresh encountered an unexpected error.') + self._sdk.logger.debug('Token refresh encountered an unexpected error: %s.', traceback.format_exc()) + self._auth = None + self._time = None + raise UnexpectedError('Token refresh encountered an unexpected error.') + if response.status_code == 200: + self._sdk.logger.debug('Token refresh request successful.') self._on_success(response) - logger.debug('Authentication token refresh successful.') - elif response.status_code is 401: - logger.debug('Authentication token refresh failed due to expired or invalid token.') + elif response.status_code == 401: + self._sdk.logger.warn('Token refresh request failed due to expired or invalid token.') + self._sdk.logger.debug('Token refresh request failed due to expired or invalid token.') + self._auth = None + self._time = None self._login() else: - raise UnexpectedError('Authentication token refresh request failed due to an unexpected HTTP status code: %d' % response.status_code) + self._sdk.logger.warn('Token refresh request received an unexpected HTTP status code: %s.' % response.status_code) + self._sdk.logger.debug('Token refresh request receive an unexpected HTTP status code: %s' % response.status_code) + self._auth = None + self._time = None + raise exceptions.UnexpectedError('Token refresh request receive an unexpected HTTP status code: %d' % response.status_code) def _on_success (self, response): - logger.debug('Authentication update starting.') + self._sdk.logger.debug('Authentication update starting.') try: auth = response.json() except Exception as exception: - raise UnexpectedError('An unexpected error has occured parsing authentication response.') - else: - logger.critical(auth) - logger.critical(response.status_code) - self._auth = auth - self._time = (int(time.time()) + auth['expiration'] * 3.0 / 1000.0 ) / 4.0 - logger.debug('Authentication update successful: %s' % auth) + self._sdk.logger.warn('Authentication update encountered an unexpected error.') + self._sdk.logger.debug('Authentication updated encountered an unexpected error:' % traceback.format_exc()) + self._auth = None + self._time = None + raise exceptions.UnexpectedError('Authentication update encountered an unexpected error.') + self._auth = auth + self._time = (int(time.time()) + auth['expiration'] * 3.0 / 1000.0 ) / 4.0 + self._sdk.logger.debug('Authentication update successful: %s' % auth) @staticmethod def generate_jwt (payload, secret): @@ -105,4 +145,3 @@ def generate_jwt (payload, secret): return '.'.join([algorithm_b64, payload_b64, signature_b64]) -authenticator = _Authenticator() \ No newline at end of file diff --git a/exp/exceptions.py b/exp/exceptions.py index fa7d8b9..0b1c52a 100644 --- a/exp/exceptions.py +++ b/exp/exceptions.py @@ -1,6 +1,9 @@ -from .logger import logger import traceback +import logging + +logger = logging.getLogger('exp') + class ExpError (Exception): @@ -16,7 +19,7 @@ class AuthenticationError (ExpError): class UnexpectedError (ExpError): - def __init__ (self, *arg, **kwargs): + def __init__ (self, *args, **kwargs): logger.debug('An unexpected error occured:') logger.debug(traceback.format_exc()) super(UnexpectedError, self).__init__(*args, **kwargs) diff --git a/exp/exp.py b/exp/exp.py new file mode 100644 index 0000000..3490bbf --- /dev/null +++ b/exp/exp.py @@ -0,0 +1,216 @@ +import signal +import sys +import logging +import traceback + +from logging.handlers import RotatingFileHandler + +from . import network +from . import authenticator +from . import api +from . import exceptions + + +""" List of all instances of Exp. """ +instances = [] + + +""" Configure the logger. """ +file_handler_formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s') + +file_handler = RotatingFileHandler('debug.log', mode='a', maxBytes=5*1024*1024, backupCount=5, encoding=None, delay=0) +file_handler.setFormatter(file_handler_formatter) +file_handler.setLevel(logging.DEBUG) + +stream_handler_formatter = logging.Formatter('EXP/%(levelname)-10s: %(message)s') + +stream_handler = logging.StreamHandler() +stream_handler.setFormatter(stream_handler_formatter) +stream_handler.setLevel(logging.INFO) + +logger = logging.getLogger('exp') +#logger.addHandler(file_handler) +#logger.addHandler(stream_handler) +logger.setLevel(logging.DEBUG) + + +""" Terminate all running instances when Ctrl-C is pressed. """ +signal.signal(signal.SIGINT, lambda signal, frame: stop()) + + +def start (enable_network=True, host='https://api.goexp.io/', **options): + + """ Validate SDK options """ + options['host'] = host + options['enable_network'] = enable_network + if options.get('type') is 'user' or ((options.get('username') or options.get('password') or options.get('organization')) and not options.get('type')): + options['type'] = 'user' + if not options.get('username'): + raise exceptions.RuntimeError('Please specify the username.') + if not options.get('password'): + raise exceptions.RuntimeError('Please specify the password.') + if not options.get('organization'): + raise exceptions.RuntimeError('Please specify the organization.') + elif options.get('type') is 'device' or ((options.get('secret') or options.get('allow_pairing')) and not options.get('type')): + options['type'] = 'device' + if not options.get('uuid') and not options.get('allow_pairing'): + raise exceptions.RuntimeError('Please specify the device uuid.') + if not options.get('secret') and not options.get('allow_pairing'): + raise exceptions.RuntimeError('Please specify the device secret.') + elif options.get('type') is 'consumer_app' or (options.get('api_key') and not options.get('type')): + options['type'] = 'consumer_app' + if not options.get('uuid'): + raise exceptions.RuntimeError('Please specify the consumer app uuid.') + if not options.get('api_key'): + raise exceptions.RuntimeError('Please specify the consumer app api key.') + else: + raise exceptions.RuntimeError('Please specify authentication type.') + + """ Generate wrapper and SDK instance """ + sdk = Sdk(**options) + if options['enable_network']: + sdk.network.start() + exp = Exp(sdk) + instances.append(exp) + return exp + + +def stop (): + """ Stop all running SDK instances. """ + [exp.stop() for exp in instances[:]] + + +class Sdk (object): + """ Wrapper to hold SDK modules. """ + + def __init__(self, **options): + self.options = options + self.authenticator = authenticator.Authenticator(self) + self.api = api.Api(self) + self.network = network.Network(self) + self.logger = logger + self.exceptions = exceptions + + +class Exp (object): + + def __init__(self, sdk): + self._sdk_ = sdk + + @property + def _sdk(self): + if not self._sdk_: + raise exceptions.RuntimeError('This SDK instance is stopped.') + return self._sdk_ + + + """ Runtime """ + + def stop (self): + if self in instances: + instances.remove(self) + self._sdk.network.stop() + self._sdk_ = None # Remove internal references to SDK modules. All calls will now throw runtime error. + + def get_auth (self): + return self._sdk.authenticator.get_auth() + + + """ Naked API """ + + def get (self, *args, **kwargs): + return self._sdk.api.get(*args, **kwargs) + + def post (self, *args, **kwargs): + return self._sdk.api.post(*args, **kwargs) + + def patch (self, *args, **kwargs): + return self._sdk.api.patch(*args, **kwargs) + + def put (self, *args, **kwargs): + return self._sdk.api.put(*args, **kwargs) + + def delete (self, *args, **kwargs): + return self._sdk.api.delete(*args, **kwargs) + + """ Network """ + + @property + def is_connected(self): + return self._sdk.network.is_connected + + def get_channel(self, *args, **kwargs): + return self._sdk.network.get_channel(*args, **kwargs) + + + + +""" + + # def get_device (*args, **kwargs): + # return api.Device.get(*args, **kwargs) + + # def find_devices (*args, **kwargs): + # return api.Device.find(*args, **kwargs) + + # def create_device (*args, **kwargs): + # return api.Device.create(*args, **kwargs) + + + # def get_thing (*args, **kwargs): + # return api.Thing.get(*args, **kwargs) + + # def find_things (*args, **kwargs): + # return api.Thing.find(*args, **kwargs) + + # def create_thing (*args, **kwargs): + # return api.Thing.create(*args, **kwargs) + + + # def get_experience (*args, **kwargs): + # return api.Experience.get(*args, **kwargs) + + # def find_experiences (*args, **kwargs): + # return api.Experience.find(*args, **kwargs) + + # def create_experience (*args, **kwargs): + # return api.Experience.create(*args, **kwargs) + + + # def get_location (*args, **kwargs): + # return api.Location.get(*args, **kwargs) + + # def find_locations (*args, **kwargs): + # return api.Location.find(*args, **kwargs) + + # def create_location (*args, **kwargs): + # return api.Location.create(*args, **kwargs) + + + # def get_data (*args, **kwargs): + # return api.Data.get(*args, **kwargs) + + # def find_data (*args, **kwargs): + # return api.Data.find(*args, **kwargs) + + # def create_data (*args, **kwargs): + # return api.Data.create(*args, **kwargs) + + + # def get_feed (*args, **kwargs): + # return api.Feed.get(*args, **kwargs) + + # def find_feeds (*args, **kwargs): + # return api.Feed.find(*args, **kwargs) + + # def create_feed (*args, **kwargs): + # return api.Feed.create(*args, **kwargs) + + + # def get_content (*args, **kwargs): + # return api.Content.get(*args, **kwargs) + + # def find_content (*args, **kwargs): + # return api.Content.find(*args, **kwargs) + +""" diff --git a/exp/logger.py b/exp/logger.py deleted file mode 100644 index 775b1c5..0000000 --- a/exp/logger.py +++ /dev/null @@ -1,19 +0,0 @@ - -import logging -from logging.handlers import RotatingFileHandler - - -_file_handler_formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s') -_file_handler = RotatingFileHandler('debug.log', mode='a', maxBytes=5*1024*1024, backupCount=5, encoding=None, delay=0) -_file_handler.setFormatter(_file_handler_formatter) -_file_handler.setLevel(logging.DEBUG) - -_stream_handler_formatter = logging.Formatter('EXP/%(levelname)-10s: %(message)s') -_stream_handler = logging.StreamHandler() -_stream_handler.setLevel(logging.INFO) -_stream_handler.setFormatter(_stream_handler_formatter) - -logger = logging.getLogger('exp') -logger.addHandler(_file_handler) -logger.addHandler(_stream_handler) -logger.setLevel(logging.DEBUG) diff --git a/exp/network.py b/exp/network.py index 586f753..bed884b 100644 --- a/exp/network.py +++ b/exp/network.py @@ -2,59 +2,56 @@ import threading import uuid -from .logger import logger import urlparse from socketIO_client import SocketIO, BaseNamespace import json import base64 -from . import api_utils - import traceback -from .authenticator import authenticator - - class _Broadcast (object): - _path = '/api/networks/current/response' - - def __init__(self, message): + def __init__(self, sdk, message): + self._sdk = sdk self._message = message - self._time = int(time.time()) + self.time = int(time.time()) @property def payload(self): return self._message['payload'] - def respond(self, payload): - response = { 'id': self._message['id'], 'channel': self._message['channel'], 'payload': payload } - return api_utils.post(self._path, response) + def respond(self, response): + self._sdk.api.post('/api/networks/current/responses', {'id': self._message['id'], 'channel': self._message['channel'], 'payload': response }) class _Listener (object): - def __init__(self, namespace, max_age=60): + def __init__(self, namespace, sdk, max_age=60, **kwargs): self._namespace = namespace + self._sdk = sdk self._max_age = max_age self._event = threading.Event() self._broadcasts = [] - def _receive (self, message): - broadcast = _Broadcast(message) + def _prune (self): now = int(time.time()) - self._broadcasts = [broadcast for broadcast in self._broadcasts if now - broadcast._time < self._max_age] - self._broadcasts.append(broadcast) + self._broadcasts = [broadcast for broadcast in self._broadcasts if now - broadcast.time < self._max_age] + + def receive (self, message): + self._prune() + self._broadcasts.append(_Broadcast(self._sdk, message)) self._event.set() - def wait (self, timeout=None): + def wait (self, timeout=0, **kwargs): + self._prune() if not self._broadcasts: self._event.clear() self._event.wait(timeout) if self._broadcasts: - return self._broadcasts.pop(0) + broadcast = self._broadcasts.pop(0) + return broadcast def cancel(self): self._namespace.cancel_listener(self) @@ -62,11 +59,12 @@ def cancel(self): class _Namespace (object): - def __init__(self): + def __init__(self, sdk): self._listeners = [] + self._sdk = sdk def listen (self, **kwargs): - listener = _Listener(self, **kwargs) + listener = _Listener(self, self._sdk, **kwargs) self._listeners.append(listener) return listener @@ -75,7 +73,7 @@ def cancel_listener(self, listener): self._listeners.remove(listener) def receive (self, message): - [listener._receive(message) for listener in self._listeners] + [listener.receive(message) for listener in self._listeners] @property def has_listeners (self): @@ -84,32 +82,33 @@ def has_listeners (self): class _Channel (object): - def __init__ (self, id): + def __init__ (self, id, sdk): self._id = id + self._sdk = sdk self._namespaces = {} - self._subscription = threading.Event() + self.subscription = threading.Event() - def broadcast (self, name, payload=None, timeout=5): + def broadcast (self, name, payload=None, timeout=0.1): path = '/api/networks/current/broadcasts' - message = {'channel': self._id, 'name': name, 'payload': payload} - params = {'timeout': timeout} - return api_utils.post(path, message, params) + payload = {'channel': self._id, 'name': name, 'payload': payload} + params = {'timeout': timeout * 1000 } + return self._sdk.api.post(path, payload, params) def listen (self, name, **kwargs): if not self._namespaces.get(name): - self._namespaces[name] = _Namespace() + self._namespaces[name] = _Namespace(self._sdk) listener = self._namespaces[name].listen(**kwargs) - if not self._subscription.is_set(): - socket.emit('subscribe', [self._id]) - self._subscription.wait() + if not self.subscription.is_set(): + self._sdk.network.emit('subscribe', [self._id]) + self.subscription.wait() return listener - def _receive (self, message): + def receive (self, message): if message['name'] in self._namespaces: self._namespaces[message['name']].receive(message) @property - def _has_listeners (self): + def has_listeners (self): return any([namespace.has_listeners for name, namespace in self._namespaces.iteritems()]) @@ -120,169 +119,183 @@ def _has_listeners (self): -class _Network (object): +class Network (object): - def __init__(self): + def __init__(self, sdk): + self._sdk = sdk self._channels = {} self._auth = None - self._options = None + self._socket = None + self._abort = False self._parent = threading.currentThread() - self._thread = None + self._thread = threading.Thread(target=lambda: self._main_event_loop()) self._lock = threading.Lock() - self._is_started = False def start (self): - self._lock.aquire() - if self._is_started: - return - self._is_started = True - self._parent = threading.currentThread() - self._thread = threading.Thread(target=lambda: self._main_event_loop()) self._thread.start() - self._lock.release() - def configure (self, **options): - self._lock.acquire() - self._auth = None - self._options = options - self._lock.release() + def stop (self): + self._abort = True def get_channel(self, name, system=False, consumer=False): channel_id = self._generate_channel_id(name, system, consumer) - return self.get_channel_by_id(channel_id) + channel = self._get_channel_by_id(channel_id) + return channel - def get_channel_by_id (self, channel_id): + def emit (self, name, payload): + if not self._socket: + return False + return self._socket.emit(name, payload) + + def _get_channel_by_id (self, channel_id): if channel_id not in self._channels: - self._channels[channel_id] = _Channel(channel_id) + self._channels[channel_id] = _Channel(channel_id, self._sdk) return self._channels[channel_id] - @staticmethod - def _generate_channel_id (name, system=False, consumer=False): - organization = authenticator.get_auth()['identity']['organization'] + def _generate_channel_id (self, name, system=False, consumer=False): + organization = self._sdk.authenticator.get_auth()['identity']['organization'] raw_id = [organization, name, 1 if system else 0, 1 if consumer else 0] json_id = json.dumps(raw_id, separators=(',', ':')) return base64.b64encode(json_id) - """ Callbacks on socket events """ - - def on_connect(self): - ids = [id for id, channel in self._channels.iteritems() if channel._has_listeners] - socket.emit('subscribe', ids) + def on_connect(self, socket): + if socket != self._socket: + return + [channel.subscription.clear() for id, channel in self._channels.iteritems()] + self._socket.emit('subscribe', [id for id, channel in self._channels.iteritems() if channel.has_listeners]) - def on_disconnect(self): - [channel._subscription.clear() for id, channel in self._channels.iteritems()] + def on_disconnect(self, socket): + if socket != self._socket: + return + [channel.subscription.clear() for id, channel in self._channels.iteritems()] - def on_subscribed(self, ids): - [self.get_channel_by_id(id)._subscription.set() for id in ids] + def on_subscribed(self, socket, ids): + if socket != self._socket: + return + [self._get_channel_by_id(id).subscription.set() for id in ids] - def on_broadcast (self, message): - self.get_channel_by_id(message['channel'])._receive(message) + def on_broadcast (self, socket, message): + if socket != self._socket: + return + self._get_channel_by_id(message['channel']).receive(message) def _main_event_loop (self): while True: - self._lock.acquire() + """ If parent thread is dead or network is inactive, disconnect and break thread. """ + if not self._parent.is_alive() or self._abort: + if self._socket: + self._socket.stop() + self._socket = None + break - """ If network is not enabled or options aren't set, disconnect and wait. """ - if not self._options or not self._options['enable_network']: - socket.disconnect() - self._lock.release() + """ Attempt to retrieve current auth. """ + try: + auth = self._sdk.authenticator.get_auth() + except Exception: + self._auth = None time.sleep(1) continue - """ If parent thread is dead, disconnect and break event loop. """ - if self._parent and not self._parent.is_alive(): - socket.disconnect() - self._lock.release() - break; + """ Disconnect if auth has changed. """ + if auth != self._auth: + self._auth = auth + if self._socket: + self._socket.stop() + self._socket = None - """ Attempt to retrieve current auth. If auth has changed, reconnect. """ - try: - auth = authenticator.get_auth() - if auth != self._auth: - self._auth = auth - socket.connect(**self._auth) - except Exception: - socket.disconnect() - self._lock.release() + """ Do nothing if there is no auth. """ + if not self._auth: time.sleep(1) continue + """ Start the socket if not started. """ + if not self._socket: + try: + self._socket = _Socket(self._sdk) + self._socket.start(**self._auth) + except: + self._sdk.logger.warning('Socket failed to start.') + self._sdk.logger.warning('Socket failed to start: %s', traceback.format_exc()) + self._socket = None + time.sleep(1) + continue + """ Listen for socket events. """ try: - socket.wait(1) - except Exception: - self._lock.release() + self._socket.wait(1) + except: + self._sdk.logger.warning('Socket failed to wait for messages.') + self._sdk.logger.warning('Socket failed to wait for messages: %s', traceback.format_exc()) time.sleep(1) - continue - else: - self._lock.release() + + class _Socket (object): - def __init__(self): + def __init__(self, sdk): + self._sdk = sdk self._socket = None - @property - def is_connected (self): - return self._socket and self._socket.connected + def start (self, **auth): - def connect (self, **auth): - self.disconnect() - params = { 'token': auth['token'] } - parsed_host = urlparse.urlparse(auth['network']['host']) - self._socket = SocketIO(parsed_host.hostname, - parsed_host.port, - params=params, - Namespace=_SocketHandler, - hurry_interval_in_seconds=10) - - def disconnect (self): - if self._socket: - network.on_disconnect() - try: - self._socket.disconnect() - self._socket = None - except: - pass - network.on_disconnect() - def wait (self, seconds): - if self.is_connected: - try: - self._socket.wait(seconds=seconds) - except Exception: - logger.warning('Error in network thread.') - logger.debug('Error in network thread: %s', traceback.format_exc()) - time.sleep(seconds) - else: - time.sleep(seconds) - - def emit (self, name, payload): - if self.is_connected: - self._socket.emit(name, payload) + class Namespace (BaseNamespace): + def on_broadcast (self, message): + self.socket._sdk.network.on_broadcast(self.socket, message) + def on_connect (self): + self.socket._sdk.network.on_connect(self.socket) -class _SocketHandler(BaseNamespace): + def on_disconnect (self): + self.socket._sdk.network.on_disconnect(self.socket) - def on_broadcast(self, message): - network.on_broadcast(message) + def on_subscribed (self, message): + self.socket._sdk.network.on_subscribed(self.socket, message) - def on_connect(self): - network.on_connect() + Namespace.socket = self - def on_disconnect(self): - network.on_disconnect() + params = { 'token': auth['token'] } + parsed_host = urlparse.urlparse(auth['network']['host']) + self._socket = SocketIO(parsed_host.hostname, parsed_host.port, Namespace, params=params, hurry_interval_in_seconds=10) - def on_subscribed(self, ids): - network.on_subscribed(ids) + def stop (self): + self._sdk.logger.debug('Disconnecting from network.') + try: + self._socket.disconnect() + self._sdk.logger.debug('Disconnected from network') + except: + self._sdk.logger.warning('Failed to disconnect from network.') + self._sdk.logger.debug('Failed to disconnect from network: %s', traceback.format_exc()) + def wait (self, seconds): + if not self.is_connected: + return time.sleep(seconds) + try: + self._socket.wait(seconds) + except: + self._sdk.logger.warning('Failed to wait for socket messages.') + self._sdk.logger.debug('Failed to wait for socket messages: %s', traceback.format_exc()) + time.sleep(seconds) + def emit (self, name, payload): + if not self.is_connected: + self._sdk.logger.debug('Failed to emit socket message: Device is offline.') + return False + try: + self._sdk.logger.debug('Emitting socket message: %s,%s', name, payload) + self._socket.emit(name, payload) + return True + except: + self._sdk.logger.warning('Failed to emit socket message.') + self._sdk.logger.debug('Failed to emit socket message: %s', traceback.format_exc()) + return False -socket = _Socket() -network = _Network() \ No newline at end of file + @property + def is_connected (self): + return self._socket and self._socket.connected diff --git a/exp/runtime.py b/exp/runtime.py deleted file mode 100644 index f8249c8..0000000 --- a/exp/runtime.py +++ /dev/null @@ -1,44 +0,0 @@ - -from .logger import logger -from .network import network -from .authenticator import authenticator -from .exceptions import RuntimeError - -class Runtime (object): - - def start (self, enable_network=True, host='https://api.goexp.io/', **options): - - options['host'] = host - options['enable_network'] = enable_network - - if options.get('type') is 'user' or ((options.get('username') or options.get('password') or options.get('organization')) and not options.get('type')): - options['type'] = 'user' - if not options.get('username'): - raise RuntimeError('Please specify the username.') - if not options.get('password'): - raise RuntimeError('Please specify the password.') - if not options.get('organization'): - raise RuntimeError('Please specify the organization.') - elif options.get('type') is 'device' or ((options.get('secret') or options.get('allow_pairing')) and not options.get('type')): - options['type'] = 'device' - if not options.get('uuid') and not options.get('allow_pairing'): - raise RuntimeError('Please specify the device uuid.') - if not options.get('secret') and not options.get('allow_pairing'): - raise RuntimeError('Please specify the device secret.') - elif options.get('type') is 'consumer_app' or (options.get('api_key') and not options.get('type')): - options['type'] = 'consumer_app' - if not options.get('uuid'): - raise RuntimeError('Please specify the consumer app uuid.') - if not options.get('api_key'): - raise RuntimeError('Please specify the consumer app api key.') - else: - raise RuntimeError('Please specify authentication type.') - - authenticator.configure(**options) # Configure the authenticator. - network.configure(**options) # Configure the network. - network.start() - authenticator.get_auth() # Block until authentication has been received. - logger.info('EXP SDK started successfully.') - - -runtime = Runtime() diff --git a/setup.py b/setup.py index ccdb496..ae3cf3d 100644 --- a/setup.py +++ b/setup.py @@ -2,7 +2,7 @@ from setuptools import setup, find_packages setup( - name="exp", + name="exp_sdk", version="0.0.0", packages=find_packages(), install_requires=["requests", "socketIO_client"] diff --git a/tests/test_api.py b/tests/test_api.py new file mode 100644 index 0000000..2cd1a23 --- /dev/null +++ b/tests/test_api.py @@ -0,0 +1,35 @@ +import utils +import random +import string + +class Test (utils.Device): + + def test_get (self): + self.exp.get('/api/devices/' + self.device_credentials.get('uuid')) + + def test_post (self): + self.exp.post('/api/experiences', {}) + + def test_patch (self): + document = self.exp.post('/api/experiences', {}) + self.exp.patch('/api/experiences/' + document['uuid'], {}) + + def test_put (self): + self.exp.put('/api/data/test1/test2', { 'a': 1 }) + + def test_delete (self): + document = self.exp.post('/api/experiences', {}) + self.exp.delete('/api/experiences/' + document['uuid']) + + def test_get_none (self): + document = self.exp.get('/api/devices/adsdsadas') + if document: + raise Exception + + def test_post_error (self): + name = ''.join(random.choice(string.lowercase) for i in range(10)) + self.exp.post('/api/experiences', { 'name': name }) + try: + self.exp.post('/api/experiences', { 'name': name }) + except self.exp_sdk.ApiError: + pass \ No newline at end of file diff --git a/tests/test_auth.py b/tests/test_auth.py new file mode 100644 index 0000000..5a35790 --- /dev/null +++ b/tests/test_auth.py @@ -0,0 +1,53 @@ +import unittest +import requests +import time + +from . import utils + +from exp import exceptions + +class AuthBase (object): + + + def test_authentication (self): + self.exp.get_auth() + + def test_token_refresh (self): + self.exp._sdk.authenticator._login() + self.exp._sdk.authenticator._refresh() + + def test_refresh_401 (self): + auth = self.exp.get_auth() + auth['token'] = auth['token'] + 'blah' + self.exp._sdk.options['uuid'] = 'blah' + self.exp._sdk.options['username'] = 'blah' + try: + self.exp._sdk.authenticator._refresh() + except self.exp_sdk.AuthenticationError: + pass + else: + raise Exception + + + +class TestDeviceAuth (AuthBase, utils.Device, unittest.TestCase): pass +class TestUserAuth (AuthBase, utils.User, unittest.TestCase): pass +class TestConsumerAuth (AuthBase, utils.Consumer, unittest.TestCase): pass + + +class TestDevice401 (utils.Base, unittest.TestCase): + + def test_login_401 (self): + self.device_credentials['uuid'] = 'wrong uuid' + self.exp = self.exp_sdk.start(**self.device_credentials) + try: + self.exp.get_auth() + except self.exp_sdk.AuthenticationError: + try: + self.exp.get_auth() + except self.exp_sdk.AuthenticationError: + pass + else: + raise Exception + else: + raise Exception diff --git a/tests/test_network.py b/tests/test_network.py new file mode 100644 index 0000000..fde43bf --- /dev/null +++ b/tests/test_network.py @@ -0,0 +1,38 @@ +import utils +import random +import string +import threading +import time + +class Test (utils.Device): + + def test_simple_message_pattern (self): + channel = self.exp.get_channel('test_channel') + listener = channel.listen('test_message') + channel.broadcast('test_message', { 'a': 1 }) + broadcast = listener.wait() + if broadcast.payload['a'] != 1: + raise + + +class Test(utils.Base): + + def test_responding (self): + exp1 = self.exp_sdk.start(**self.consumer_credentials) + exp2 = self.exp_sdk.start(**self.consumer_credentials) + + channel1 = exp1.get_channel('test_channel_2', consumer=True) + channel2 = exp2.get_channel('test_channel_2', consumer=True) + + self.listener = channel1.listen('test_message_2') + + threading.Thread(target=lambda: self.responder()).start() + time.sleep(.5) + response = channel1.broadcast('test_message_2', { 'a': 1 }) + if response[0]['b'] != 2: + raise Exception + + def responder (self): + broadcast = self.listener.wait(60) + if broadcast.payload['a'] == 1: + broadcast.respond({ 'b': 2 }) diff --git a/tests/test_start.py b/tests/test_start.py new file mode 100644 index 0000000..073040d --- /dev/null +++ b/tests/test_start.py @@ -0,0 +1,101 @@ +import unittest +import requests +import time + +from . import utils + +from exp import exceptions + +class StartFailBase (object): + + def test_start (self): + try: + self.exp_sdk.start(**self.credentials) + except self.exp_sdk.RuntimeError: + pass + else: + raise Exception + +class StartSuccessBase (object): + + def test_start (self): + self.exp_sdk.start(**self.credentials) + + +class DeviceStartBase (utils.Base): + + def setUp (self): + super(DeviceStartBase, self).setUp() + self.credentials = self.device_credentials + +class UserStartBase (utils.Base): + + def setUp(self): + super(UserStartBase, self).setUp() + self.credentials = self.user_credentials + +class ConsumerStartBase (utils.Base): + + def setUp(self): + super(ConsumerStartBase, self).setUp() + self.credentials = self.consumer_credentials + + +class TestNoDeviceUuid (StartFailBase, DeviceStartBase, unittest.TestCase): + + def setUp(self): + super(TestNoDeviceUuid, self).setUp() + self.credentials['uuid'] = None + + + +class TestNoDeviceSecret (StartFailBase, DeviceStartBase, unittest.TestCase): + + def setUp(self): + super(TestNoDeviceSecret, self).setUp() + self.credentials['secret'] = None + + + +class TestAllowPairing (StartSuccessBase, DeviceStartBase, unittest.TestCase): + + def setUp(self): + super(TestAllowPairing, self).setUp() + self.credentials['uuid'] = None + self.credentials['allow_pairing'] = True + self.credentials['secret'] = None + + +class TestNoUsername (StartFailBase, UserStartBase, unittest.TestCase): + + def setUp(self): + super(TestNoUsername, self).setUp() + self.credentials['username'] = None + + +class TestNoPassword (StartFailBase, UserStartBase, unittest.TestCase): + + def setUp(self): + super(TestNoPassword, self).setUp() + self.credentials['password'] = None + + +class TestNoOrganization (StartFailBase, UserStartBase, unittest.TestCase): + + def setUp(self): + super(TestNoOrganization, self).setUp() + self.credentials['username'] = None + + +class TestNoConsumerUuid (StartFailBase, ConsumerStartBase, unittest.TestCase): + + def setUp(self): + super(TestNoConsumerUuid, self).setUp() + self.credentials['uuid'] = None + + +class TestNoConsumerApiKey (StartFailBase, ConsumerStartBase, unittest.TestCase): + + def setUp(self): + super(TestNoConsumerApiKey, self).setUp() + self.credentials['api_key'] = None \ No newline at end of file diff --git a/tests/test_stop.py b/tests/test_stop.py new file mode 100644 index 0000000..0452750 --- /dev/null +++ b/tests/test_stop.py @@ -0,0 +1,29 @@ + +import unittest + +import utils + +class Test(utils.Base, unittest.TestCase): + def test_stop_all (self): + exp1 = self.exp_sdk.start(**self.consumer_credentials) + exp2 = self.exp_sdk.start(**self.consumer_credentials) + self.exp_sdk.stop() + try: + exp1.get_auth() + except self.exp_sdk.RuntimeError: + pass + else: + raise Error + + def test_stop (self): + exp1 = self.exp_sdk.start(**self.consumer_credentials) + exp2 = self.exp_sdk.start(**self.consumer_credentials) + exp1.stop() + try: + exp1.get_auth() + except self.exp_sdk.RuntimeError: + pass + else: + raise Error + exp2.get_auth() + diff --git a/tests/utils.py b/tests/utils.py new file mode 100644 index 0000000..49009f6 --- /dev/null +++ b/tests/utils.py @@ -0,0 +1,37 @@ +import exp as exp_sdk + + + +class Base (object): + + exp_sdk = exp_sdk + + def setUp(self): + self.device_credentials = { 'uuid': 'test-uuid', 'secret': 'test-secret', 'host': 'http://localhost:9000/' } + self.user_credentials = { 'username': 'test@goexp.io', 'password': 'test-Password1', 'organization': 'scala', 'host': 'http://localhost:9000/' } + self.consumer_credentials = { 'uuid': 'test-uuid', 'api_key': 'test-api-key', 'host': 'http://localhost:9000/' } + + def tearDown (self): + self.exp_sdk.stop() + + +class Device (Base): + + def setUp (self): + super(Device, self).setUp() + self.exp = exp_sdk.start(**self.device_credentials) + + +class User (Base): + + def setUp (self): + super(User, self).setUp() + self.exp = exp_sdk.start(**self.user_credentials) + + +class Consumer (Base): + + def setUp (self): + super(Consumer, self).setUp() + self.exp = exp_sdk.start(**self.consumer_credentials) + From 2d10f51b13c2b47d928319ceb1897a71017e3fb6 Mon Sep 17 00:00:00 2001 From: James Dalessio Date: Thu, 24 Mar 2016 23:38:03 -0400 Subject: [PATCH 036/104] Remove cruft. --- tests/api_integration_test.py | 185 ------------------------------ tests/device_unit_test.py | 66 ----------- tests/network_integration_test.py | 77 ------------- tests/runtime_unit_test.py | 142 ----------------------- tests/test_network.py | 8 +- 5 files changed, 6 insertions(+), 472 deletions(-) delete mode 100644 tests/api_integration_test.py delete mode 100644 tests/device_unit_test.py delete mode 100644 tests/network_integration_test.py delete mode 100644 tests/runtime_unit_test.py diff --git a/tests/api_integration_test.py b/tests/api_integration_test.py deleted file mode 100644 index 6ac2e21..0000000 --- a/tests/api_integration_test.py +++ /dev/null @@ -1,185 +0,0 @@ -import unittest -import exp -import os -from exp.network import network -from exp.runtime import runtime -import time - -import random -import string - -user_credentials = {} -user_credentials['username'] = os.environ['EXP_USERNAME'] -user_credentials['password'] = os.environ['EXP_PASSWORD'] -user_credentials['organization'] = os.environ['EXP_ORGANIZATION'] -user_credentials['host'] = os.environ['EXP_HOST'] -user_credentials['enable_network'] = False - - - -device_credentials = {} -device_credentials['uuid'] = os.environ['EXP_DEVICE_UUID'] -device_credentials['secret'] = os.environ['EXP_SECRET'] -device_credentials['host'] = os.environ['EXP_HOST'] -device_credentials['enable_network'] = False - - - - -class AutoReloader (object): - - def setUp (self): - exp.start(**self.credentials) - - def tearDown (self): - pass#time.sleep(1) - - - -class UserAuthenticator (AutoReloader): - - credentials = device_credentials # Temp because stormpath is slow. - - -def random_word (): - return ''.join(random.choice(string.lowercase) for i in range(20)) - - - -class Devices (object): - - - def test_create (self): - device = exp.create_device({ 'name': random_word() }) - device.delete() - - def test_get(self): - device = exp.create_device({ 'name': random_word() }) - exp.get_device(device.document['uuid']) - device.delete() - - def test_save (self): - device = exp.create_device({ 'name': random_word() }) - device.save() - device.delete() - - def test_identify (self): - device = exp.create_device({ 'name': random_word() }) - device.identify() - - def test_find (self): - devices = exp.find_devices() - - -class Experiences (object): - - def test_create (self): - experience = exp.create_experience({ 'name': random_word() }) - experience.delete() - - def test_get(self): - experience = exp.create_experience({ 'name': random_word() }) - exp.get_experience(experience.document['uuid']) - experience.delete() - - def test_save (self): - experience = exp.create_experience({ 'name': random_word() }) - experience.save() - experience.delete() - - def test_find (self): - experiences = exp.find_experiences() - - - -class Feeds (object): - - @unittest.skip - def test_create (self): - feed = exp.create_feed({ 'name': random_word() }) - feed.delete() - - @unittest.skip - def test_get(self): - feed = exp.create_feed({ 'name': random_word() }) - exp.get_feed(feed.document['uuid']) - feed.delete() - - @unittest.skip - def test_save (self): - feed = exp.create_feed({ 'name': random_word() }) - feed.save() - feed.delete() - - def test_find (self): - feeds = exp.find_feeds() - - - -class Locations (object): - - def test_create (self): - location = exp.create_location({ 'name': random_word() }) - location.delete() - - def test_get(self): - location = exp.create_location({ 'name': random_word() }) - exp.get_location(location.document['uuid']) - location.delete() - - def test_save (self): - location = exp.create_location({ 'name': random_word() }) - location.save() - location.delete() - - def test_find (self): - locations = exp.find_locations() - - - -class Things (object): - - @unittest.skip - def test_create (self): - thing = exp.create_thing({ 'name': random_word() }) - thing.delete() - - @unittest.skip - def test_get(self): - thing = exp.create_thing({ 'name': random_word() }) - exp.get_thing(thing.document['uuid']) - thing.delete() - - @unittest.skip - def test_save (self): - thing = exp.create_thing({ 'name': random_word() }) - thing.save() - thing.delete() - - def test_find (self): - things = exp.find_things() - - - -class Content (object): - pass - - - -class Data (object): - pass - - - -class Zones (object): - pass - - -class TestUserDevices (Devices, UserAuthenticator, AutoReloader): pass -class TestUserExperiences (Experiences, UserAuthenticator, AutoReloader): pass -class TestUserLocations (Locations, UserAuthenticator, AutoReloader): pass -class TestUserFeeds (Feeds, UserAuthenticator, AutoReloader): pass -class TestUserThings (Things, UserAuthenticator, AutoReloader): pass -class TestUserContent (Content, UserAuthenticator, AutoReloader): pass -class TestUserData (Data, UserAuthenticator, AutoReloader): pass -class TestUserZones (Zones, UserAuthenticator, AutoReloader): pass \ No newline at end of file diff --git a/tests/device_unit_test.py b/tests/device_unit_test.py deleted file mode 100644 index 66bdbc9..0000000 --- a/tests/device_unit_test.py +++ /dev/null @@ -1,66 +0,0 @@ - -import unittest - - -from exp.api import Device, QueryResult -import exp.api_utils - -class TestException (Exception): pass - - -class TestDeviceFind (unittest.TestCase): - - mock = { - 'total': 2, - 'results': [ - { 'name': 'device 1', 'uuid': 'fake uuid 1' }, - { 'name': 'device 2', 'uuid': 'fake uuid 2' } - ] - } - - def api_utils_get_mock (self, path, params=None): - if path != '/api/devices': - raise Exception() - return self.mock - - def raise_exception (self): - raise TestException() - - def tearDown (self): - exp.api_utils.get = self.original_api_utils_get - - def setUp (self): - self.original_api_utils_get = exp.api_utils.get - - def test_error_passthrough (self): - exp.api_utils.get = lambda *args, **kwargs: self.raise_exception() - try: - exp.find_devices() - except TestException: - pass - else: - raise Exception() - - def test (self): - exp.api_utils.get = lambda *args, **kwargs: self.api_utils_get_mock(*args, **kwargs) - devices = exp.find_devices() - for device in devices: - if not isinstance(device, Device): - raise Exception - - def test_results_length (self): - devices = exp.find_devices() - if len(devices) != - pass - - def test_results_type (self): - pass - - def test_results_document (self): - pass - - - - - - diff --git a/tests/network_integration_test.py b/tests/network_integration_test.py deleted file mode 100644 index 9432282..0000000 --- a/tests/network_integration_test.py +++ /dev/null @@ -1,77 +0,0 @@ -import unittest -import exp -import os -from exp.network import network -from exp.runtime import runtime -import time - - -user_credentials = {} -user_credentials['username'] = os.environ['EXP_USERNAME'] -user_credentials['password'] = os.environ['EXP_PASSWORD'] -user_credentials['organization'] = os.environ['EXP_ORGANIZATION'] -user_credentials['host'] = os.environ['EXP_HOST'] - - - - -class AutoReloader (object): - - def setUp (self): - exp.start(**self.credentials) - - def tearDown (self): - pass#time.sleep(1) - - - -class UserAuthenticator (AutoReloader): - - credentials = user_credentials - - - - -class Broadcasting (object): - - def test_broadcasting(self): - channel = exp.get_channel('test1') - channel.broadcast('test2') - channel.broadcast('test2', None) - channel.broadcast('test2', payload=None) - channel.broadcast('test2', timeout=1) - -class Responding (object): - pass - -class Listening (object): - - def test_listening (self): - channel = exp.get_channel('test3') - listener = channel.listen('test4') - channel.broadcast('test4') - broadcast = listener.wait(3) - if not broadcast: - raise Exception() - - -class Cancelling (object): - - def test_listener_cancel (self): - channel = exp.get_channel('test51') - listener = channel.listen('test4') - listener.cancel() - channel.broadcast('test4') - broadcast = listener.wait(1) - if broadcast: - raise Exception() - - - - -class TestUserBroadcasting (Broadcasting, UserAuthenticator, unittest.TestCase): pass -class TestUserListening (Listening, UserAuthenticator, unittest.TestCase): pass -class TestUserCancelling (Cancelling, UserAuthenticator, unittest.TestCase): pass - - - diff --git a/tests/runtime_unit_test.py b/tests/runtime_unit_test.py deleted file mode 100644 index b4109d5..0000000 --- a/tests/runtime_unit_test.py +++ /dev/null @@ -1,142 +0,0 @@ -import unittest -import exp -from exp.network import network -from exp.runtime import runtime -from exp.authenticator import authenticator - -def noop(*args, **kwargs): - pass - -original_network_configure = network.configure -original_authenticator_configure = authenticator.configure -original_authenticator_get_auth = authenticator.get_auth - -class Base(object): - - type_ = None - - username = None - password = None - organization = None - - uuid = None - secret = None - api_key = None - - allow_pairing = None - enable_network = True - - def setUp (self): - runtime.is_started = False - self.stub() - - def tearDown (self): - runtime.is_started = False - self.unstub() - - def stub (self): - network.configure = noop - authenticator.configure = noop - authenticator.get_auth = noop - - def unstub (self): - network.configure = original_network_configure - authenticator.configure = original_authenticator_configure - authenticator.get_auth = original_authenticator_get_auth - - def start (self): - return exp.start(type=self.type_, - username=self.username, password=self.password, organization=self.organization, - uuid=self.uuid, secret=self.secret, api_key=self.api_key, - allow_pairing=self.allow_pairing, enable_network=self.enable_network) - - -class ErrorBase(Base): - - exception = exp.RuntimeError - - def test (self): - self.assertRaises(self.exception, lambda: self.start()) - - -class NoOptions (ErrorBase, unittest.TestCase): - pass - - -class NoUsername (ErrorBase, unittest.TestCase): - password = '_' - organization = '_' - - -class NoPassword (ErrorBase, unittest.TestCase): - username = '_' - organization = '_' - - -class NoOrganization (ErrorBase, unittest.TestCase): - username = '_' - password = '_' - - -class NoDeviceUuid (ErrorBase, unittest.TestCase): - secret = '_' - - -class NoConsumerAppUuid (ErrorBase, unittest.TestCase): - api_key = '_' - - -class NoDeviceSecret (ErrorBase, unittest.TestCase): - type_ = 'device' - uuid = '_' - -class NoConsumerAppApiKey (ErrorBase, unittest.TestCase): - type_ = 'consumer_app' - uuid = '_' - -class NoSecretOrApiKey (ErrorBase, unittest.TestCase): - uuid = '_' - - -class SuccessBase (Base): - - def test (self): - self.start() - - -class UserCredentials (SuccessBase, unittest.TestCase): - username = '_' - password = '_' - organization = '_' - -class UserTypedCredentials (SuccessBase, unittest.TestCase): - type_ = 'user' - username = '_' - password = '_' - organization = '_' - -class DeviceCredentials (SuccessBase, unittest.TestCase): - uuid = '_' - secret = '_' - -class DeviceTypedCredentials (SuccessBase, unittest.TestCase): - type_ = 'device' - uuid = '_' - secret = '_' - -class PairingCredentials (SuccessBase, unittest.TestCase): - allow_pairing = True - -class ConsumerAppCredentials (SuccessBase, unittest.TestCase): - uuid = '_' - api_key = '_' - -class ConsumerAppTypedCredentials (SuccessBase, unittest.TestCase): - type_ = 'consumer_app' - uuid = '_' - api_key = '_' - - - -if __name__ == '__main__': - unittest.main() diff --git a/tests/test_network.py b/tests/test_network.py index fde43bf..ad11eba 100644 --- a/tests/test_network.py +++ b/tests/test_network.py @@ -4,7 +4,7 @@ import threading import time -class Test (utils.Device): +class Test1 (utils.Device): def test_simple_message_pattern (self): channel = self.exp.get_channel('test_channel') @@ -15,7 +15,7 @@ def test_simple_message_pattern (self): raise -class Test(utils.Base): +class Test2 (utils.Base): def test_responding (self): exp1 = self.exp_sdk.start(**self.consumer_credentials) @@ -36,3 +36,7 @@ def responder (self): broadcast = self.listener.wait(60) if broadcast.payload['a'] == 1: broadcast.respond({ 'b': 2 }) + + + def test_listener_cancelling (self): + pass # TODO \ No newline at end of file From 17ddcdb9e130a0c7122ef4d875eb180bbd4c1128 Mon Sep 17 00:00:00 2001 From: James Dalessio Date: Sat, 26 Mar 2016 00:01:17 -0400 Subject: [PATCH 037/104] Working on docs and tests. --- tests/test_devices.py | 115 ++++++++++++++++++++++++++++++++++++++++++ tests/test_network.py | 8 ++- tests/utils.py | 8 ++- 3 files changed, 129 insertions(+), 2 deletions(-) create mode 100644 tests/test_devices.py diff --git a/tests/test_devices.py b/tests/test_devices.py new file mode 100644 index 0000000..35047a3 --- /dev/null +++ b/tests/test_devices.py @@ -0,0 +1,115 @@ + +import unittest + +from . import utils + + +class TestDevicesGet(utils.Device, unittest.TestCase): + + def test_get (self): + if not isinstance(self.exp.get_device(self.device_credentials['uuid']), self.exp._sdk.api.Device): + raise Exception + + def test_get_with_invalid_uuid (self): + if self.exp.get_device('not a device uuid') is not None: + raise Exception + + def test_get_none (self): + if self.exp.get_device(None) is not None: + raise Exception + + def test_get_empty (self): + if self.exp.get_device() is not None: + raise Exception + + def test_get_wrong_type (self): + if self.exp.get_device({}) is not None: + raise Exception + + +class TestDevicesFind(utils.Device, unittest.TestCase): + + def test_find_without_params (self): + devices = self.exp.find_devices() + for device in devices: + if not isinstance(device, self.exp._sdk.api.Device): + raise Exception + + def test_find_with_params (self): + devices = self.exp.find_devices({ 'uuid': self.device_credentials['uuid'] }) + if len(devices) != 1: + raise Exception + if not isinstance(devices[0], self.exp._sdk.api.Device): + raise Exception + + def test_find_with_wrong_type (self): + devices = self.exp.find_devices('test') + if not isinstance(devices, list) or devices: + raise Exception + + +class TestCreate(utils.Device, unittest.TestCase): + + def test_create_with_subtype (self): + device = self.exp.create_device({ 'subtype' : 'scala:device:player' }) + if not isinstance(device, self.exp._sdk.api.Device): + raise Exception + if not self.exp.get_device(device.document['uuid']): + raise Exception + + def test_create_empty (self): + device = self.exp.create_device() + if not isinstance(device, self.exp._sdk.api.Device): + raise Exception + if not self.exp.get_device(device.document['uuid']): + raise Exception + + def test_create_none (self): + device = self.exp.create_device(None) + if not isinstance(device, self.exp._sdk.api.Device): + raise Exception + if not self.exp.get_device(device.document['uuid']): + raise Exception + + +class TestSave(utils.Device, unittest.TestCase): + + def test_save (self): + name = self.generate_name() + device1 = self.exp.create_device() + device1.name = name + device1.save() + device2 = self.exp.get_device(device1.uuid) + if device2.name != name: + raise Exception + +class TestRefresh (utils.Device, unittest.TestCase): + + def test_refresh (self): + name = self.generate_name() + device1 = self.exp.create_device() + device2 = self.exp.get_device(device1.uuid) + device2.name = name + device2.save() + device1.refresh() + if device1.name != name: + raise Exception + + +class TestGettersAndSetters (utils.Device, unittest.TestCase): + + def test_uuid_getter (self): + device = self.exp.create_device() + if device.uuid is None: + raise Exception + + def test_name_getter_setter (self): + name = self.generate_name() + device = self.exp.create_device() + device.name = name + if device.name != name: + raise Exception + if self.document['name'] != name: + raise Exception + + diff --git a/tests/test_network.py b/tests/test_network.py index ad11eba..0f7d806 100644 --- a/tests/test_network.py +++ b/tests/test_network.py @@ -39,4 +39,10 @@ def responder (self): def test_listener_cancelling (self): - pass # TODO \ No newline at end of file + exp = self.exp_sdk.start(**self.consumer_credentials) + channel = exp.get_channel('test_channel_3', consumer=True) + listener = channel.listen('test_message_3') + listener.cancel() + channel.broadcast('test_message_3') + if listener.wait(.1): + raise Exception \ No newline at end of file diff --git a/tests/utils.py b/tests/utils.py index 49009f6..4f17e2d 100644 --- a/tests/utils.py +++ b/tests/utils.py @@ -1,5 +1,6 @@ import exp as exp_sdk - +import string +import random class Base (object): @@ -14,6 +15,11 @@ def setUp(self): def tearDown (self): self.exp_sdk.stop() + @staticmethod + def generate_name(): + return ''.join(random.choice(string.ascii_lowercase + string.digits) for _ in range(16)) + + class Device (Base): From 6fccfcbe823cd7c4ce6be58d9d655a87c462a7d7 Mon Sep 17 00:00:00 2001 From: James Dalessio Date: Sat, 26 Mar 2016 00:02:14 -0400 Subject: [PATCH 038/104] Commit missing files. --- README.md | 51 ++++++++-- exp/api.py | 246 +++++++++++++++++++++++++++++++++++++++++++++- exp/api_old.py | 203 -------------------------------------- exp/exceptions.py | 9 +- exp/exp.py | 20 ++-- 5 files changed, 301 insertions(+), 228 deletions(-) delete mode 100644 exp/api_old.py diff --git a/README.md b/README.md index 44873a5..16dfa79 100644 --- a/README.md +++ b/README.md @@ -70,24 +70,55 @@ enable_network | ```True``` | Whether to enable real time network communication. ## Listeners ## API +Allows access to raw authenticated API calls. `params` is a dictionary of url params, `payload` is a JSON serializable type, and `timeout` is the duration, in seconds, to wait for the request to complete. + | Description --- | --- - `exp.get(path, params=None, timeout=None)` | - `exp.post(path, payload=None, params=None, timeout=None)` | - `exp.patch(path, payload=None, params=None, timeout=None)` | - `exp.delete(path, payhload=None, params=None, timeout=None)` | + `exp.get(path, params=None, timeout=10)` | Send a GET request. + `exp.post(path, payload=None, params=None, timeout=10)` | Send a POST request. + `exp.patch(path, payload=None, params=None, timeout=10)` | Send a PATCH request. + `exp.put(path, payload=None, params=None, timeout=10)` | Send a PUT request. + `exp.delete(path, payhload=None, params=None, timeout=10)` | Send a DELETE request. ## Devices -- ```device = exp.get_device(uuid)```: Retrieves a device by uuid. -- ```device = exp.create_device(document)```: Creates a device from a dictionary. -- ```devices = exp.find_devices(params)```: Retrieves a list of devices given a dictionary of query parameters. See the API docs. + +### `exp.get_device(uuid=None)` +Returns the device with the given uuid or `None`. + +```python + +device1 = exp.get_device('[matching uuid]') +device2 = exp.get_device() # device2=None +device3 = exp.get_device('[unmatched uuid]') # device3=None + +``` + +### `exp.create_device(document=None)` + +Creates a device. + +```python + +device1 = exp.create_device() +device2 = exp.create_device({ 'name': 'my-new-device' }) + +``` + + + + +`exp.find_devices(params=None)` Returns a list of devices matching the given query parameters. `params` is a dictionary of query parameters. + + - ```device.uuid```: The device's uuid. -- ```device.document```: The device's underlying document, a dictionary. -- ```device.save()```: Saves the device to EXP. -- ```device.get_channel(system=False, consumer=False)```: Retrieves a [channel](#Channels) for communication about the device. +- ```device.name```: The device's name. +- ```device.document```: The device's underlying document. +- ```device.save()```: Saves the device. +- ```device.refresh()```: Refreshes the device's document in place. +- ```device.get_channel(system=False, consumer=False)```: Returns the [channel](#Channels) for this `device`. ## Things - ```thing = exp.get_thing(uuid)```: Retrieves a thing by uuid. diff --git a/exp/api.py b/exp/api.py index 21c25b0..423e281 100644 --- a/exp/api.py +++ b/exp/api.py @@ -2,10 +2,252 @@ import requests import traceback +class Resource (object): + + _collection_path = None + + def __init__ (self, document, sdk): + self.document = document + self._sdk = sdk + + @classmethod + def _get_resource_path_static (cls, uuid): + return cls._collection_path + '/' + uuid + + def _get_resource_path (self): + return self._get_resource_path_static(self.document['uuid']) + + def _get_channel_name (self): + return self.document['uuid'] + + @classmethod + def get (cls, uuid, sdk): + if not isinstance(uuid, basestring): + return None + try: + document = sdk.api.get(cls._get_resource_path_static(uuid)) + except sdk.exceptions.ApiError as exception: + if exception.status_code == 404: + return None + raise + return cls(document, sdk) + + @classmethod + def create (cls, document, sdk): + return cls(sdk.api.post(cls._collection_path, document), sdk) + + @classmethod + def find (cls, params, sdk): + if params and not isinstance(params, dict): + return [] + return [cls(document, sdk) for document in sdk.api.get(cls._collection_path, params)['results']] + + def save (self): + self.document = self._sdk.api.patch(self._get_resource_path(), self.document) + + def refresh (self): + self.document = self._sdk.api.get(self._get_resource_path()) + + def get_channel(self, **kwargs): + return self._sdk.network.get_channel(self._get_channel_name(), **kwargs) + +class UuidMixin (object): + + @property + def uuid (self): + return self.document.get('uuid') + + @property + def name (self): + return self.document.get('name') + + @name.setter + def name (self, value): + self.document['name'] = value + + +class DeviceThingBase(Resource): + + def get_location (self): + uuid = self.document.get('location', {}).get('uuid') + if not uuid: + return None + return self._sdk.api.Location.get(uuid, self._sdk) + + def get_zones (self): + location = self.get_location() + if not location: + return [] + keys = [document['key'] for document in self.document.get('location', {}).get('zones', [])] + zones = [self._sdk.api.Zone(document, self, self._sdk) for document in location.document['zones'] if document.key in keys] + + +class Device (DeviceThingBase, UuidMixin): + + _collection_path = '/api/devices' + + def get_experience (self): + uuid = self.document.get('experience', {}).get('uuid') + if not uuid: + return None + return self._sdk.api.Experience.get(uuid, self._sdk) + + def identify (self): + return self.get_channel().broadcast('identify') + + +class Thing (DeviceThingBase): + + _collection_path = '/api/things' + + +class Experience (Resource): + + _collection_path = '/api/experiences' + + def get_devices (self): + return self._sdk.api.Device.find({ 'experience.uuid' : self.document.get('uuid') }) + + +class Location (Resource): + + _collection_path = '/api/locations' + + def get_devices (self): + return self._sdk.api.Device.find({ 'location.uuid': self.document.get('uuid') }) + + def get_things (self): + return self._sdk.api.Thing.find({ 'location.uuid': self.document.get('uuid') }) + + def get_zones (self): + return [self._sdk.api.Zone(document, self, self._sdk) for document in self.document.get('zones', [])] + + def get_layout_url (self): + return self._get_resource_path(self.document, self._sdk) + '/layout?_rt=' + self._sdk.authenticator.get_auth()['restrictedToken'] + + +class Zone (Resource): + + def __init__ (self, document, location, sdk): + super(Zone, self).__init__(document, sdk) + self._location = location + + def save (self): + return self._location.save() + + def refresh (self): + self._location = self._location.refresh() + self._document = [document for document in self._location.document.get('zones') if document['key'] == self.document['key']] + + def get_location (self): + return self._location + + def get_devices (self): + return self._sdk.api.Device.find({ 'location.uuid': self._location.document['uuid'], 'location.zones.key': self.document['key'] }) + + def get_things (self): + return self._sdk.api.Thing.find({ 'location.uuid': self._location.document['uuid'], 'location.zones.key': self.document['key'] }) + + @classmethod + def get_channel_name(cls, document, sdk): + return self._location.document['uuid'] + ':zone:' + self.document['key'] + + +class Feed (Resource): + + _collection_path = '/api/connectors/feeds' + + def get_data (self): + return self._sdk.api.get(self._get_resource_path(self.document, self._sdk) + '/data') + + +class Data (Resource): + + _collection_path = '/api/data' + + @classmethod + def _get_resource_path(cls, document, sdk): + return cls._collection_path + '/' + urllib.quote(document['group']) + '/' + urllib.quote(document['key']) + + @classmethod + def get (cls, document, sdk): + document['group'] = document.get('group', 'default') + document_ = sdk.api.get(self._get_resource_path(document, sdk)) + if not document_: + document_ = { 'key': document['key'], 'group': document['group'], 'value': None } + return cls(document, sdk) + + @property + def value (self): + return self.document.get('value', None) + + @value.setter + def value(self, value): + self.document['value'] = value + + @classmethod + def create (cls, document, sdk): + data = cls(document, sdk) + data.save() + return data + + def save (self): + self.document = self._sdk.api.put(self._get_resource_path(self.document, self._sdk), self.document) + + def _get_channel_name (self): + return 'data' + ':' + self.document['key'] + ':' + self.document['group'] + + +class Content (Resource): + + _collection_path = '/api/content' + + def get_children (self): + if self._children is None: + if self.document['children']: + return [self.__class__(document, self._sdk) for document in self.document['children']] + else: + self.refresh() + return self.get_children() + return [] + + def get_subtype (self): + return self.document['subtype'] + + def get_url (self): + auth = self._sdk.authenticator.get_auth() + delivery_url = auth['api']['host'] + '/api/delivery' + rt_string = '?_rt=' + auth['restrictedToken'] + if self.get_subtype() == 'scala:content:file': + return delivery_url + self._get_resource_path(self.document, self._sdk) + rt_string + elif self.get_subtype() == 'scala:content:app': + return delivery_url + self._get_resource_path(self.document, self._sdk) + rt_string + elif self.get_subtype() == 'scala:content:url': + return self.document['url'] + + def get_variant_url (self, name): + auth = self._sdk.authenticator.get_auth() + delivery_url = auth['api']['host'] + '/api/delivery' + rt_string = '?_rt=' + auth['restrictedToken'] + if self.get_subtype() == 'scala:content:file' and self.has_variant(name): + return delivery_url + '?variant=' + urllib.quote(name) + rt_string + + def has_variant (self, name): + return name in self.document.get('variants', []) + class Api (object): + Device = Device + Thing = Thing + Experience = Experience + Location = Location + Zone = Zone + Feed = Feed + Data = Data + Content = Content + def __init__(self, sdk): self._sdk = sdk @@ -24,7 +266,7 @@ def _on_error(self, exception): self._sdk.logger.debug('API call encountered an unexpected error: %s' % traceback.format_exc()) raise self._sdk.exceptions.UnexpectedError('API call encountered an unexpected error.') else: - raise self._sdk.exceptions.ApiError(payload) + raise self._sdk.exceptions.ApiError(code=payload.get('code'), message=payload.get('message'), status_code=exception.response.status_code) else: self._sdk.logger.warn('API call encountered an unexpected error.') self._sdk.logger.debug('API call encountered an unexpected error: %s' % traceback.format_exc()) @@ -34,8 +276,6 @@ def _on_error(self, exception): def get(self, path, params=None, timeout=10): try: response = requests.get(self._get_url(https://github.com/path), timeout=timeout, params=params, headers=self._get_headers()) - if response.status_code == 404: - return None response.raise_for_status() try: return response.json() diff --git a/exp/api_old.py b/exp/api_old.py deleted file mode 100644 index c36ccf6..0000000 --- a/exp/api_old.py +++ /dev/null @@ -1,203 +0,0 @@ - - - - - -class QueryResult (object): - - def __init__(self, results=None, total=None): - self.results = results - self.total = total - - def __iter__(self): - for result in self.results: - yield result - - def __str__(self): - return str([result for result in self.results]) - - def __contains__ (self, item): - return item in self.results - - def __len__ (self): - return len(self.results) - - def __getitem__ (self, index): - return self.results[index] - - - -class Resource (object): - - path = None - - def __init__ (self, document): - self.document = document - - @classmethod - def get_resource_path (cls, uuid): - return cls.path + '/' + uuid - - def get_path (self): - return self.get_resource_path(self.uuid) - - @property - def uuid (self): - return self.document.get('uuid') - - - @classmethod - def get (cls, uuid, *args, **kwargs): - return cls(api_utils.get(cls.get_resource_path(uuid), *args, **kwargs) - - @classmethod - def create (cls, document=None, **kwargs): - return cls(api_utils.post(cls.collection_path, payload=document or {}, **kwargs) - - @classmethod - def find (cls, params=None): - response = api_utils.get(cls.path, params) - response['results'] = [cls(document) for document in response['results']] - return QueryResult(**response) - - - - def delete (self, **kwargs): - return api_utils.delete(self.get_path(), **kwargs) - - def save (self, **kwargs): - self.document = api_utils.patch(self.get_path(), payload=self.document, **kwargs) - - def refresh (self): - self.document = api_utils.get(self.get_path()) - - - - - def get_channel (self, **kwargs): - return network.get_channel(self._get_channel_name(), **kwargs) - - def get_channel_name (self): - return self.uuid - - def fling (payload, **kwargs): - return self.get_channel().broadcast('fling', payload, **kwargs) - - -class StandardResource (Resource): - - - - - - -class Device (Resource): - - path = '/api/devices' - - def identify (self): - return self.get_channel().broadcast('identify', None, 500) - - -class Thing (Resource): - - path = '/api/things' - - -class Feed (Resource): - - path = '/api/connectors/feeds' - - def get_data (self): - return api_utils(self._gat_path() + '/data') - - -class Experience (Resource): - - path = '/api/experiences' - - -class Location (Resource): - - path = '/api/locations' - - def get_zones (self): - return [Zone(document, self) for document in self.document['zones']] - - def get_layout_url (self): - return self._get_path() + '/layout?_rt=' + authenticator.get_auth()['restrictedToken'] - - -class Zone (Resource): - - def __init__ (self, document, location): - self._location = location - super(Zone, self).__init__(document) - - def save (self): - return self._location.save() - - def _get_channel_name (self): - return self._location.uuid + ':zone:' + self.document['key'] - - -class Data (Resource): - - path = '/api/data' - - def get_path (self): - return self.__class__.path + '/' + urllib.quote(this.document['group']) + '/' + urllib.quote(this.document['key']) - - @classmethod - def get (cls, group, key): - resource = cls({ 'group': group, 'key': key }) - resource.refresh() - return resource - - @classmethod - def create (cls, group, key, value): - resource = cls({ 'group': group, 'key': key, 'value': value }) - resource.save() - return resource - - def get_channel_name (self): - return 'data' + ':' + this.document['key'] + ':' + this.document['group'] - - -class Content (Resource): - - path = '/api/content' - - @property - def children (self): - if self._children is None: - if self.document['children']: - return [self.__class__(document) for document in self.document['children']] - else: - self.refresh() - return self.children - return [] - - @property - def subtype (self): - return self.document['subtype'] - - def get_url (self): - auth = authenticator.get_auth() - delivery_url = auth['api']['host'] + '/api/delivery' - rt_string = '?_rt=' + auth['restrictedToken'] - if self.subtype == 'scala:content:file': - return delivery_url + this.document.path + rt_string - elif self.subtype == 'scala:content:app': - return delivery_url + this.document.path + rt_string - elif self.subtype == 'scala:content:url': - return self.document['url'] - - def get_variant_url (name): - auth = authenticator.get_auth() - delivery_url = auth['api']['host'] + '/api/delivery' - rt_string = '?_rt=' + auth['restrictedToken'] - if self.subtype == 'scala:content:file' and name in this.document['variants']: - return delivery_url + '?variant=' + urllib.quote(name) + rt_string - - diff --git a/exp/exceptions.py b/exp/exceptions.py index 0b1c52a..ba62fb8 100644 --- a/exp/exceptions.py +++ b/exp/exceptions.py @@ -36,11 +36,10 @@ def __str__ (self): class ApiError(ExpError): - def __init__(self, payload): - logger.debug('An api error has occured.') - logger.debug(traceback.format_exc()) - self.message = payload.get('message') - self.code = payload.get('code') + def __init__(self, code=None, message=None, status_code=None): + self.message = message or 'An unknown error has occurred.' + self.code = code or 'unknown.error' + self.status_code = status_code def __str__(self): return '%s: %s' % (self.code, self.message) diff --git a/exp/exp.py b/exp/exp.py index 3490bbf..7ea7824 100644 --- a/exp/exp.py +++ b/exp/exp.py @@ -72,6 +72,7 @@ def start (enable_network=True, host='https://api.goexp.io/', **options): sdk.network.start() exp = Exp(sdk) instances.append(exp) + exp.get_auth() return exp @@ -143,18 +144,23 @@ def get_channel(self, *args, **kwargs): return self._sdk.network.get_channel(*args, **kwargs) + """ API Resources """ + def get_device (self, uuid=None): + return api.Device.get(uuid, self._sdk) + + def find_devices (self, params=None): + return api.Device.find(params, self._sdk) + + def create_device (self, document=None): + return api.Device.create(document, self._sdk) -""" - # def get_device (*args, **kwargs): - # return api.Device.get(*args, **kwargs) - # def find_devices (*args, **kwargs): - # return api.Device.find(*args, **kwargs) - # def create_device (*args, **kwargs): - # return api.Device.create(*args, **kwargs) +""" + + # d # def get_thing (*args, **kwargs): From 32f644e07c2b21bcc4ebc92d4968ec05e842f52c Mon Sep 17 00:00:00 2001 From: James Dalessio Date: Sat, 26 Mar 2016 23:40:17 -0400 Subject: [PATCH 039/104] Updating readme and api sigs --- README.md | 452 +++++++++++++++++++++++++++++++++++++++++++++-------- exp/api.py | 10 +- 2 files changed, 391 insertions(+), 71 deletions(-) diff --git a/README.md b/README.md index 16dfa79..877cd33 100644 --- a/README.md +++ b/README.md @@ -39,51 +39,343 @@ enable_network | ```True``` | Whether to enable real time network communication. # Reference ## Runtime - | Description ---- | --- -`exp_sdk.start(**options)` | Returns an instance of the SDK. See [startup options](#startup-options). -`exp_sdk.stop()` | Stops all running instances of the SDK. -`exp.stop()` | Stops the SDK instance. -`exp.is_connected` | Whether or not there is an active connection to the EXP network. -`exp.get_auth()` | Returns authentication payload. +### exp_sdk.start(**options) -## Exceptions - | Description - --- | --- - `exp_sdk.ExpError` | Base class for all EXP exceptions. - `exp_sdk.UnexpectedError` | Raised when an unexpected error occurs. - `exp_sdk.RuntimeError` | Raised when [startup options](#startup-options) are incorrect or inconsistent. - `exp_sdk.AuthenticationError` | Raised when the sdk cannot authenticate due to bad credentials. - `exp_sdk.ApiError` | Raised when an API call fails. Has properties `message` and `code`. See the [API documentation](#https://docs.goexp.io). +Starts and returns an sdk instance. +```python -## Channels - | Description - --- | --- - `exp.getChannel(name, consumer=False, system=False)` | Returns a channel with the given name and channel flags. - `channel.broadcast(name, payload, timeout)` | Returns a broadcast](#broadcasts) object. - `broadcast.response` +import exp_sdk + +# Authenticating as a user. +exp = exp_sdk.start(username='joe@scala.com', password='joeIsAwes0me', organization='joeworld') + +# Authenticating as a device. +exp = exp_sdk.start(uuid='[uuid]', secret='[secret]') + +# Authenticating as a consumer app. +exp = exp_sdk.start(uuid='[uuid]', api_key='[api-key]') + +``` + +`start` can be called multiple times to start multiple independent instances of the sdk. The sdk can be started using user, device, or consumer app credentials. + +`**options` supports the following keyword arguments: + +Name | Default | Description +--- | --- | --- +username | `None` | The username used to log in to EXP. Required user credential. +password | `None` | The password of the user. Required user credential. +organization | `None` | The organization of the user. Required user credential +uuid | `None` | The device or consumer app uuid. Required consumer app credential and required device credential unless `allow_pairing` is `True`. +secret | `None` | The device secret. Required device credential unless `allow_pairing` is `True`. +api_key | `None` | The consumer app api key. Required consumer app credential. +allow_pairing | `False` | Whether to allow authentication to fallback to pairing mode. If `True`, invalid or empty device credentials will start the sdk in pairing mode. +host | `https://api.goexp.io` | The api host to authenticate with. +enable_network | `True` | Whether or not to establish a socket connection with the EXP network. If `False`, you will not be able to listen for broadcasts. + + +### exp_sdk.stop() + +Stops all running instances of the sdk, cancels all listeners and stops all network connections. + +```python + +exp_1 = exp_sdk.start(**options_1) +exp_2 = exp_sdk.start(**options_2) + +exp_sdk.stop() + +exp_2.create_device() # Exception. + +``` + +New instances can still be created by calling `start`. + +### exp.stop() + +Stops the sdk instance, cancels its listeners, and stops all network connections. + +```python + +exp = exp_sdk.start(**options) +exp.stop() +exp.get_auth() # Exception. + +``` + +Sdk instances cannot be restarted and any invokation on the instance will raise an exception. + + +### exp.is_connected + +Whether or not there is an active socket connection to the network. + +```python + +# Wait for a connection. +while not exp.is_connected: + time.sleep(1) + +``` + + +### exp.get_auth() + +Returns the up to date authentication payload. The authentication payload may be updated when invoking this method. + +```python + +print 'My authentication token is : %s' % exp.get_auth()['token'] + +``` + + +## Network + + +### exp.getChannel(name, consumer=False, system=False) + +Returns a the channel with the given name and flags. + +```python + +channel = exp.get_channel('my-consumer-channel', consumer=True) + +``` + + +### channel.broadcast(name, payload=None, timeout=0.1) + +Sends a broadcast on the channel with the given name and payload. Returns a list of responses. `timeout` is the number of seconds to hold the request open to wait for responses. + +```python + +channel = exp.get_channel('my-channel') +responses = channel.broadcast('hi!', { 'test': 'nice to meet you!' }) +[print response for response in responses] + +``` + + +### channel.listen(name, max_age=60) + +Returns a listener for events on the channel. `max_age` is the number of seconds the listener will keep events before they are discarded. + +```python + +channel = exp.get_channel('my-channel') +listener = channel.listen('my-event', max_age=30) + +``` + + +## listener.wait(timeout=0) + +Wait for `timeout` seconds for broadcasts. Returns a broadcast if a broadcast is in the queue or if a broadcast is received before the timeout. If timeout is reached, returns `None`. + +```python +channel = exp.get_channel('my-channel') +listener = channel.listen('my-event') + +while True: + broadcast = listener.wait(60) + if broadcast: + print 'I got a broadcast!' + +``` + + +## listener.cancel() + +Cancels the listener. The listener is unsubscribed from broadcasts and will no longer receive messages. This cannot be undone. + +```python + +listener.cancel() +broadcast = listener.wait(60) # Will always be None + +``` + + +## broadcast.payload + +The payload of the broadcast. Can be any JSON serializable type. + +## broadcast.respond(response) + +Respond to the broadcast with a JSON serializable response. + +```python + +channel = exp.get_channel('my-channel') +listener = channel.listen('my-event') + +while True: + broadcast = listener.wait(60) + if broadcast && broadcast.payload == 'hi!': + broadcast.respond('hi back at you!') + + +``` + + + + + +## HTTP Requests + +Send custom authenticated API calls. `params` is a dictionary of url params, `payload` is a JSON serializable type, and `timeout` is the duration, in seconds, to wait for the request to complete. `path` is relative to the api host root. All methods will return a JSON serializable type. + +### exp.get(path, params=None, timeout=10) + +Send a GET request. + +```python + +# Find devices by name. +result = exp.get('/api/devices', { 'name': 'my-name' }) + +``` + +### exp.post(path, payload=None, params=None, timeout=10) + +Send a POST request. + +```python + +# Create a new experience. +document = exp.post('/api/experiences', { 'apps': [] }) + +``` + + +### exp.patch(path, payload=None, params=None, timeout=10) + +Send a PATCH request. + +```python + +# Change the name of an experience. +document = exp.patch('/api/experiences/[uuid]', { 'name': 'new-name' }) + +``` + + +### exp.put(path, payload=None, params=None, timeout=10) + +Send a PUT request. + +```python + +# Insert a data value. +document = exp.put('/api/data/cats/fluffy', { 'eyes': 'blue'}) + +``` + + +### exp.delete(path, payload=None, params=None, timeout=10) + +Send a DELETE request. + +```python + +# Delete a location. +exp.delete('/api/location/[uuid]') + +``` + + + +## Common Resource Methods and Attributes + +### resource.uuid + +The uuid of the resource. Cannot be set. Maps to `resource.document['uuid']` + +### resource.name + +The name of the resource. Can be set directly. Maps to `resource.document['name']`. + +### resource.document + +The resource's underlying document + +### resource.save() + +Saves the resource and updates the document in place. + +```python + +device = exp.get_device('[uuid]') +device.name = 'my-new-name' +device.save() +# device changes are now saved + +``` + + +### resource.refresh() + +Refreshes the resource's underlying document in place. + +```python + +device = exp.create_device() +device.name = 'new-name' +device_2 = exp.get_device(device.uuid) +device.save() +device_2.refresh() +print device_2.name # 'new-name' + +``` + + + +### resource.get_channel(system=False, consumer=False) + +Returns the channel whose name is contextually associated with this resource. + +```python + +channel = experience.get_channel() +channel.broadcast('hello?') + +``` + +### resource.fling(payload) + +Fling an app launch payload on this resource's channel. + +```python +location = exp.get_location('[uuid]') +location.fling({ 'appTemplate' : { 'uuid': '[uuid'} }) + +``` + +See ??? for more information about app launch payloads. + +### resource.identify() + +Requests that devices listening for this event on this resource's channel visually identify themselves. Implementation is device specific; this is simply a convience method. + +```python +location = exp.get_location('[uuid]') +location.identify() # Tell all devices at this location to identify themselves! + +``` -## Broadcasts -## Listeners -## API -Allows access to raw authenticated API calls. `params` is a dictionary of url params, `payload` is a JSON serializable type, and `timeout` is the duration, in seconds, to wait for the request to complete. - | Description - --- | --- - `exp.get(path, params=None, timeout=10)` | Send a GET request. - `exp.post(path, payload=None, params=None, timeout=10)` | Send a POST request. - `exp.patch(path, payload=None, params=None, timeout=10)` | Send a PATCH request. - `exp.put(path, payload=None, params=None, timeout=10)` | Send a PUT request. - `exp.delete(path, payhload=None, params=None, timeout=10)` | Send a DELETE request. ## Devices +Devices inherit all [common resource methods and attributes](#common-resource-methods-and-attributes). + ### `exp.get_device(uuid=None)` Returns the device with the given uuid or `None`. @@ -91,14 +383,14 @@ Returns the device with the given uuid or `None`. ```python device1 = exp.get_device('[matching uuid]') -device2 = exp.get_device() # device2=None -device3 = exp.get_device('[unmatched uuid]') # device3=None +device2 = exp.get_device() # None +device3 = exp.get_device('[unmatched uuid]') # None ``` ### `exp.create_device(document=None)` -Creates a device. +Creates and returns a new device. ```python @@ -108,17 +400,66 @@ device2 = exp.create_device({ 'name': 'my-new-device' }) ``` +### `exp.find_devices(params=None)` +Returns a list of devices matching the given query parameters. `params` is a dictionary of query parameters. + +```python + +devices = exp.find_devices({ 'location.zone.key': 'my-zone-key' }) +[print device.name for device in devices] + +``` + + +### device.get_location() + +Returns the device's location or `None`. + +### device.get_experience() + +Returns the device's experience or `None` + + + + +## Things + +Devices inherit all [common resource methods and attributes](#common-resource-methods-and-attributes). + +### thing.get_location() + +Returns the device's location or `None`. + +### thing.get_experience() + +Returns the device's experience or `None` + + + + +### Experiences +### Locations +### Zones +### Feeds +### Data +### Content + + + + +## Exceptions + + | Description + --- | --- + `exp_sdk.ExpError` | Base class for all EXP exceptions. + `exp_sdk.UnexpectedError` | Raised when an unexpected error occurs. + `exp_sdk.RuntimeError` | Raised when [startup options](#startup-options) are incorrect or inconsistent. + `exp_sdk.AuthenticationError` | Raised when the sdk cannot authenticate due to bad credentials. + `exp_sdk.ApiError` | Raised when an API call fails. Has properties `message` and `code`. See the [API documentation](#https://docs.goexp.io). -`exp.find_devices(params=None)` Returns a list of devices matching the given query parameters. `params` is a dictionary of query parameters. -- ```device.uuid```: The device's uuid. -- ```device.name```: The device's name. -- ```device.document```: The device's underlying document. -- ```device.save()```: Saves the device. -- ```device.refresh()```: Refreshes the device's document in place. -- ```device.get_channel(system=False, consumer=False)```: Returns the [channel](#Channels) for this `device`. ## Things - ```thing = exp.get_thing(uuid)```: Retrieves a thing by uuid. @@ -179,33 +520,6 @@ device2 = exp.create_device({ 'name': 'my-new-device' }) - ```data.key```: The data's key. - ```data.group```: The data's group. -## Channels -- ```channel = exp.get_channel(name, system=False, consumer=False)```: Get a channel by name. -- ```responses = channel.broadcast(name, payload=None, timeout=0):``` Send a broadcast on this channel, with string ```name``` and JSON serializable type ```payload```. Wait for ```timeout``` seconds for responses. Returns a list of the responses. -- ```listener = channel.listen(name)```: Listen for messages with name ```name``` on the given channel. Returns a [Listener](#listeners). - - -## Listeners -- ```listener.cancel()```: Permanently detach the listener. -- ```broadcast = listener.wait(timeout=0)```: Block for broadcasts for ```timeout``` seconds. Returns a [broadcast](#broadcasts) or ```None```. - - -## Broadcasts -- ```broadcast.payload```: The payload of the broadcast. -- ```broadcast.respond(payload)```: Respond to the broadcast. ```payload``` must be a JSON serializable. - - - - -## Custom API Calls -The following methods make custom API calls that include authentication. Use for API calls that aren't supported by the SDK. ```params``` is specified as a dictionary of query params and ```payload``` must be a JSON serializable type. With the exception of DELETE, these requests will return the parsed JSON response. -- ```document = exp.get(path, params=None)``` -- ```document = exp.post(path, payload=None, params=None)``` -- ```document = exp.patch(path, payload=None, params=None)``` -- ```document = exp.put(path, payload=None, params=None)``` -- ```exp.delete(path, params)``` - - # Examples diff --git a/exp/api.py b/exp/api.py index 423e281..8ebb26e 100644 --- a/exp/api.py +++ b/exp/api.py @@ -51,6 +51,14 @@ def refresh (self): def get_channel(self, **kwargs): return self._sdk.network.get_channel(self._get_channel_name(), **kwargs) + def identify (self, *args, consumer=False, **kwargs): + return self.get_channel(consumer=consumer).identify(*args, **kwargs) + + def fling (self, *args, consumer=False, **kwargs): + return self.get_channel(consumer=consumer).fling(*args, **kwargs) + + + class UuidMixin (object): @property @@ -92,8 +100,6 @@ def get_experience (self): return None return self._sdk.api.Experience.get(uuid, self._sdk) - def identify (self): - return self.get_channel().broadcast('identify') class Thing (DeviceThingBase): From cb4f2e52205cc3c4b446c656c352f76ca34a52b0 Mon Sep 17 00:00:00 2001 From: James Dalessio Date: Sun, 27 Mar 2016 00:33:50 -0400 Subject: [PATCH 040/104] Unified standard tests. --- exp/api.py | 22 +++---- exp/exp.py | 46 ++++++++++---- tests/test_api.py | 5 -- tests/test_auth.py | 10 +-- tests/test_devices.py | 114 ++--------------------------------- tests/test_experiences.py | 11 ++++ tests/test_things.py | 14 +++++ tests/utils.py | 124 ++++++++++++++++++++++++++++++++++++++ 8 files changed, 202 insertions(+), 144 deletions(-) create mode 100644 tests/test_experiences.py create mode 100644 tests/test_things.py diff --git a/exp/api.py b/exp/api.py index 8ebb26e..66c3606 100644 --- a/exp/api.py +++ b/exp/api.py @@ -51,15 +51,15 @@ def refresh (self): def get_channel(self, **kwargs): return self._sdk.network.get_channel(self._get_channel_name(), **kwargs) - def identify (self, *args, consumer=False, **kwargs): - return self.get_channel(consumer=consumer).identify(*args, **kwargs) + def identify (self, *args, **kwargs): + return self.get_channel(consumer=kwargs.pop('consumer', False)).identify(*args, **kwargs) - def fling (self, *args, consumer=False, **kwargs): - return self.get_channel(consumer=consumer).fling(*args, **kwargs) + def fling (self, *args, **kwargs): + return self.get_channel(consumer=kwargs.pop('consumer', False)).fling(*args, **kwargs) -class UuidMixin (object): +class CommonResourceMixin (object): @property def uuid (self): @@ -90,7 +90,7 @@ def get_zones (self): zones = [self._sdk.api.Zone(document, self, self._sdk) for document in location.document['zones'] if document.key in keys] -class Device (DeviceThingBase, UuidMixin): +class Device (DeviceThingBase, CommonResourceMixin): _collection_path = '/api/devices' @@ -102,12 +102,12 @@ def get_experience (self): -class Thing (DeviceThingBase): +class Thing (DeviceThingBase, CommonResourceMixin): _collection_path = '/api/things' -class Experience (Resource): +class Experience (Resource, CommonResourceMixin): _collection_path = '/api/experiences' @@ -115,7 +115,7 @@ def get_devices (self): return self._sdk.api.Device.find({ 'experience.uuid' : self.document.get('uuid') }) -class Location (Resource): +class Location (Resource, CommonResourceMixin): _collection_path = '/api/locations' @@ -159,7 +159,7 @@ def get_channel_name(cls, document, sdk): return self._location.document['uuid'] + ':zone:' + self.document['key'] -class Feed (Resource): +class Feed (Resource, CommonResourceMixin): _collection_path = '/api/connectors/feeds' @@ -204,7 +204,7 @@ def _get_channel_name (self): return 'data' + ':' + self.document['key'] + ':' + self.document['group'] -class Content (Resource): +class Content (Resource, CommonResourceMixin): _collection_path = '/api/content' diff --git a/exp/exp.py b/exp/exp.py index 7ea7824..f6d3454 100644 --- a/exp/exp.py +++ b/exp/exp.py @@ -147,16 +147,47 @@ def get_channel(self, *args, **kwargs): """ API Resources """ def get_device (self, uuid=None): - return api.Device.get(uuid, self._sdk) + return self._sdk.api.Device.get(uuid, self._sdk) def find_devices (self, params=None): - return api.Device.find(params, self._sdk) + return self._sdk.api.Device.find(params, self._sdk) def create_device (self, document=None): - return api.Device.create(document, self._sdk) + return self._sdk.api.Device.create(document, self._sdk) + def get_thing (self, uuid=None): + return self._sdk.api.Thing.get(uuid, self._sdk) + + def find_things (self, params=None): + return self._sdk.api.Thing.find(params, self._sdk) + + def create_thing (self, document=None): + return self._sdk.api.Thing.create(document, self._sdk) + + + def get_experience (self, uuid=None): + return self._sdk.api.Experience.get(uuid, self._sdk) + + def find_experiences (self, params=None): + return self._sdk.api.Experience.find(params, self._sdk) + + def create_experience (self, document=None): + return self._sdk.api.Experience.create(document, self._sdk) + + + """ def get_experience (self, document=None): + return api.Experience.get(*args, **kwargs) + + # def find_experiences (*args, **kwargs): + # return api.Experience.find(*args, **kwargs) + + # def create_experience (*args, **kwargs): + # return api.Experience.create(*args, **kwargs) + +""" + """ @@ -173,14 +204,7 @@ def create_device (self, document=None): # return api.Thing.create(*args, **kwargs) - # def get_experience (*args, **kwargs): - # return api.Experience.get(*args, **kwargs) - - # def find_experiences (*args, **kwargs): - # return api.Experience.find(*args, **kwargs) - - # def create_experience (*args, **kwargs): - # return api.Experience.create(*args, **kwargs) + # def get_location (*args, **kwargs): diff --git a/tests/test_api.py b/tests/test_api.py index 2cd1a23..d2898e2 100644 --- a/tests/test_api.py +++ b/tests/test_api.py @@ -21,11 +21,6 @@ def test_delete (self): document = self.exp.post('/api/experiences', {}) self.exp.delete('/api/experiences/' + document['uuid']) - def test_get_none (self): - document = self.exp.get('/api/devices/adsdsadas') - if document: - raise Exception - def test_post_error (self): name = ''.join(random.choice(string.lowercase) for i in range(10)) self.exp.post('/api/experiences', { 'name': name }) diff --git a/tests/test_auth.py b/tests/test_auth.py index 5a35790..f396a66 100644 --- a/tests/test_auth.py +++ b/tests/test_auth.py @@ -39,15 +39,9 @@ class TestDevice401 (utils.Base, unittest.TestCase): def test_login_401 (self): self.device_credentials['uuid'] = 'wrong uuid' - self.exp = self.exp_sdk.start(**self.device_credentials) try: - self.exp.get_auth() + exp = self.exp_sdk.start(**self.device_credentials) except self.exp_sdk.AuthenticationError: - try: - self.exp.get_auth() - except self.exp_sdk.AuthenticationError: - pass - else: - raise Exception + pass else: raise Exception diff --git a/tests/test_devices.py b/tests/test_devices.py index 35047a3..ce19551 100644 --- a/tests/test_devices.py +++ b/tests/test_devices.py @@ -3,113 +3,9 @@ from . import utils +class Test(utils.Device, utils.ResourceBase): -class TestDevicesGet(utils.Device, unittest.TestCase): - - def test_get (self): - if not isinstance(self.exp.get_device(self.device_credentials['uuid']), self.exp._sdk.api.Device): - raise Exception - - def test_get_with_invalid_uuid (self): - if self.exp.get_device('not a device uuid') is not None: - raise Exception - - def test_get_none (self): - if self.exp.get_device(None) is not None: - raise Exception - - def test_get_empty (self): - if self.exp.get_device() is not None: - raise Exception - - def test_get_wrong_type (self): - if self.exp.get_device({}) is not None: - raise Exception - - -class TestDevicesFind(utils.Device, unittest.TestCase): - - def test_find_without_params (self): - devices = self.exp.find_devices() - for device in devices: - if not isinstance(device, self.exp._sdk.api.Device): - raise Exception - - def test_find_with_params (self): - devices = self.exp.find_devices({ 'uuid': self.device_credentials['uuid'] }) - if len(devices) != 1: - raise Exception - if not isinstance(devices[0], self.exp._sdk.api.Device): - raise Exception - - def test_find_with_wrong_type (self): - devices = self.exp.find_devices('test') - if not isinstance(devices, list) or devices: - raise Exception - - -class TestCreate(utils.Device, unittest.TestCase): - - def test_create_with_subtype (self): - device = self.exp.create_device({ 'subtype' : 'scala:device:player' }) - if not isinstance(device, self.exp._sdk.api.Device): - raise Exception - if not self.exp.get_device(device.document['uuid']): - raise Exception - - def test_create_empty (self): - device = self.exp.create_device() - if not isinstance(device, self.exp._sdk.api.Device): - raise Exception - if not self.exp.get_device(device.document['uuid']): - raise Exception - - def test_create_none (self): - device = self.exp.create_device(None) - if not isinstance(device, self.exp._sdk.api.Device): - raise Exception - if not self.exp.get_device(device.document['uuid']): - raise Exception - - -class TestSave(utils.Device, unittest.TestCase): - - def test_save (self): - name = self.generate_name() - device1 = self.exp.create_device() - device1.name = name - device1.save() - device2 = self.exp.get_device(device1.uuid) - if device2.name != name: - raise Exception - -class TestRefresh (utils.Device, unittest.TestCase): - - def test_refresh (self): - name = self.generate_name() - device1 = self.exp.create_device() - device2 = self.exp.get_device(device1.uuid) - device2.name = name - device2.save() - device1.refresh() - if device1.name != name: - raise Exception - - -class TestGettersAndSetters (utils.Device, unittest.TestCase): - - def test_uuid_getter (self): - device = self.exp.create_device() - if device.uuid is None: - raise Exception - - def test_name_getter_setter (self): - name = self.generate_name() - device = self.exp.create_device() - device.name = name - if device.name != name: - raise Exception - if self.document['name'] != name: - raise Exception - - + get_name = 'get_device' + find_name = 'find_devices' + create_name = 'create_device' + class_ = utils.api.Device diff --git a/tests/test_experiences.py b/tests/test_experiences.py new file mode 100644 index 0000000..217830d --- /dev/null +++ b/tests/test_experiences.py @@ -0,0 +1,11 @@ + +import unittest + +from . import utils + +class Test(utils.Device, utils.ResourceBase): + + get_name = 'get_experience' + find_name = 'find_experiences' + create_name = 'create_experience' + class_ = utils.api.Experience diff --git a/tests/test_things.py b/tests/test_things.py new file mode 100644 index 0000000..4b7b086 --- /dev/null +++ b/tests/test_things.py @@ -0,0 +1,14 @@ + +import unittest + +from . import utils + +class Test(utils.Device, utils.ResourceBase): + + get_name = 'get_thing' + find_name = 'find_things' + create_name = 'create_thing' + class_ = utils.api.Thing + + def generate_valid_document (self): + return { 'subtype': 'scala:thing:rfid', 'id': self.generate_name(), 'name': self.generate_name()} diff --git a/tests/utils.py b/tests/utils.py index 4f17e2d..4edb6fe 100644 --- a/tests/utils.py +++ b/tests/utils.py @@ -3,6 +3,9 @@ import random +from exp import api + + class Base (object): exp_sdk = exp_sdk @@ -41,3 +44,124 @@ def setUp (self): super(Consumer, self).setUp() self.exp = exp_sdk.start(**self.consumer_credentials) + + + + + +class ResourceBase (object): + + class_ = None + get_name = '' + create_name = '' + find_name = '' + + + def get (self, *args, **kwargs): + return getattr(self.exp, self.get_name)(*args, **kwargs) + + def create (self, *args, **kwargs): + return getattr(self.exp, self.create_name)(*args, **kwargs) + + def find (self, *args, **kwargs): + return getattr(self.exp, self.find_name)(*args, **kwargs) + + def create_valid (self): + return self.create(self.generate_valid_document()) + + def generate_valid_document (self): + return {} + + def assert_isinstance (self, resource): + if not isinstance(resource, self.class_): + raise Exception + + + def test_get (self): + resource_1 = self.create_valid() + resource_2 = self.get(resource_1.uuid) + self.assert_isinstance(resource_2) + + def test_get_with_invalid_uuid (self): + if self.get('invalid uuid') is not None: + raise Exception + + def test_get_none (self): + if self.get(None) is not None: + raise Exception + + def test_get_empty (self): + if self.get() is not None: + raise Exception + + + def test_find_without_params (self): + self.create_valid() + [self.assert_isinstance(resource) for resource in self.find()] + + def test_find_with_params (self): + resource = self.create_valid() + resources = self.find({ 'name': resource.name }) + if not resources: + raise Exception + self.assert_isinstance(resources[0]) + + def test_find_with_wrong_type (self): + self.create(self.generate_valid_document()) + [self.assert_isinstance(resource) for resources in self.find('test')] + + + def test_create_with_valid_document (self): + self.assert_isinstance(self.create(self.generate_valid_document())) + + def test_create_empty (self): + try: + self.create() + except self.exp_sdk.ApiError: + pass + + def test_create_none (self): + try: + self.create() + except self.exp_sdk.ApiError: + pass + + def test_save (self): + name = self.generate_name() + resource_1 = self.create_valid() + resource_1.name = name + resource_1.save() + resource_2 = self.get(resource_1.uuid) + if resource_2.name != name: + raise Exception + + + def test_refresh (self): + name = self.generate_name() + resource_1 = self.create_valid() + resource_2 = self.get(resource_1.uuid) + resource_1.name = name + resource_1.save() + resource_2.refresh() + if resource_2.name != name: + raise Exception + + def test_uuid_getter (self): + resource = self.create_valid() + if resource.uuid != resource.document['uuid']: + raise Exception + + def test_name_getter_setter (self): + name = self.generate_name() + resource = self.create_valid() + resource.name = name + if resource.name != name: + raise Exception + if resource.document['name'] != name: + raise Exception + + + def test_document (self): + resource = self.create_valid() + if not isinstance(resource.document, dict): + raise Exception \ No newline at end of file From 5afb398c52b6f1d2abae581a3fb853306ca375d5 Mon Sep 17 00:00:00 2001 From: James Dalessio Date: Mon, 28 Mar 2016 00:01:33 -0400 Subject: [PATCH 041/104] API tests almost complete. --- exp/api.py | 246 ++++++++++++++++++++++++-------------- exp/exp.py | 78 ++++-------- tests/test_content.py | 15 +++ tests/test_data.py | 41 +++++++ tests/test_devices.py | 30 ++++- tests/test_experiences.py | 9 +- tests/test_feeds.py | 20 ++++ tests/test_locations.py | 31 +++++ tests/test_network.py | 19 ++- tests/test_things.py | 22 +++- tests/test_zones.py | 35 ++++++ tests/utils.py | 122 ++++++++++--------- 12 files changed, 460 insertions(+), 208 deletions(-) create mode 100644 tests/test_content.py create mode 100644 tests/test_data.py create mode 100644 tests/test_feeds.py create mode 100644 tests/test_locations.py create mode 100644 tests/test_zones.py diff --git a/exp/api.py b/exp/api.py index 66c3606..2459a6d 100644 --- a/exp/api.py +++ b/exp/api.py @@ -2,35 +2,32 @@ import requests import traceback + class Resource (object): _collection_path = None def __init__ (self, document, sdk): - self.document = document + self._document = document self._sdk = sdk - @classmethod - def _get_resource_path_static (cls, uuid): - return cls._collection_path + '/' + uuid + def _get_channel_name (self): + raise NotImplementedError def _get_resource_path (self): - return self._get_resource_path_static(self.document['uuid']) + raise NotImplementedError - def _get_channel_name (self): - return self.document['uuid'] + @property + def document (self): + if not isinstance(self._document, dict): + self._document = {} + return self._document - @classmethod - def get (cls, uuid, sdk): - if not isinstance(uuid, basestring): - return None - try: - document = sdk.api.get(cls._get_resource_path_static(uuid)) - except sdk.exceptions.ApiError as exception: - if exception.status_code == 404: - return None - raise - return cls(document, sdk) + def save (self): + self._document = self._sdk.api.patch(self._get_resource_path(), self.document) + + def refresh (self): + self._document = self._sdk.api.get(self._get_resource_path()) @classmethod def create (cls, document, sdk): @@ -38,46 +35,56 @@ def create (cls, document, sdk): @classmethod def find (cls, params, sdk): - if params and not isinstance(params, dict): - return [] return [cls(document, sdk) for document in sdk.api.get(cls._collection_path, params)['results']] - def save (self): - self.document = self._sdk.api.patch(self._get_resource_path(), self.document) - - def refresh (self): - self.document = self._sdk.api.get(self._get_resource_path()) - def get_channel(self, **kwargs): return self._sdk.network.get_channel(self._get_channel_name(), **kwargs) - def identify (self, *args, **kwargs): - return self.get_channel(consumer=kwargs.pop('consumer', False)).identify(*args, **kwargs) - - def fling (self, *args, **kwargs): - return self.get_channel(consumer=kwargs.pop('consumer', False)).fling(*args, **kwargs) - - -class CommonResourceMixin (object): +class CommonResource (Resource): @property def uuid (self): - return self.document.get('uuid') + return self.document['uuid'] @property - def name (self): - return self.document.get('name') + def name(self): + return self.document['name'] @name.setter def name (self, value): self.document['name'] = value + def _get_channel_name (self): + return self.uuid + + def _get_resource_path (self): + return '{}/{}'.format(self._collection_path, self.uuid) + + @classmethod + def get (cls, uuid, sdk): + if not uuid or not isinstance(uuid, basestring): + return None + path = '{}/{}'.format(cls._collection_path, uuid) + try: + remote_document = sdk.api.get(path) + except sdk.exceptions.ApiError as exception: + if exception.status_code == 404: + return None + raise + return cls(remote_document, sdk) -class DeviceThingBase(Resource): + +class GetLocationMixin (object): + + def _get_location_uuid (self): + raise NotImplementedError + + def _get_zone_keys (self): + raise NotImplementedError def get_location (self): - uuid = self.document.get('location', {}).get('uuid') + uuid = self._get_location_uuid() if not uuid: return None return self._sdk.api.Location.get(uuid, self._sdk) @@ -86,125 +93,184 @@ def get_zones (self): location = self.get_location() if not location: return [] - keys = [document['key'] for document in self.document.get('location', {}).get('zones', [])] - zones = [self._sdk.api.Zone(document, self, self._sdk) for document in location.document['zones'] if document.key in keys] + keys = self._get_zone_keys() + return [self._sdk.api.Zone(document, self, self._sdk) for document in location.document.get('zones', []) if document.get('key') in keys] -class Device (DeviceThingBase, CommonResourceMixin): +class GetExperienceMixin (object): - _collection_path = '/api/devices' + def _get_experience_uuid (self): + raise NotImplementedError def get_experience (self): - uuid = self.document.get('experience', {}).get('uuid') + uuid = self._get_experience_uuid() if not uuid: return None return self._sdk.api.Experience.get(uuid, self._sdk) +class GetDevicesMixin (object): + + def _get_device_query_params (self): + raise NotImplementedError + + def get_devices (self): + return self._sdk.api.Device.find(self._get_device_query_params(), self._sdk) + + +class GetThingsMixin (object): + + def _get_thing_query_params (self): + raise NotImplementedError + + def get_things (self): + return self._sdk.api.Thing.find(self._get_thing_query_params(), self._sdk) -class Thing (DeviceThingBase, CommonResourceMixin): + +class Device (CommonResource, GetLocationMixin, GetExperienceMixin): + + _collection_path = '/api/devices' + + def _get_experience_uuid (self): + return self.document.get('experience', {}).get('uuid') + + def _get_location_uuid (self): + return self.document.get('location', {}).get('uuid') + + def _get_zone_keys (self): + return [document.get('key') for document in self.document.get('location', {}).get('zones', []) if document.get('key')] + + +class Thing (CommonResource, GetLocationMixin): _collection_path = '/api/things' + def _get_location_uuid (self): + return self.document.get('location', {}).get('uuid') + + def _get_zone_keys (self): + return [document.get('key') for document in self.document.get('location', {}).get('zones', []) if document.get('key')] + -class Experience (Resource, CommonResourceMixin): +class Experience (CommonResource, GetDevicesMixin): _collection_path = '/api/experiences' - def get_devices (self): - return self._sdk.api.Device.find({ 'experience.uuid' : self.document.get('uuid') }) + def _get_device_query_params (self): + return { 'experience.uuid' : self.uuid } -class Location (Resource, CommonResourceMixin): +class Location (CommonResource, GetDevicesMixin, GetThingsMixin): _collection_path = '/api/locations' - def get_devices (self): - return self._sdk.api.Device.find({ 'location.uuid': self.document.get('uuid') }) + def _get_device_query_params (self): + return { 'location.uuid' : self.uuid } - def get_things (self): - return self._sdk.api.Thing.find({ 'location.uuid': self.document.get('uuid') }) + def _get_thing_query_params (self): + return { 'location.uuid': self.uuid } def get_zones (self): return [self._sdk.api.Zone(document, self, self._sdk) for document in self.document.get('zones', [])] def get_layout_url (self): - return self._get_resource_path(self.document, self._sdk) + '/layout?_rt=' + self._sdk.authenticator.get_auth()['restrictedToken'] + return self._get_resource_path() + '/layout?_rt=' + self._sdk.authenticator.get_auth()['restrictedToken'] -class Zone (Resource): +class Feed (CommonResource): + + _collection_path = '/api/connectors/feeds' + + def get_data (self): + return self._sdk.api.get(self._get_resource_path() + '/data') + + +class Zone (Resource, GetDevicesMixin, GetThingsMixin): def __init__ (self, document, location, sdk): super(Zone, self).__init__(document, sdk) self._location = location + @property + def key(self): + return self.document.get('key') + + def _get_device_query_params (self): + return { 'location.uuid': self._location.uuid, 'location.zones.key': self.key } + + def _get_thing_query_params (self): + return { 'location.uuid': self._location.uuid, 'location.zones.key': self.key } + def save (self): return self._location.save() def refresh (self): - self._location = self._location.refresh() - self._document = [document for document in self._location.document.get('zones') if document['key'] == self.document['key']] + self._location.refresh() + matches = [document for document in self._location.document.get('zones', []) if self.key == document['key']] + if matches: + self._document = matches[0] def get_location (self): return self._location - def get_devices (self): - return self._sdk.api.Device.find({ 'location.uuid': self._location.document['uuid'], 'location.zones.key': self.document['key'] }) - - def get_things (self): - return self._sdk.api.Thing.find({ 'location.uuid': self._location.document['uuid'], 'location.zones.key': self.document['key'] }) - - @classmethod - def get_channel_name(cls, document, sdk): - return self._location.document['uuid'] + ':zone:' + self.document['key'] - + def _get_channel_name(self): + return self._location.uuid + ':zone:' + self.key -class Feed (Resource, CommonResourceMixin): - _collection_path = '/api/connectors/feeds' - - def get_data (self): - return self._sdk.api.get(self._get_resource_path(self.document, self._sdk) + '/data') class Data (Resource): _collection_path = '/api/data' - @classmethod - def _get_resource_path(cls, document, sdk): - return cls._collection_path + '/' + urllib.quote(document['group']) + '/' + urllib.quote(document['key']) + @property + def group(self): + return self.document.get('group') - @classmethod - def get (cls, document, sdk): - document['group'] = document.get('group', 'default') - document_ = sdk.api.get(self._get_resource_path(document, sdk)) - if not document_: - document_ = { 'key': document['key'], 'group': document['group'], 'value': None } - return cls(document, sdk) + @property + def key(self): + return self.document.get('key') @property def value (self): - return self.document.get('value', None) + return self.document.get('value') @value.setter def value(self, value): self.document['value'] = value + def _get_resource_path(self): + return '{}/{}/{}'.format(self._collection_path, self.group, self.key) + @classmethod - def create (cls, document, sdk): - data = cls(document, sdk) - data.save() - return data + def get (cls, group, key, sdk): + path = '{}/{}/{}'.format(cls._collection_path, group, key) + try: + document = sdk.api.get(path) + except sdk.exceptions.ApiError as exception: + if exception.status_code == 404: + return None + raise + return cls(document, sdk) + + @classmethod + def create (cls, group, key, value, sdk): + path = '{}/{}/{}'.format(cls._collection_path, group, key) + document = sdk.api.put(path, value) + return cls(document, sdk) def save (self): - self.document = self._sdk.api.put(self._get_resource_path(self.document, self._sdk), self.document) + a = self._sdk.api.put(self._get_resource_path(), self.value) + print 'qweqewwqe' + print a def _get_channel_name (self): - return 'data' + ':' + self.document['key'] + ':' + self.document['group'] + return 'data:{}:{}'.format(self.group, self.key) + + -class Content (Resource, CommonResourceMixin): +class Content (CommonResource): _collection_path = '/api/content' diff --git a/exp/exp.py b/exp/exp.py index f6d3454..8a387e7 100644 --- a/exp/exp.py +++ b/exp/exp.py @@ -134,6 +134,7 @@ def put (self, *args, **kwargs): def delete (self, *args, **kwargs): return self._sdk.api.delete(*args, **kwargs) + """ Network """ @property @@ -156,7 +157,6 @@ def create_device (self, document=None): return self._sdk.api.Device.create(document, self._sdk) - def get_thing (self, uuid=None): return self._sdk.api.Thing.get(uuid, self._sdk) @@ -177,70 +177,38 @@ def create_experience (self, document=None): return self._sdk.api.Experience.create(document, self._sdk) - """ def get_experience (self, document=None): - return api.Experience.get(*args, **kwargs) - - # def find_experiences (*args, **kwargs): - # return api.Experience.find(*args, **kwargs) - - # def create_experience (*args, **kwargs): - # return api.Experience.create(*args, **kwargs) - -""" - - -""" - - # d - - - # def get_thing (*args, **kwargs): - # return api.Thing.get(*args, **kwargs) - - # def find_things (*args, **kwargs): - # return api.Thing.find(*args, **kwargs) - - # def create_thing (*args, **kwargs): - # return api.Thing.create(*args, **kwargs) - - - - - - # def get_location (*args, **kwargs): - # return api.Location.get(*args, **kwargs) - - # def find_locations (*args, **kwargs): - # return api.Location.find(*args, **kwargs) + def get_location (self, uuid=None): + return self._sdk.api.Location.get(uuid, self._sdk) - # def create_location (*args, **kwargs): - # return api.Location.create(*args, **kwargs) + def find_locations (self, params=None): + return self._sdk.api.Location.find(params, self._sdk) + def create_location (self, document=None): + return self._sdk.api.Location.create(document, self._sdk) - # def get_data (*args, **kwargs): - # return api.Data.get(*args, **kwargs) - # def find_data (*args, **kwargs): - # return api.Data.find(*args, **kwargs) + def get_feed (self, uuid=None): + return self._sdk.api.Feed.get(uuid, self._sdk) - # def create_data (*args, **kwargs): - # return api.Data.create(*args, **kwargs) + def find_feeds (self, params=None): + return self._sdk.api.Feed.find(params, self._sdk) + def create_feed (self, document=None): + return self._sdk.api.Feed.create(document, self._sdk) - # def get_feed (*args, **kwargs): - # return api.Feed.get(*args, **kwargs) - # def find_feeds (*args, **kwargs): - # return api.Feed.find(*args, **kwargs) + def get_data (self, group=None, key=None): + return self._sdk.api.Data.get(group, key, self._sdk) - # def create_feed (*args, **kwargs): - # return api.Feed.create(*args, **kwargs) + def find_data (self, params=None): + return self._sdk.api.Data.find(params, self._sdk) + def create_data (self, group, key, value): + return self._sdk.api.Data.create(group, key, value, self._sdk) - # def get_content (*args, **kwargs): - # return api.Content.get(*args, **kwargs) - # def find_content (*args, **kwargs): - # return api.Content.find(*args, **kwargs) + def get_content (self, uuid=None): + return self._sdk.api.Content.get(uuid, self._sdk) -""" + def find_content (self, params=None): + return self._sdk.api.Content.find(params, self._sdk) diff --git a/tests/test_content.py b/tests/test_content.py new file mode 100644 index 0000000..759e1f1 --- /dev/null +++ b/tests/test_content.py @@ -0,0 +1,15 @@ + +import unittest + +from . import utils + +class Test(utils.Device, utils.CommonResourceBase): + + get_name = 'get_content' + find_name = 'find_content' + creatable = False + class_ = utils.api.Content + + def create(self, _=None): + return self.exp.find_content()[0] + diff --git a/tests/test_data.py b/tests/test_data.py new file mode 100644 index 0000000..0367824 --- /dev/null +++ b/tests/test_data.py @@ -0,0 +1,41 @@ + +import unittest + +from . import utils + +class Test(utils.Device, utils.ResourceBase): + + get_name = 'get_data' + find_name = 'find_data' + create_name = 'create_data' + class_ = utils.api.Data + + def create_valid (self): + return self.create(group=self.generate_name(), key=self.generate_name(), value={ 'test': self.generate_name() }) + + def create (self, group=None, key=None, value=None): + return self.exp.create_data(group, key, value) + + def test_find (self): + data = self.create_valid() + [self.assert_isinstance(data) for data in self.find()] + items = self.find({ 'group': data.group }) + if not data.key in [item.key for item in items]: + raise Exception + self.assert_isinstance(items[0]) + + def test_create (self): + data = self.exp.create_data('cats', 'fluffy', { 't': 'meow1' }) + if data.key != 'fluffy' or data.group != 'cats' or data.value['t'] != 'meow1': + raise Exception + + def test_update (self): + data = self.create_valid() + data.value = '__test__' + data.save() + print data.group + print data.key + data = self.exp.get_data(data.group, data.key) + print data.value + if data.value != '__test__': + raise Exception \ No newline at end of file diff --git a/tests/test_devices.py b/tests/test_devices.py index ce19551..38508af 100644 --- a/tests/test_devices.py +++ b/tests/test_devices.py @@ -3,9 +3,37 @@ from . import utils -class Test(utils.Device, utils.ResourceBase): +class Test(utils.Device, utils.CommonResourceBase): get_name = 'get_device' find_name = 'find_devices' create_name = 'create_device' class_ = utils.api.Device + + def test_get_location (self): + device = self.create_valid() + location = self.exp.create_location() + device.document['location'] = {} + device.document['location']['uuid'] = location.uuid + device.save() + location = device.get_location() + if not location: + raise Exception + + def test_get_experience (self): + device = self.create_valid() + experience = self.exp.create_experience() + device.document['experience'] = {} + device.document['experience']['uuid'] = experience.uuid + device.save() + experience = device.get_experience() + if not experience: + raise Exception + + def test_get_zones (self): + location = self.exp.create_location({ 'zones': [{ 'key': 'key_1' }, { 'key': 'key_2' }]}) + device = self.create({ 'location': { 'uuid': location.uuid, 'zones': [{ 'key': 'key_2'}] }}) + zones = device.get_zones() + if zones[0].key != 'key_2': + raise Exception + diff --git a/tests/test_experiences.py b/tests/test_experiences.py index 217830d..abc5ddf 100644 --- a/tests/test_experiences.py +++ b/tests/test_experiences.py @@ -3,9 +3,16 @@ from . import utils -class Test(utils.Device, utils.ResourceBase): +class Test(utils.Device, utils.CommonResourceBase): get_name = 'get_experience' find_name = 'find_experiences' create_name = 'create_experience' class_ = utils.api.Experience + + def test_get_devices (self): + experience = self.create_valid() + device = self.exp.create_device({ 'experience': { 'uuid': experience.uuid } }) + devices = experience.get_devices() + if device.uuid not in [x.uuid for x in devices]: + raise Exception diff --git a/tests/test_feeds.py b/tests/test_feeds.py new file mode 100644 index 0000000..fac1952 --- /dev/null +++ b/tests/test_feeds.py @@ -0,0 +1,20 @@ + +import unittest + +from . import utils + +class Test(utils.Device, utils.CommonResourceBase): + + get_name = 'get_feed' + find_name = 'find_feeds' + create_name = 'create_feed' + class_ = utils.api.Feed + + def generate_valid_document (self): + return { 'subtype': 'scala:feed:weather', 'searchValue': '19713', 'name': self.generate_name() } + + def test_get_data (self): + feed = self.create_valid() + data = feed.get_data() + if not isinstance(data, dict): + raise Exception \ No newline at end of file diff --git a/tests/test_locations.py b/tests/test_locations.py new file mode 100644 index 0000000..07b2166 --- /dev/null +++ b/tests/test_locations.py @@ -0,0 +1,31 @@ + +import unittest + +from . import utils + +class Test(utils.Device, utils.CommonResourceBase): + + get_name = 'get_location' + find_name = 'find_locations' + create_name = 'create_location' + class_ = utils.api.Location + + def test_get_devices (self): + location = self.create_valid() + device = self.exp.create_device({ 'location': { 'uuid': location.uuid } }) + devices = location.get_devices() + if device.uuid not in [x.uuid for x in devices]: + raise Exception + + def test_get_things (self): + location = self.create_valid() + thing = self.exp.create_thing({ 'location': { 'uuid': location.uuid }, 'name': self.generate_name(), 'subtype': 'scala:thing:rfid', 'id': '123'}) + things = location.get_things() + if thing.uuid not in [x.uuid for x in things]: + raise Exception + + def test_layout_url (self): + location = self.create_valid() + url = location.get_layout_url() + print url + diff --git a/tests/test_network.py b/tests/test_network.py index 0f7d806..6dcdf7f 100644 --- a/tests/test_network.py +++ b/tests/test_network.py @@ -14,6 +14,23 @@ def test_simple_message_pattern (self): if broadcast.payload['a'] != 1: raise + def test_queue (self): + channel = self.exp.get_channel(self.generate_name()) + listener = channel.listen('m', max_age=2) + channel.broadcast('m', 1) + channel.broadcast('m', 2) + time.sleep(2) + channel.broadcast('m', 3) + time.sleep(.5) + channel.broadcast('m', 4) + time.sleep(.5) + if listener.wait().payload != 3: + raise Exception + if listener.wait().payload != 4: + raise Exception + if listener.wait(): + raise Exception + class Test2 (utils.Base): @@ -45,4 +62,4 @@ def test_listener_cancelling (self): listener.cancel() channel.broadcast('test_message_3') if listener.wait(.1): - raise Exception \ No newline at end of file + raise Exception diff --git a/tests/test_things.py b/tests/test_things.py index 4b7b086..7bab292 100644 --- a/tests/test_things.py +++ b/tests/test_things.py @@ -3,7 +3,7 @@ from . import utils -class Test(utils.Device, utils.ResourceBase): +class Test(utils.Device, utils.CommonResourceBase): get_name = 'get_thing' find_name = 'find_things' @@ -12,3 +12,23 @@ class Test(utils.Device, utils.ResourceBase): def generate_valid_document (self): return { 'subtype': 'scala:thing:rfid', 'id': self.generate_name(), 'name': self.generate_name()} + + def test_get_location (self): + thing = self.create_valid() + location = self.exp.create_location() + thing.document['location'] = {} + thing.document['location']['uuid'] = location.uuid + thing.save() + location = thing.get_location() + if not location: + raise Exception + + def test_get_zones (self): + location = self.exp.create_location({ 'zones': [{ 'key': 'key_1' }, { 'key': 'key_2' }]}) + document = self.generate_valid_document() + document['location'] = { 'uuid': location.uuid, 'zones': [{ 'key': 'key_2'}] } + thing = self.create(document) + zones = thing.get_zones() + if zones[0].key != 'key_2': + raise Exception + diff --git a/tests/test_zones.py b/tests/test_zones.py new file mode 100644 index 0000000..b6ff945 --- /dev/null +++ b/tests/test_zones.py @@ -0,0 +1,35 @@ + +import unittest + +from . import utils + +class Test(utils.Device, utils.ResourceBase): + + class_ = utils.api.Zone + creatable = False + findable = False + + + def create (self, junk=None): + return self.exp.create_location({ 'zones': [{ 'key': self.generate_name(), 'name': self.generate_name() }]}).get_zones()[0] + + + + def test_get_devices (self): + zone = self.create_valid() + device = self.exp.create_device({ 'location': { 'uuid': zone.get_location().uuid, 'zones': [{ 'key': zone.key }]}}) + devices = zone.get_devices() + if device.uuid not in [x.uuid for x in devices]: + raise Exception + if len(devices) > 1: + raise Exception + + + def test_get_things (self): + zone = self.create_valid() + thing = self.exp.create_thing({ 'location': { 'uuid': zone.get_location().uuid, 'zones': [{ 'key': zone.key }]}, 'id': self.generate_name(), 'name': self.generate_name(), 'subtype': 'scala:thing:rfid'}) + things = zone.get_things() + if thing.uuid not in [x.uuid for x in things]: + raise Exception + if len(things) > 1: + raise Exception diff --git a/tests/utils.py b/tests/utils.py index 4edb6fe..322a837 100644 --- a/tests/utils.py +++ b/tests/utils.py @@ -47,24 +47,20 @@ def setUp (self): - - class ResourceBase (object): class_ = None - get_name = '' create_name = '' find_name = '' + findable = True + savable = True + creatable = True + def create (self, document=None): + return getattr(self.exp, self.create_name)(document) - def get (self, *args, **kwargs): - return getattr(self.exp, self.get_name)(*args, **kwargs) - - def create (self, *args, **kwargs): - return getattr(self.exp, self.create_name)(*args, **kwargs) - - def find (self, *args, **kwargs): - return getattr(self.exp, self.find_name)(*args, **kwargs) + def find (self, params=None): + return getattr(self.exp, self.find_name)(params) def create_valid (self): return self.create(self.generate_valid_document()) @@ -76,65 +72,75 @@ def assert_isinstance (self, resource): if not isinstance(resource, self.class_): raise Exception + def test_create (self): + if self.creatable: + self.assert_isinstance(self.create_valid()) + try: + self.create() + except self.exp_sdk.ApiError: + pass + try: + self.create({}) + except self.exp_sdk.ApiError: + pass - def test_get (self): - resource_1 = self.create_valid() - resource_2 = self.get(resource_1.uuid) - self.assert_isinstance(resource_2) - - def test_get_with_invalid_uuid (self): - if self.get('invalid uuid') is not None: - raise Exception - - def test_get_none (self): - if self.get(None) is not None: - raise Exception - - def test_get_empty (self): - if self.get() is not None: + def test_document (self): + resource = self.create_valid() + if not isinstance(resource.document, dict): raise Exception - - def test_find_without_params (self): + def test_find (self): + if not self.findable: + return self.create_valid() [self.assert_isinstance(resource) for resource in self.find()] - - def test_find_with_params (self): - resource = self.create_valid() resources = self.find({ 'name': resource.name }) if not resources: raise Exception self.assert_isinstance(resources[0]) - def test_find_with_wrong_type (self): - self.create(self.generate_valid_document()) - [self.assert_isinstance(resource) for resources in self.find('test')] + def test_save (self): + if self.savable: + self.create_valid().save() + + def test_refresh (self): + self.create_valid().refresh() + def test_get_channel (self): + resource = self.create_valid() + channel = resource.get_channel(consumer=False, system=True) + if not channel.broadcast: # Duck typed. + raise Exception - def test_create_with_valid_document (self): - self.assert_isinstance(self.create(self.generate_valid_document())) - def test_create_empty (self): - try: - self.create() - except self.exp_sdk.ApiError: - pass +class CommonResourceBase (ResourceBase): - def test_create_none (self): - try: - self.create() - except self.exp_sdk.ApiError: - pass + get_name = '' - def test_save (self): - name = self.generate_name() + def get (self, uuid=None): + return getattr(self.exp, self.get_name)(uuid) + + def test_get (self): resource_1 = self.create_valid() - resource_1.name = name - resource_1.save() resource_2 = self.get(resource_1.uuid) - if resource_2.name != name: + self.assert_isinstance(resource_2) + if self.get('invalid uuid') is not None: + raise Exception + if self.get() is not None: + raise Exception + if getattr(self.exp, self.get_name)() is not None: raise Exception + def test_save (self): + if self.savable: + name = self.generate_name() + resource_1 = self.create_valid() + resource_1.name = name + resource_1.save() + resource_2 = self.get(resource_1.uuid) + if resource_2.name != name: + raise Exception + super(CommonResourceBase, self).test_save() def test_refresh (self): name = self.generate_name() @@ -145,23 +151,21 @@ def test_refresh (self): resource_2.refresh() if resource_2.name != name: raise Exception + super(CommonResourceBase, self).test_refresh() - def test_uuid_getter (self): + def test_uuid (self): resource = self.create_valid() - if resource.uuid != resource.document['uuid']: + if not resource.uuid or resource.uuid != resource.document['uuid']: raise Exception - def test_name_getter_setter (self): + def test_name (self): name = self.generate_name() resource = self.create_valid() + if resource.document['name'] != resource.name: + raise Exception resource.name = name if resource.name != name: raise Exception if resource.document['name'] != name: raise Exception - - def test_document (self): - resource = self.create_valid() - if not isinstance(resource.document, dict): - raise Exception \ No newline at end of file From 833e1126510127acf63699d9222e8cabd085466f Mon Sep 17 00:00:00 2001 From: James Dalessio Date: Mon, 28 Mar 2016 12:22:31 -0400 Subject: [PATCH 042/104] Big readme updates and finishing tests. --- README.md | 235 +++++++++++++++++++++++++++++++----------- exp/api.py | 71 +++++++------ exp/exp.py | 4 +- tests/test_content.py | 49 +++++++++ tests/test_data.py | 9 +- tests/utils.py | 17 +-- 6 files changed, 278 insertions(+), 107 deletions(-) diff --git a/README.md b/README.md index 877cd33..1927c23 100644 --- a/README.md +++ b/README.md @@ -171,6 +171,28 @@ listener = channel.listen('my-event', max_age=30) ``` +### channel.fling(payload) + +Fling an app launch payload on the channel. + +```python +location = exp.get_location('[uuid]') +location.get_channel().fling({ 'appTemplate' : { 'uuid': '[uuid'} }) + +``` + + +### channel.identify() + +Requests that devices listening for this event on this channel visually identify themselves. Implementation is device specific; this is simply a convience method. + +```python +location = exp.get_location('[uuid]') +location.get_channel().identify() # Tell all devices at this location to identify themselves! + +``` + + ## listener.wait(timeout=0) Wait for `timeout` seconds for broadcasts. Returns a broadcast if a broadcast is in the queue or if a broadcast is received before the timeout. If timeout is reached, returns `None`. @@ -216,7 +238,7 @@ while True: broadcast = listener.wait(60) if broadcast && broadcast.payload == 'hi!': broadcast.respond('hi back at you!') - + break ``` @@ -287,6 +309,102 @@ exp.delete('/api/location/[uuid]') ``` +## Resources + + +### exp.get_device(uuid=None) +Returns the [device](#devices) with the given uuid or `None` if no [device](#devices) could be found. + +### exp.create_device(document=None) +Returns a [device](#devices) created based on the supplied document. +```python +device = exp.create_device({ 'subtype': 'scala:device:player' }) +``` + +### exp.find_devices(params=None) +Returns a list of [devices](#devices) matching the given query parameters. `params` is a dictionary of query parameters. + + + +### exp.get_thing(uuid=None) +Returns the [thing](#things) with the given uuid or `None` if no [thing](#things) could be found. + +### exp.create_thing(document=None) +Returns a [thing](#things) created based on the supplied document. +```python +thing = exp.create_thing({ 'subtype': 'scala:thing:rfid', 'id': '[rfid]', 'name': 'my-rfid-tag' }) +``` + +### exp.find_things(params=None) +Returns a list of [things](#things) matching the given query parameters. `params` is a dictionary of query parameters. + + + +### exp.get_experience(uuid=None) +Returns the [experience](#experiences) with the given uuid or `None` if no [experience](#experiences) could be found. + +### exp.create_experience(document=None) +Returns a [experience](#experience) created based on the supplied document. + +### exp.find_experiences(params=None) +Returns a list of [experiences](#experiences) matching the given query parameters. `params` is a dictionary of query parameters. + + +### exp.get_location(uuid=None) +Returns the [location](#locations) with the given uuid or `None` if no [location](#locations) could be found. + +### exp.create_location(document=None) +Returns a [location](#location) created based on the supplied document. + +### exp.find_locations(params=None) +Returns a list of [locations](#locations) matching the given query parameters. `params` is a dictionary of query parameters. + + +## exp.get_feed(uuid=None) +Returns the [feed](#feeds) with the given uuid or `None` if no [feed](#feeds) could be found. + +### exp.create_feed(document=None) +Returns a [feed](#feed) created based on the supplied document. +```python +feed = exp.create_feed({ 'subtype': 'scala:feed:weather', 'searchValue': '16902', 'name': 'My Weather Feed' }) +``` + +### exp.find_feeds(params=None) +Returns a list of [feeds](#feeds) matching the given query parameters. `params` is a dictionary of query parameters. +```python +feeds = exp.find_feeds({ 'subtype': 'scala:feed:facebook' }) +``` + + +## exp.get_data(group='default', key=None) +Returns the [data item](#data) with the given group or key or `None` if the [data item] could not be found. +```python +data = exp.get_data('cats', 'fluffy') +``` + +### exp.create_data(group='default', key=None, value=None) +Returns a [data item](#data) created based on the supplied group, key, and value. +```python +data = exp.create_data('cats', 'fluffy', { 'color': 'brown'}) +``` + +### exp.find_data(params=None) +Returns a list of [data items](#data) matching the given query parameters. `params` is a dictionary of query parameters. +```python +items = exp.find_data({ 'group': 'cats' }) +``` + + +## exp.get_content(uuid=None) +Returns the [content item](#content) with the given uuid or `None` if no [content item](#content) could be found. + +### exp.find_content(params=None) +Returns a list of [content items](#content) matching the given query parameters. `params` is a dictionary of query parameters. + + + + + ## Common Resource Methods and Attributes @@ -332,7 +450,6 @@ print device_2.name # 'new-name' ``` - ### resource.get_channel(system=False, consumer=False) Returns the channel whose name is contextually associated with this resource. @@ -344,101 +461,104 @@ channel.broadcast('hello?') ``` -### resource.fling(payload) -Fling an app launch payload on this resource's channel. -```python -location = exp.get_location('[uuid]') -location.fling({ 'appTemplate' : { 'uuid': '[uuid'} }) - -``` - -See ??? for more information about app launch payloads. - -### resource.identify() - -Requests that devices listening for this event on this resource's channel visually identify themselves. Implementation is device specific; this is simply a convience method. - -```python -location = exp.get_location('[uuid]') -location.identify() # Tell all devices at this location to identify themselves! - -``` +## Devices +Devices inherit all [common resource methods and attributes](#common-resource-methods-and-attributes). +### device.get_location() +Returns the device's [location](#locations) or `None`. -## Devices +### device.get_zones() +Returns a list of the device's [zones](#zones). -Devices inherit all [common resource methods and attributes](#common-resource-methods-and-attributes). +### device.get_experience() +Returns the device's [experience](#experiences) or `None` -### `exp.get_device(uuid=None)` -Returns the device with the given uuid or `None`. +## Things +Things inherit all [common resource methods and attributes](#common-resource-methods-and-attributes). -```python +### thing.get_location() +Returns the device's [location](#locations) or `None`. -device1 = exp.get_device('[matching uuid]') -device2 = exp.get_device() # None -device3 = exp.get_device('[unmatched uuid]') # None +### thing.get_zones() +Returns a list of the thing's [#zones](#zones). -``` +### thing.get_experience() +Returns the device's [experience](#experiences) or `None` -### `exp.create_device(document=None)` -Creates and returns a new device. +### Experiences +Experiences inherit all [common resource methods and attributes](#common-resource-methods-and-attributes). -```python +### experience.get_devices() +Returns a list of devices that are part of this experience. -device1 = exp.create_device() -device2 = exp.create_device({ 'name': 'my-new-device' }) -``` +### Locations +Locations inherit all [common resource methods and attributes](#common-resource-methods-and-attributes). +### location.get_devices() +Returns a list of [devices](#devices) that are part of this location. -### `exp.find_devices(params=None)` +### location.get_things() +Returns a list of [devices](#devices) that are part of this location. -Returns a list of devices matching the given query parameters. `params` is a dictionary of query parameters. +### location.get_zones() +Returns a list of [zones](#zones) that are part of this location. -```python +### location.get_layout_url() +Returns a url pointing to the location's layout image. -devices = exp.find_devices({ 'location.zone.key': 'my-zone-key' }) -[print device.name for device in devices] -``` +### Zones +Zones inherit the [common resource methods and attributes](#common-resource-methods-and-attributes) `save()`, `refresh()`, and `get_channel()`. +### zone.key +The zone's key. -### device.get_location() +### zone.name +The zone's name. -Returns the device's location or `None`. +### zone.get_devices() +Returns all [devices](#devices) that are members of this zone. -### device.get_experience() +### zone.get_things() +Returns all [things](#things) that are members of this zone. -Returns the device's experience or `None` +### zone.get_location() +Returns the zone's [location](#locations) +## Feeds +Feeds inherit all [common resource methods and attributes](#common-resource-methods-and-attributes). -## Things +### feed.get_data() +Returns the feed's data. -Devices inherit all [common resource methods and attributes](#common-resource-methods-and-attributes). -### thing.get_location() +## Data -Returns the device's location or `None`. +Zones inherit the [common resource methods and attributes](#common-resource-methods-and-attributes) `save()`, `refresh()`, and `get_channel()`. -### thing.get_experience() +### data.key +The data item's key. Settable. -Returns the device's experience or `None` +### data.group +The data item's group. Settable +### data.value +The data item's value. Settable. -### Experiences ### Locations ### Zones ### Feeds @@ -461,15 +581,6 @@ Returns the device's experience or `None` -## Things -- ```thing = exp.get_thing(uuid)```: Retrieves a thing by uuid. -- ```thing = exp.create_thing(document)```: Creates a thing from a dictionary. -- ```thing = exp.find_things(params)```: Retrieves a list of things given a dictionary of query parameters. See the API docs. -- ```thing.uuid```: The thing's uuid. -- ```thing.document```: The thing's underying document, a dictionary. -- ```thing.save()```: Saves the thing to EXP. -- ```thing.get_channel(system=False, consumer=False)```: Retrieves a [channel](#Channels) for communication about the thing. - ## Experiences - ```experience = exp.get_experience(uuid)```: Retrieves an experience by uuid. - ```experience = exp.create_experience(document)```: Creates an experience from a dictionary. diff --git a/exp/api.py b/exp/api.py index 2459a6d..9c5b5a9 100644 --- a/exp/api.py +++ b/exp/api.py @@ -193,7 +193,15 @@ def __init__ (self, document, location, sdk): @property def key(self): - return self.document.get('key') + return self.document['key'] + + @property + def name(self): + return self.document['name'] + + @name.setter + def name (self, value): + self.document['name'] = value def _get_device_query_params (self): return { 'location.uuid': self._location.uuid, 'location.zones.key': self.key } @@ -227,10 +235,18 @@ class Data (Resource): def group(self): return self.document.get('group') + @group.setter + def group (self, value): + self.document['group'] = value + @property def key(self): return self.document.get('key') + @key.setter + def group (self, value): + self.document['key'] = value + @property def value (self): return self.document.get('value') @@ -257,12 +273,11 @@ def get (cls, group, key, sdk): def create (cls, group, key, value, sdk): path = '{}/{}/{}'.format(cls._collection_path, group, key) document = sdk.api.put(path, value) - return cls(document, sdk) + data = cls(document, sdk) + return data def save (self): - a = self._sdk.api.put(self._get_resource_path(), self.value) - print 'qweqewwqe' - print a + self._document = self._sdk.api.put(self._get_resource_path(), self.value) def _get_channel_name (self): return 'data:{}:{}'.format(self.group, self.key) @@ -274,38 +289,36 @@ class Content (CommonResource): _collection_path = '/api/content' + def save (self): + raise NotImplementedError + + @property + def subtype(self): + return self.document.get('subtype') + def get_children (self): - if self._children is None: - if self.document['children']: - return [self.__class__(document, self._sdk) for document in self.document['children']] - else: - self.refresh() - return self.get_children() - return [] + path = '{}/{}/children'.format(self._collection_path, self.uuid) + return [self.__class__(document, self._sdk) for document in self._sdk.api.get(path).get('children', [])] - def get_subtype (self): - return self.document['subtype'] + def _get_delivery_url (self): + auth = self._sdk.authenticator.get_auth() + base = '{}/api/delivery'.format(auth['api']['host']) + encoded_path = urllib.quote(self.document.get('path')) + return '{}/{}?_rt={}'.format(base, encoded_path, auth['restrictedToken']) def get_url (self): - auth = self._sdk.authenticator.get_auth() - delivery_url = auth['api']['host'] + '/api/delivery' - rt_string = '?_rt=' + auth['restrictedToken'] - if self.get_subtype() == 'scala:content:file': - return delivery_url + self._get_resource_path(self.document, self._sdk) + rt_string - elif self.get_subtype() == 'scala:content:app': - return delivery_url + self._get_resource_path(self.document, self._sdk) + rt_string - elif self.get_subtype() == 'scala:content:url': - return self.document['url'] + if self.subtype == 'scala:content:file': + return self._get_delivery_url() + elif self.subtype == 'scala:content:app': + return self._get_delivery_url() + elif self.subtype == 'scala:content:url': + return self.document.get('url') def get_variant_url (self, name): - auth = self._sdk.authenticator.get_auth() - delivery_url = auth['api']['host'] + '/api/delivery' - rt_string = '?_rt=' + auth['restrictedToken'] - if self.get_subtype() == 'scala:content:file' and self.has_variant(name): - return delivery_url + '?variant=' + urllib.quote(name) + rt_string + return '{}&variant='.format(self._get_delivery_url(), name) def has_variant (self, name): - return name in self.document.get('variants', []) + return name in [variant['name'] for variant in self.document.get('variants', [])] diff --git a/exp/exp.py b/exp/exp.py index 8a387e7..018aac5 100644 --- a/exp/exp.py +++ b/exp/exp.py @@ -197,13 +197,13 @@ def create_feed (self, document=None): return self._sdk.api.Feed.create(document, self._sdk) - def get_data (self, group=None, key=None): + def get_data (self, group='default', key=None): return self._sdk.api.Data.get(group, key, self._sdk) def find_data (self, params=None): return self._sdk.api.Data.find(params, self._sdk) - def create_data (self, group, key, value): + def create_data (self, group=None, key=None, value=None): return self._sdk.api.Data.create(group, key, value, self._sdk) diff --git a/tests/test_content.py b/tests/test_content.py index 759e1f1..e0f7548 100644 --- a/tests/test_content.py +++ b/tests/test_content.py @@ -8,8 +8,57 @@ class Test(utils.Device, utils.CommonResourceBase): get_name = 'get_content' find_name = 'find_content' creatable = False + savable = False class_ = utils.api.Content def create(self, _=None): return self.exp.find_content()[0] + def create_valid (self): + return self.create() + + def test_subtype (self): + items = self.exp.find_content() + for item in items: + if not item.subtype: + raise Exception + + def test_get_url_url (self): + items = [item for item in self.exp.find_content() if item.subtype == 'scala:content:url'] + if not items: + raise Exception('No url content items found for testing.') + if not items[0].get_url(): + raise Exception + + def test_has_variant (self): + items = self.exp.find_content() + for item in items: + variants = item.document.get('variants', []) + if variants: + for variant in variants: + if not item.has_variant(variant['name']): + raise Exception + if item.has_variant('not a variant'): + raise Exception + + def test_get_url_file (self): + items = [item for item in self.exp.find_content() if item.subtype == 'scala:content:file'] + if not items: + raise Exception('No url content items found for testing.') + if not items[0].get_url(): + raise Exception + + + def test_get_url_app (self): + items = [item for item in self.exp.find_content() if item.subtype == 'scala:content:app'] + if not items: + raise Exception('No url content items found for testing.') + if not items[0].get_url(): + raise Exception + + def test_get_variant_url (self): + items = self.exp.find_content() + for item in items: + if not item.get_variant_url('https://github.com/test_variant'): + raise Exception + diff --git a/tests/test_data.py b/tests/test_data.py index 0367824..2680347 100644 --- a/tests/test_data.py +++ b/tests/test_data.py @@ -31,11 +31,8 @@ def test_create (self): def test_update (self): data = self.create_valid() - data.value = '__test__' + data.value = { 'test2': 'a' } data.save() - print data.group - print data.key data = self.exp.get_data(data.group, data.key) - print data.value - if data.value != '__test__': - raise Exception \ No newline at end of file + if data.value['test2'] != 'a': + raise Exception diff --git a/tests/utils.py b/tests/utils.py index 322a837..39abe56 100644 --- a/tests/utils.py +++ b/tests/utils.py @@ -143,14 +143,15 @@ def test_save (self): super(CommonResourceBase, self).test_save() def test_refresh (self): - name = self.generate_name() - resource_1 = self.create_valid() - resource_2 = self.get(resource_1.uuid) - resource_1.name = name - resource_1.save() - resource_2.refresh() - if resource_2.name != name: - raise Exception + if self.savable: + name = self.generate_name() + resource_1 = self.create_valid() + resource_2 = self.get(resource_1.uuid) + resource_1.name = name + resource_1.save() + resource_2.refresh() + if resource_2.name != name: + raise Exception super(CommonResourceBase, self).test_refresh() def test_uuid (self): From 6da336e3dad68e0d7ad195509e84070a3a25de1a Mon Sep 17 00:00:00 2001 From: James Dalessio Date: Mon, 28 Mar 2016 12:24:26 -0400 Subject: [PATCH 043/104] update readme. --- README.md | 73 ++++++++++++------------------------------------------- 1 file changed, 15 insertions(+), 58 deletions(-) diff --git a/README.md b/README.md index 1927c23..28b59c1 100644 --- a/README.md +++ b/README.md @@ -546,7 +546,7 @@ Returns the feed's data. ## Data -Zones inherit the [common resource methods and attributes](#common-resource-methods-and-attributes) `save()`, `refresh()`, and `get_channel()`. +Data items inherit the [common resource methods and attributes](#common-resource-methods-and-attributes) `save()`, `refresh()`, and `get_channel()`. ### data.key The data item's key. Settable. @@ -558,12 +558,21 @@ The data item's group. Settable The data item's value. Settable. +## Content +Content items inherit all [common resource methods and attributes](#common-resource-methods-and-attributes) except `save()`. + +### `content.subtype` +The content item's subtype. Not settable. + +### `content.get_url()` +Returns the delivery url for this content item. + +### `content.has_variant(name)` +Returns a boolean indicating whether or not this content item has a variant with the given name. + +### `content.get_variant_url(https://github.com/name)` +Returns the delivery url for a variant of this content item. -### Locations -### Zones -### Feeds -### Data -### Content @@ -581,58 +590,6 @@ The data item's value. Settable. -## Experiences -- ```experience = exp.get_experience(uuid)```: Retrieves an experience by uuid. -- ```experience = exp.create_experience(document)```: Creates an experience from a dictionary. -- ```experiences = exp.find_experiences(params)```: Retrieves a list of experiences given a dictionary of query params. See the API docs. -- ```experience.uuid```: The experience's uuid. -- ```experience.document```: The experience's underlying document, a dictionary. -- ```experience.save()```: Saves the experience to EXP. -- ```experience.get_channel(system=False, consumer=False)```: Get a [channel](#Channels) for communication about this experience. - - -## Locations -- ```location = exp.get_location(uuid)```: Retrieves a location by uuid. -- ```location = exp.create_location(document)```: Creates a location from a dictionary. -- ```locations = exp.find_locations(params)```: Retrieves a list of locations given a dictionary of query params. See the API docs. -- ```location.uuid```: The locations's uuid. -- ```location.document```: The location's underlying document, a dictionary. -- ```location.get_channel(system=False, consumer=False)```: Get a [channel](#Channels) for communication about this location. -- ```location.get_zones()```: Get a list of [zones](#Zones) that are part of this location. - - -## Zones -- ```zone.document```: The underlying zone's document, a dictionary. -- ```zone.get_channel(system=False, consumer=False)```: Get a [channel](#Channels) for communication about this zone. - -## Content -- ```content = exp.get_content(uuid)```: Retrieves a content resource by uuid. -- ```content_list = exp.find_content(params)```: Returns a list of content using the given query params. -- ```content.get_url()```: Returns a delivery URL for content retrieval. -- ```content.get_variant_url()```: Returns a delivery URL for a variant of the content. -- ```content.children```: A list of child content resources. -- ```content.document```: The underlying zone's document, a dictionary. -- ```content.subtype```: The content subtype. See the API docs. - -## Feeds -- ```feed = exp.get_feed(uuid)```: Retrieves a feed resource by uuid. -- ```feeds = exp.find_feeds(params)```: Get a list of feeds given a dictionary of query params. See the API docs. -- ```feed = exp.create_feed(document)```: Create and save a new feed from a feed document. -- ```feed.document```: The underlying feed's document, a dictionary. -- ```feed.get_data()```: Get the feed's data. - - -## Data -- ```data = exp.get_data(key, group='default')```: Retrieves data by key and group. -- ```data = exp.find_data(params)```: Retrieves a list of data given a dictionary of query params. See the API docs. -- ```data = exp.create_data(key, value, group='default')```: -- ```data.save()```: Saves the data. -- ```data.value```: The value of the data, a JSON serializable type. -- ```data.key```: The data's key. -- ```data.group```: The data's group. - - - # Examples ## Creating a Device and Listening for Updates From 278e4062393a1b63cb90422ad86a7faa2b0adcbd Mon Sep 17 00:00:00 2001 From: James Dalessio Date: Mon, 28 Mar 2016 14:01:28 -0400 Subject: [PATCH 044/104] Expirement with md. --- README.md | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 28b59c1..9a2b58f 100644 --- a/README.md +++ b/README.md @@ -561,16 +561,17 @@ The data item's value. Settable. ## Content Content items inherit all [common resource methods and attributes](#common-resource-methods-and-attributes) except `save()`. -### `content.subtype` +**`content.subtype`** The content item's subtype. Not settable. -### `content.get_url()` +`content.get_url()` Returns the delivery url for this content item. -### `content.has_variant(name)` +--- +***`content.has_variant(name)`*** Returns a boolean indicating whether or not this content item has a variant with the given name. -### `content.get_variant_url(https://github.com/name)` +`content.get_variant_url(https://github.com/name)` Returns the delivery url for a variant of this content item. From 930e261e2b51b34ccb693ae6ebb90703243bc7c3 Mon Sep 17 00:00:00 2001 From: James Dalessio Date: Mon, 28 Mar 2016 14:06:27 -0400 Subject: [PATCH 045/104] Expirement with md. --- README.md | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 9a2b58f..cf679d6 100644 --- a/README.md +++ b/README.md @@ -564,14 +564,13 @@ Content items inherit all [common resource methods and attributes](#common-resou **`content.subtype`** The content item's subtype. Not settable. -`content.get_url()` +**`content.get_url()`** Returns the delivery url for this content item. ---- -***`content.has_variant(name)`*** +**`content.has_variant(name)`** Returns a boolean indicating whether or not this content item has a variant with the given name. -`content.get_variant_url(https://github.com/name)` +**`content.get_variant_url(https://github.com/name)`** Returns the delivery url for a variant of this content item. From 2aa40da4d6c4aa73a09903fb61be15b3e86ef7c8 Mon Sep 17 00:00:00 2001 From: James Dalessio Date: Mon, 28 Mar 2016 14:12:11 -0400 Subject: [PATCH 046/104] Expirement with md. --- README.md | 71 ++++++++++++++++++++++++++----------------------------- 1 file changed, 33 insertions(+), 38 deletions(-) diff --git a/README.md b/README.md index cf679d6..675fb82 100644 --- a/README.md +++ b/README.md @@ -363,42 +363,42 @@ Returns a list of [locations](#locations) matching the given query parameters. ` ## exp.get_feed(uuid=None) Returns the [feed](#feeds) with the given uuid or `None` if no [feed](#feeds) could be found. -### exp.create_feed(document=None) +**`exp.create_feed(document=None)`** Returns a [feed](#feed) created based on the supplied document. ```python feed = exp.create_feed({ 'subtype': 'scala:feed:weather', 'searchValue': '16902', 'name': 'My Weather Feed' }) ``` -### exp.find_feeds(params=None) +**`exp.find_feeds(params=None)`** Returns a list of [feeds](#feeds) matching the given query parameters. `params` is a dictionary of query parameters. ```python feeds = exp.find_feeds({ 'subtype': 'scala:feed:facebook' }) ``` -## exp.get_data(group='default', key=None) +**`exp.get_data(group='default', key=None)`** Returns the [data item](#data) with the given group or key or `None` if the [data item] could not be found. ```python data = exp.get_data('cats', 'fluffy') ``` -### exp.create_data(group='default', key=None, value=None) +**`exp.create_data(group='default', key=None, value=None)`** Returns a [data item](#data) created based on the supplied group, key, and value. ```python data = exp.create_data('cats', 'fluffy', { 'color': 'brown'}) ``` -### exp.find_data(params=None) +**`exp.find_data(params=None)`** Returns a list of [data items](#data) matching the given query parameters. `params` is a dictionary of query parameters. ```python items = exp.find_data({ 'group': 'cats' }) ``` -## exp.get_content(uuid=None) +**`exp.get_content(uuid=None)`** Returns the [content item](#content) with the given uuid or `None` if no [content item](#content) could be found. -### exp.find_content(params=None) +**`exp.find_content(params=None)`** Returns a list of [content items](#content) matching the given query parameters. `params` is a dictionary of query parameters. @@ -408,19 +408,16 @@ Returns a list of [content items](#content) matching the given query parameters. ## Common Resource Methods and Attributes -### resource.uuid - +**`resource.uuid`** The uuid of the resource. Cannot be set. Maps to `resource.document['uuid']` -### resource.name - +**`resource.name`** The name of the resource. Can be set directly. Maps to `resource.document['name']`. -### resource.document - +**`resource.document`** The resource's underlying document -### resource.save() +**`resource.save()`** Saves the resource and updates the document in place. @@ -434,7 +431,7 @@ device.save() ``` -### resource.refresh() +**`resource.refresh()`** Refreshes the resource's underlying document in place. @@ -450,7 +447,7 @@ print device_2.name # 'new-name' ``` -### resource.get_channel(system=False, consumer=False) +**`resource.get_channel(system=False, consumer=False)`** Returns the channel whose name is contextually associated with this resource. @@ -471,90 +468,88 @@ channel.broadcast('hello?') ## Devices Devices inherit all [common resource methods and attributes](#common-resource-methods-and-attributes). -### device.get_location() +**`device.get_location()`** Returns the device's [location](#locations) or `None`. -### device.get_zones() +**`device.get_zones()`** Returns a list of the device's [zones](#zones). -### device.get_experience() +**`device.get_experience()`** Returns the device's [experience](#experiences) or `None` ## Things Things inherit all [common resource methods and attributes](#common-resource-methods-and-attributes). -### thing.get_location() +**`thing.get_location()`** Returns the device's [location](#locations) or `None`. -### thing.get_zones() +**`thing.get_zones()`** Returns a list of the thing's [#zones](#zones). -### thing.get_experience() +**`thing.get_experience()`** Returns the device's [experience](#experiences) or `None` ### Experiences Experiences inherit all [common resource methods and attributes](#common-resource-methods-and-attributes). -### experience.get_devices() +**`experience.get_devices()`** Returns a list of devices that are part of this experience. ### Locations Locations inherit all [common resource methods and attributes](#common-resource-methods-and-attributes). -### location.get_devices() +**`location.get_devices()`** Returns a list of [devices](#devices) that are part of this location. -### location.get_things() +**`location.get_things()`** Returns a list of [devices](#devices) that are part of this location. -### location.get_zones() +**`location.get_zones()`** Returns a list of [zones](#zones) that are part of this location. -### location.get_layout_url() +**`location.get_layout_url()`** Returns a url pointing to the location's layout image. ### Zones Zones inherit the [common resource methods and attributes](#common-resource-methods-and-attributes) `save()`, `refresh()`, and `get_channel()`. -### zone.key +**`zone.key`** The zone's key. -### zone.name +**`zone.name`** The zone's name. -### zone.get_devices() +**`zone.get_devices()`** Returns all [devices](#devices) that are members of this zone. -### zone.get_things() +**`zone.get_things()`** Returns all [things](#things) that are members of this zone. -### zone.get_location() +**`zone.get_location()`** Returns the zone's [location](#locations) ## Feeds - Feeds inherit all [common resource methods and attributes](#common-resource-methods-and-attributes). -### feed.get_data() +**`feed.get_data()`** Returns the feed's data. ## Data - Data items inherit the [common resource methods and attributes](#common-resource-methods-and-attributes) `save()`, `refresh()`, and `get_channel()`. -### data.key +**`data.key`** The data item's key. Settable. -### data.group +**`data.group`** The data item's group. Settable -### data.value +**`data.value`** The data item's value. Settable. From ee41429809306080ecb8da56df8effaafcd8e80e Mon Sep 17 00:00:00 2001 From: James Dalessio Date: Mon, 28 Mar 2016 15:09:34 -0400 Subject: [PATCH 047/104] docs --- README.md | 426 ++++++++++++++++++++++++++---------------------------- 1 file changed, 203 insertions(+), 223 deletions(-) diff --git a/README.md b/README.md index 675fb82..0be6ecf 100644 --- a/README.md +++ b/README.md @@ -36,16 +36,27 @@ host | ```'https://api.goexp.io/'``` | The api server to authenticate with. enable_network | ```True``` | Whether to enable real time network communication. If set to ```False``` you will be unable to listen on the [EXP network](# Communicating on the EXP Network). -# Reference +# Python SDK Reference v1.0.0 + +## Getting Started ## Runtime -### exp_sdk.start(**options) +**`exp_sdk.start(**options)`** -Starts and returns an sdk instance. +Starts and returns an sdk instance. Can be called multiple times to start multiple independent instances of the sdk. The sdk can be started using user, device, or consumer app credentials.`**options` supports the following keyword arguments: -```python +- `username=None` The username used to log in to EXP. Required user credential. +- `password=None` The password of the user. Required user credential. +- `organization=None` The organization of the user. Required user credential +- `uuid=None` The device or consumer app uuid. Required consumer app credential and required device credential unless `allow_pairing` is `True`. +- `secret=None` The device secret. Required device credential unless `allow_pairing` is `True`. +- `api_key=None` The consumer app api key. Required consumer app credential. +- `allow_pairing=False` Whether to allow authentication to fallback to pairing mode. If `True`, invalid or empty device - credentials will start the sdk in pairing mode. +- `host=https://api.goexp.io` The api host to authenticate with. +- `enable_network=True` Whether or not to establish a socket connection with the EXP network. If `False`, you will not be - able to listen for broadcasts. +```python import exp_sdk # Authenticating as a user. @@ -56,365 +67,325 @@ exp = exp_sdk.start(uuid='[uuid]', secret='[secret]') # Authenticating as a consumer app. exp = exp_sdk.start(uuid='[uuid]', api_key='[api-key]') - ``` -`start` can be called multiple times to start multiple independent instances of the sdk. The sdk can be started using user, device, or consumer app credentials. - -`**options` supports the following keyword arguments: - -Name | Default | Description ---- | --- | --- -username | `None` | The username used to log in to EXP. Required user credential. -password | `None` | The password of the user. Required user credential. -organization | `None` | The organization of the user. Required user credential -uuid | `None` | The device or consumer app uuid. Required consumer app credential and required device credential unless `allow_pairing` is `True`. -secret | `None` | The device secret. Required device credential unless `allow_pairing` is `True`. -api_key | `None` | The consumer app api key. Required consumer app credential. -allow_pairing | `False` | Whether to allow authentication to fallback to pairing mode. If `True`, invalid or empty device credentials will start the sdk in pairing mode. -host | `https://api.goexp.io` | The api host to authenticate with. -enable_network | `True` | Whether or not to establish a socket connection with the EXP network. If `False`, you will not be able to listen for broadcasts. - - -### exp_sdk.stop() +**`exp_sdk.stop()`** Stops all running instances of the sdk, cancels all listeners and stops all network connections. ```python - exp_1 = exp_sdk.start(**options_1) exp_2 = exp_sdk.start(**options_2) exp_sdk.stop() - exp_2.create_device() # Exception. - ``` New instances can still be created by calling `start`. -### exp.stop() + +**`exp.stop()`** Stops the sdk instance, cancels its listeners, and stops all network connections. ```python - exp = exp_sdk.start(**options) exp.stop() exp.get_auth() # Exception. - ``` Sdk instances cannot be restarted and any invokation on the instance will raise an exception. - -### exp.is_connected +**`exp.is_connected`** Whether or not there is an active socket connection to the network. ```python - # Wait for a connection. while not exp.is_connected: time.sleep(1) - ``` -### exp.get_auth() +**`exp.get_auth()`** Returns the up to date authentication payload. The authentication payload may be updated when invoking this method. ```python - print 'My authentication token is : %s' % exp.get_auth()['token'] - ``` -## Network +## Custom HTTP Requests +These methods all users to send custom authenticated API calls. `params` is a dictionary of url params, `payload` is a JSON serializable type, and `timeout` is the duration, in seconds, to wait for the request to complete. `path` is relative to the api host root. All methods will return a JSON serializable type. -### exp.getChannel(name, consumer=False, system=False) +**`exp.get(path, params=None, timeout=10)`** -Returns a the channel with the given name and flags. +Send a GET request. ```python - -channel = exp.get_channel('my-consumer-channel', consumer=True) - +result = exp.get('/api/devices', { 'name': 'my-name' }) # Find devices by name. ``` +**`exp.post(path, payload=None, params=None, timeout=10)`** -### channel.broadcast(name, payload=None, timeout=0.1) - -Sends a broadcast on the channel with the given name and payload. Returns a list of responses. `timeout` is the number of seconds to hold the request open to wait for responses. +Send a POST request. ```python +document = exp.post('/api/experiences', {}) # Create a new empty experience. +``` -channel = exp.get_channel('my-channel') -responses = channel.broadcast('hi!', { 'test': 'nice to meet you!' }) -[print response for response in responses] +**`exp.patch(path, payload=None, params=None, timeout=10)`** +Send a PATCH request. + +```python +document = exp.patch('/api/experiences/[uuid]', { 'name': 'new-name' }) # Rename an experience. ``` -### channel.listen(name, max_age=60) +**`exp.put(path, payload=None, params=None, timeout=10)`** -Returns a listener for events on the channel. `max_age` is the number of seconds the listener will keep events before they are discarded. +Send a PUT request. ```python - -channel = exp.get_channel('my-channel') -listener = channel.listen('my-event', max_age=30) - +document = exp.put('/api/data/cats/fluffy', { 'eyes': 'blue'}) # Insert a data value. ``` +**`exp.delete(path, payload=None, params=None, timeout=10)`** -### channel.fling(payload) - -Fling an app launch payload on the channel. +Send a DELETE request. ```python -location = exp.get_location('[uuid]') -location.get_channel().fling({ 'appTemplate' : { 'uuid': '[uuid'} }) - +exp.delete('/api/location/[uuid]') # Delete a location. ``` +## Getting a Channel -### channel.identify() +**`exp.get_channel(name, consumer=False, system=False)`** -Requests that devices listening for this event on this channel visually identify themselves. Implementation is device specific; this is simply a convience method. +Returns a [channel](#channels) with the given name and flags. ```python -location = exp.get_location('[uuid]') -location.get_channel().identify() # Tell all devices at this location to identify themselves! - +channel = exp.get_channel('my-consumer-channel', consumer=True) ``` +## API -## listener.wait(timeout=0) +**`exp.get_device(uuid=None)`** -Wait for `timeout` seconds for broadcasts. Returns a broadcast if a broadcast is in the queue or if a broadcast is received before the timeout. If timeout is reached, returns `None`. +Returns the [device](#devices) with the given uuid or `None` if no [device](#devices) could be found. -```python -channel = exp.get_channel('my-channel') -listener = channel.listen('my-event') +**`exp.create_device(document=None)`** -while True: - broadcast = listener.wait(60) - if broadcast: - print 'I got a broadcast!' +Returns a [device](#devices) created based on the supplied document. +```python +device = exp.create_device({ 'subtype': 'scala:device:player' }) ``` +**`exp.find_devices(params=None)`** -## listener.cancel() +Returns a list of [devices](#devices) matching the given query parameters. `params` is a dictionary of query parameters. -Cancels the listener. The listener is unsubscribed from broadcasts and will no longer receive messages. This cannot be undone. +**`exp.get_thing(uuid=None)`** -```python +Returns the [thing](#things) with the given uuid or `None` if no [thing](#things) could be found. -listener.cancel() -broadcast = listener.wait(60) # Will always be None +**`exp.create_thing(document=None)`** + +Returns a [thing](#things) created based on the supplied document. +```python +thing = exp.create_thing({ 'subtype': 'scala:thing:rfid', 'id': '[rfid]', 'name': 'my-rfid-tag' }) ``` +**`exp.find_things(params=None)`** -## broadcast.payload +Returns a list of [things](#things) matching the given query parameters. `params` is a dictionary of query parameters. -The payload of the broadcast. Can be any JSON serializable type. +**`exp.get_experience(uuid=None)`** -## broadcast.respond(response) +Returns the [experience](#experiences) with the given uuid or `None` if no [experience](#experiences) could be found. -Respond to the broadcast with a JSON serializable response. +**`exp.create_experience(document=None)`** -```python +Returns a [experience](#experience) created based on the supplied document. -channel = exp.get_channel('my-channel') -listener = channel.listen('my-event') +**`exp.find_experiences(params=None)`** -while True: - broadcast = listener.wait(60) - if broadcast && broadcast.payload == 'hi!': - broadcast.respond('hi back at you!') - break +Returns a list of [experiences](#experiences) matching the given query parameters. `params` is a dictionary of query parameters. -``` +**`exp.get_location(uuid=None)`** +Returns the [location](#locations) with the given uuid or `None` if no [location](#locations) could be found. +**`exp.create_location(document=None)`** +Returns a [location](#location) created based on the supplied document. +**`exp.find_locations(params=None)`** -## HTTP Requests +Returns a list of [locations](#locations) matching the given query parameters. `params` is a dictionary of query parameters. -Send custom authenticated API calls. `params` is a dictionary of url params, `payload` is a JSON serializable type, and `timeout` is the duration, in seconds, to wait for the request to complete. `path` is relative to the api host root. All methods will return a JSON serializable type. -### exp.get(path, params=None, timeout=10) +**`exp.get_feed(uuid=None)`** -Send a GET request. +Returns the [feed](#feeds) with the given uuid or `None` if no [feed](#feeds) could be found. -```python +**`exp.create_feed(document=None)`** -# Find devices by name. -result = exp.get('/api/devices', { 'name': 'my-name' }) +Returns a [feed](#feed) created based on the supplied document. +```python +feed = exp.create_feed({ 'subtype': 'scala:feed:weather', 'searchValue': '16902', 'name': 'My Weather Feed' }) ``` -### exp.post(path, payload=None, params=None, timeout=10) +**`exp.find_feeds(params=None)`** -Send a POST request. +Returns a list of [feeds](#feeds) matching the given query parameters. `params` is a dictionary of query parameters. ```python - -# Create a new experience. -document = exp.post('/api/experiences', { 'apps': [] }) - +feeds = exp.find_feeds({ 'subtype': 'scala:feed:facebook' }) ``` -### exp.patch(path, payload=None, params=None, timeout=10) +**`exp.get_data(group='default', key=None)`** -Send a PATCH request. +Returns the [data item](#data) with the given group or key or `None` if the [data item] could not be found. ```python - -# Change the name of an experience. -document = exp.patch('/api/experiences/[uuid]', { 'name': 'new-name' }) - +data = exp.get_data('cats', 'fluffy') ``` +**`exp.create_data(group='default', key=None, value=None)`** -### exp.put(path, payload=None, params=None, timeout=10) - -Send a PUT request. +Returns a [data item](#data) created based on the supplied group, key, and value. ```python - -# Insert a data value. -document = exp.put('/api/data/cats/fluffy', { 'eyes': 'blue'}) - +data = exp.create_data('cats', 'fluffy', { 'color': 'brown'}) ``` +**`exp.find_data(params=None)`** -### exp.delete(path, payload=None, params=None, timeout=10) - -Send a DELETE request. +Returns a list of [data items](#data) matching the given query parameters. `params` is a dictionary of query parameters. ```python - -# Delete a location. -exp.delete('/api/location/[uuid]') - +items = exp.find_data({ 'group': 'cats' }) ``` +**`exp.get_content(uuid=None)`** -## Resources - +Returns the [content item](#content) with the given uuid or `None` if no [content item](#content) could be found. -### exp.get_device(uuid=None) -Returns the [device](#devices) with the given uuid or `None` if no [device](#devices) could be found. +**`exp.find_content(params=None)`** -### exp.create_device(document=None) -Returns a [device](#devices) created based on the supplied document. -```python -device = exp.create_device({ 'subtype': 'scala:device:player' }) -``` +Returns a list of [content items](#content) matching the given query parameters. `params` is a dictionary of query parameters. -### exp.find_devices(params=None) -Returns a list of [devices](#devices) matching the given query parameters. `params` is a dictionary of query parameters. +## Channel +**`channel.broadcast(name, payload=None, timeout=0.1)`** -### exp.get_thing(uuid=None) -Returns the [thing](#things) with the given uuid or `None` if no [thing](#things) could be found. +Sends a [broadcast](#broadcast) on the channel with the given name and payload and returns a list of responses. `timeout` is the number of seconds to hold the request open to wait for responses. -### exp.create_thing(document=None) -Returns a [thing](#things) created based on the supplied document. ```python -thing = exp.create_thing({ 'subtype': 'scala:thing:rfid', 'id': '[rfid]', 'name': 'my-rfid-tag' }) +responses = channel.broadcast('hi!', { 'test': 'nice to meet you!' }) +[print response for response in responses] ``` -### exp.find_things(params=None) -Returns a list of [things](#things) matching the given query parameters. `params` is a dictionary of query parameters. +**`channel.listen(name, max_age=60)`** +Returns a [listener](#listener) for events on the channel. `max_age` is the number of seconds the listener will buffer events before they are discarded. +```python +channel = exp.get_channel('my-channel') +listener = channel.listen('my-event', max_age=30) +``` -### exp.get_experience(uuid=None) -Returns the [experience](#experiences) with the given uuid or `None` if no [experience](#experiences) could be found. +**`channel.fling(payload)`** -### exp.create_experience(document=None) -Returns a [experience](#experience) created based on the supplied document. +Fling an app launch payload on the channel. -### exp.find_experiences(params=None) -Returns a list of [experiences](#experiences) matching the given query parameters. `params` is a dictionary of query parameters. +```python +location = exp.get_location('[uuid]') +location.get_channel().fling({ 'appTemplate' : { 'uuid': '[uuid'} }) +``` -### exp.get_location(uuid=None) -Returns the [location](#locations) with the given uuid or `None` if no [location](#locations) could be found. +**`channel.identify()`** -### exp.create_location(document=None) -Returns a [location](#location) created based on the supplied document. +Requests that [devices](#device) listening for this event on this channel visually identify themselves. Implementation is device specific; this is simply a convience method. -### exp.find_locations(params=None) -Returns a list of [locations](#locations) matching the given query parameters. `params` is a dictionary of query parameters. +```python +location = exp.get_location('[uuid]') +location.get_channel().identify() # Tell all devices at this location to identify themselves! +``` +## Listener -## exp.get_feed(uuid=None) -Returns the [feed](#feeds) with the given uuid or `None` if no [feed](#feeds) could be found. +**`listener.wait(timeout=0)`** -**`exp.create_feed(document=None)`** -Returns a [feed](#feed) created based on the supplied document. -```python -feed = exp.create_feed({ 'subtype': 'scala:feed:weather', 'searchValue': '16902', 'name': 'My Weather Feed' }) -``` +Wait for `timeout` seconds for broadcasts. Returns a [broadcast](#broadcast) if a [broadcast](#broadcast) is in the queue or if a [broadcast](#broadcast) is received before the timeout. If timeout is reached, returns `None`. -**`exp.find_feeds(params=None)`** -Returns a list of [feeds](#feeds) matching the given query parameters. `params` is a dictionary of query parameters. ```python -feeds = exp.find_feeds({ 'subtype': 'scala:feed:facebook' }) -``` +channel = exp.get_channel('my-channel') +listener = channel.listen('my-event') +while True: + broadcast = listener.wait(60) + if broadcast: + print 'I got a broadcast!' -**`exp.get_data(group='default', key=None)`** -Returns the [data item](#data) with the given group or key or `None` if the [data item] could not be found. -```python -data = exp.get_data('cats', 'fluffy') ``` -**`exp.create_data(group='default', key=None, value=None)`** -Returns a [data item](#data) created based on the supplied group, key, and value. -```python -data = exp.create_data('cats', 'fluffy', { 'color': 'brown'}) -``` +[Broadcasts](#broadcast) are returned in the order they are received. + +**`listener.cancel()`** + +Cancels the listener. The listener is unsubscribed from [broadcasts](#broadcast) and will no longer receive messages. This cannot be undone. -**`exp.find_data(params=None)`** -Returns a list of [data items](#data) matching the given query parameters. `params` is a dictionary of query parameters. ```python -items = exp.find_data({ 'group': 'cats' }) +listener.cancel() +broadcast = listener.wait(60) # Will always be None ``` +## Broadcast -**`exp.get_content(uuid=None)`** -Returns the [content item](#content) with the given uuid or `None` if no [content item](#content) could be found. +**`broadcast.payload`** -**`exp.find_content(params=None)`** -Returns a list of [content items](#content) matching the given query parameters. `params` is a dictionary of query parameters. +The payload of the broadcast. Can be any JSON serializable type. +**`broadcast.respond(response)`** +Respond to the broadcast with a JSON serializable response. +```python +channel = exp.get_channel('my-channel') +listener = channel.listen('my-event') +while True: + broadcast = listener.wait(60) + if broadcast && broadcast.payload == 'hi!': + broadcast.respond('hi back at you!') + break +``` +## Resource -## Common Resource Methods and Attributes +These methods and attributes are shared by many of the abstract API resources. **`resource.uuid`** + The uuid of the resource. Cannot be set. Maps to `resource.document['uuid']` **`resource.name`** + The name of the resource. Can be set directly. Maps to `resource.document['name']`. **`resource.document`** + The resource's underlying document **`resource.save()`** @@ -422,150 +393,159 @@ The resource's underlying document Saves the resource and updates the document in place. ```python - device = exp.get_device('[uuid]') device.name = 'my-new-name' -device.save() -# device changes are now saved - +device.save() # device changes are now saved ``` - **`resource.refresh()`** Refreshes the resource's underlying document in place. ```python - device = exp.create_device() device.name = 'new-name' device_2 = exp.get_device(device.uuid) device.save() device_2.refresh() print device_2.name # 'new-name' - ``` - **`resource.get_channel(system=False, consumer=False)`** Returns the channel whose name is contextually associated with this resource. ```python - channel = experience.get_channel() channel.broadcast('hello?') - ``` - - - - - - -## Devices -Devices inherit all [common resource methods and attributes](#common-resource-methods-and-attributes). +## Device +Devices inherit all [common resource methods and attributes](#resource). **`device.get_location()`** -Returns the device's [location](#locations) or `None`. + +Returns the device's [location](#location) or `None`. **`device.get_zones()`** -Returns a list of the device's [zones](#zones). + +Returns a list of the device's [zones](#zone). **`device.get_experience()`** -Returns the device's [experience](#experiences) or `None` +Returns the device's [experience](#experience) or `None` -## Things -Things inherit all [common resource methods and attributes](#common-resource-methods-and-attributes). + +## Thing +Things inherit all [common resource methods and attributes](#resource). **`thing.get_location()`** -Returns the device's [location](#locations) or `None`. + +Returns the thing's [location](#location) or `None`. **`thing.get_zones()`** -Returns a list of the thing's [#zones](#zones). + +Returns a list of the thing's [#zones](#zone). **`thing.get_experience()`** -Returns the device's [experience](#experiences) or `None` + +Returns the device's [experience](#experience) or `None` -### Experiences -Experiences inherit all [common resource methods and attributes](#common-resource-methods-and-attributes). +### Experience +Experiences inherit all [common resource methods and attributes](#resource). **`experience.get_devices()`** -Returns a list of devices that are part of this experience. + +Returns a list of [devices](#device) that are part of this experience. ### Locations -Locations inherit all [common resource methods and attributes](#common-resource-methods-and-attributes). +Locations inherit all [common resource methods and attributes](#resource). **`location.get_devices()`** -Returns a list of [devices](#devices) that are part of this location. + +Returns a list of [devices](#device) that are part of this location. **`location.get_things()`** -Returns a list of [devices](#devices) that are part of this location. + +Returns a list of [devices](#device) that are part of this location. **`location.get_zones()`** -Returns a list of [zones](#zones) that are part of this location. + +Returns a list of [zones](#zone) that are part of this location. **`location.get_layout_url()`** + Returns a url pointing to the location's layout image. ### Zones -Zones inherit the [common resource methods and attributes](#common-resource-methods-and-attributes) `save()`, `refresh()`, and `get_channel()`. +Zones inherit the [common resource methods and attributes](#resource) `save()`, `refresh()`, and `get_channel()`. **`zone.key`** + The zone's key. **`zone.name`** + The zone's name. **`zone.get_devices()`** -Returns all [devices](#devices) that are members of this zone. + +Returns all [devices](#device) that are members of this zone. **`zone.get_things()`** -Returns all [things](#things) that are members of this zone. + +Returns all [things](#thing) that are members of this zone. **`zone.get_location()`** -Returns the zone's [location](#locations) + +Returns the zone's [location](#location) ## Feeds -Feeds inherit all [common resource methods and attributes](#common-resource-methods-and-attributes). +Feeds inherit all [common resource methods and attributes](#resource). **`feed.get_data()`** + Returns the feed's data. ## Data -Data items inherit the [common resource methods and attributes](#common-resource-methods-and-attributes) `save()`, `refresh()`, and `get_channel()`. +Data items inherit the [common resource methods and attributes](#resource) `save()`, `refresh()`, and `get_channel()`. **`data.key`** + The data item's key. Settable. **`data.group`** + The data item's group. Settable **`data.value`** + The data item's value. Settable. ## Content -Content items inherit all [common resource methods and attributes](#common-resource-methods-and-attributes) except `save()`. +Content items inherit all [common resource methods and attributes](#resource) except `save()`. **`content.subtype`** + The content item's subtype. Not settable. **`content.get_url()`** + Returns the delivery url for this content item. **`content.has_variant(name)`** + Returns a boolean indicating whether or not this content item has a variant with the given name. **`content.get_variant_url(https://github.com/name)`** + Returns the delivery url for a variant of this content item. From 985769cf760aff9fe33b802a7aa0d5efb5f938e3 Mon Sep 17 00:00:00 2001 From: James Dalessio Date: Mon, 28 Mar 2016 15:20:01 -0400 Subject: [PATCH 048/104] Add excetpions. --- README.md | 34 ++++++++++++++++++++++++++-------- 1 file changed, 26 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 0be6ecf..0bd9a52 100644 --- a/README.md +++ b/README.md @@ -42,7 +42,7 @@ enable_network | ```True``` | Whether to enable real time network communication. ## Runtime -**`exp_sdk.start(**options)`** +**`exp_sdk.start(options)`** Starts and returns an sdk instance. Can be called multiple times to start multiple independent instances of the sdk. The sdk can be started using user, device, or consumer app credentials.`**options` supports the following keyword arguments: @@ -554,13 +554,31 @@ Returns the delivery url for a variant of this content item. ## Exceptions - | Description - --- | --- - `exp_sdk.ExpError` | Base class for all EXP exceptions. - `exp_sdk.UnexpectedError` | Raised when an unexpected error occurs. - `exp_sdk.RuntimeError` | Raised when [startup options](#startup-options) are incorrect or inconsistent. - `exp_sdk.AuthenticationError` | Raised when the sdk cannot authenticate due to bad credentials. - `exp_sdk.ApiError` | Raised when an API call fails. Has properties `message` and `code`. See the [API documentation](#https://docs.goexp.io). + **`exp_sdk.ExpError`** + + Base class for all EXP exceptions. + + --- + + **`exp_sdk.UnexpectedError`** + + Raised when an unexpected error occurs. + + --- + **`exp_sdk.RuntimeError`** + + Raised when [startup options](#runtime) are incorrect or inconsistent. + + --- + **`exp_sdk.AuthenticationError`** + + Raised when the sdk cannot authenticate due to bad credentials. + + --- + + **`exp_sdk.ApiError`** + + Raised when an API call fails. Has properties `message` and `code`. From 292e253aa7acbfc64109291b2e32fd99e152c1c2 Mon Sep 17 00:00:00 2001 From: James Dalessio Date: Mon, 28 Mar 2016 15:50:46 -0400 Subject: [PATCH 049/104] update docs --- README.md | 168 +++++------------------------------------------------- 1 file changed, 14 insertions(+), 154 deletions(-) diff --git a/README.md b/README.md index 0bd9a52..72f651b 100644 --- a/README.md +++ b/README.md @@ -1,44 +1,6 @@ - - -# Getting Started - -The SDK is started by calling ```exp.start``` and specifying your credentials and configuration options as keyword arguments. ```exp.start``` will start additional threads to process network events. You may supply user, device, or consumer app credentials. You can also authenticate in pairing mode. When ```exp.start``` returns, you are authenticated and can begin using the SDK. - -Users must specify their ```username```, ```password```, and ```organization``` as keyword arguments to ```exp.start```. - -```python -exp.start(username='joe@joemail.com', password='JoeRocks42', organization='joesorg') -``` - -Devices must specify their ```uuid``` and ```secret``` as keyword arguments. - -```python -exp.start(uuid='[uuid]', secret='[secret]') -``` - -Consumer apps must specify their ```uuid``` and ```api_key``` as keyword arguments. - -```python -exp.start(uuid='[uuid]', api_key='[api key]') -``` - -Advanced users can authenticate in pairing mode by setting ```allow_pairing``` to ```False```. - -```python -exp.start(allow_pairing=False) -``` - -Additional options: - -Name | Default | Description ---- | --- | --- -host | ```'https://api.goexp.io/'``` | The api server to authenticate with. -enable_network | ```True``` | Whether to enable real time network communication. If set to ```False``` you will be unable to listen on the [EXP network](# Communicating on the EXP Network). - - # Python SDK Reference v1.0.0 -## Getting Started +## Installation ## Runtime @@ -191,11 +153,11 @@ Returns a list of [devices](#devices) matching the given query parameters. `para **`exp.get_thing(uuid=None)`** -Returns the [thing](#things) with the given uuid or `None` if no [thing](#things) could be found. +Returns the [thing](#thing) with the given uuid or `None` if no [thing](#things) could be found. **`exp.create_thing(document=None)`** -Returns a [thing](#things) created based on the supplied document. +Returns a [thing](#thing) created based on the supplied document. ```python thing = exp.create_thing({ 'subtype': 'scala:thing:rfid', 'id': '[rfid]', 'name': 'my-rfid-tag' }) @@ -203,11 +165,11 @@ thing = exp.create_thing({ 'subtype': 'scala:thing:rfid', 'id': '[rfid]', 'name' **`exp.find_things(params=None)`** -Returns a list of [things](#things) matching the given query parameters. `params` is a dictionary of query parameters. +Returns a list of [things](#thing) matching the given query parameters. `params` is a dictionary of query parameters. **`exp.get_experience(uuid=None)`** -Returns the [experience](#experiences) with the given uuid or `None` if no [experience](#experiences) could be found. +Returns the [experience](#experience) with the given uuid or `None` if no [experience](#experience) could be found. **`exp.create_experience(document=None)`** @@ -215,11 +177,11 @@ Returns a [experience](#experience) created based on the supplied document. **`exp.find_experiences(params=None)`** -Returns a list of [experiences](#experiences) matching the given query parameters. `params` is a dictionary of query parameters. +Returns a list of [experiences](#experience) matching the given query parameters. `params` is a dictionary of query parameters. **`exp.get_location(uuid=None)`** -Returns the [location](#locations) with the given uuid or `None` if no [location](#locations) could be found. +Returns the [location](#location) with the given uuid or `None` if no [location](#location) could be found. **`exp.create_location(document=None)`** @@ -227,12 +189,12 @@ Returns a [location](#location) created based on the supplied document. **`exp.find_locations(params=None)`** -Returns a list of [locations](#locations) matching the given query parameters. `params` is a dictionary of query parameters. +Returns a list of [locations](#location) matching the given query parameters. `params` is a dictionary of query parameters. **`exp.get_feed(uuid=None)`** -Returns the [feed](#feeds) with the given uuid or `None` if no [feed](#feeds) could be found. +Returns the [feed](#feed) with the given uuid or `None` if no [feed](#feed) could be found. **`exp.create_feed(document=None)`** @@ -244,7 +206,7 @@ feed = exp.create_feed({ 'subtype': 'scala:feed:weather', 'searchValue': '16902' **`exp.find_feeds(params=None)`** -Returns a list of [feeds](#feeds) matching the given query parameters. `params` is a dictionary of query parameters. +Returns a list of [feeds](#feed) matching the given query parameters. `params` is a dictionary of query parameters. ```python feeds = exp.find_feeds({ 'subtype': 'scala:feed:facebook' }) @@ -367,7 +329,7 @@ listener = channel.listen('my-event') while True: broadcast = listener.wait(60) - if broadcast && broadcast.payload == 'hi!': + if broadcast and broadcast.payload == 'hi!': broadcast.respond('hi back at you!') break ``` @@ -461,7 +423,7 @@ Experiences inherit all [common resource methods and attributes](#resource). Returns a list of [devices](#device) that are part of this experience. -### Locations +## Location Locations inherit all [common resource methods and attributes](#resource). **`location.get_devices()`** @@ -481,7 +443,7 @@ Returns a list of [zones](#zone) that are part of this location. Returns a url pointing to the location's layout image. -### Zones +## Zone Zones inherit the [common resource methods and attributes](#resource) `save()`, `refresh()`, and `get_channel()`. **`zone.key`** @@ -505,7 +467,7 @@ Returns all [things](#thing) that are members of this zone. Returns the zone's [location](#location) -## Feeds +## Feed Feeds inherit all [common resource methods and attributes](#resource). **`feed.get_data()`** @@ -581,105 +543,3 @@ Returns the delivery url for a variant of this content item. Raised when an API call fails. Has properties `message` and `code`. - - -# Examples - -## Creating a Device and Listening for Updates - -Updates to API resources are sent out over a system channel with the event name "update". - -```python - device = exp.create_device({ 'name': 'my_sweet_device' }) - device.save() - channel = device.get_channel(system=True) - listener = channel.listen('update') - while True: - if listener.wait(5): - print 'The device was updated!' - -``` - - -## Modifying a Resource in Place - -```python -experience = exp.get_experience('[uuid]') -experience.document['name'] = 'new name' -experience.save() -``` - - -## Using The EXP Network - -The EXP network facilitates real time communication between entities connected to EXP. A user or device can broadcast a JSON serializable payload to users and devices in your organization, and listeners to those broadcasts can respond to the broadcasters. - -### Channels - -All messages on the EXP network are sent over a channel. Channels have a name, and two flags: ```system``` and ```consumer```. - -```python -channel = exp.get_channel('my_channel', system=False, consumer=False) -``` - -Use ```system=True``` to get a system channel. You cannot send messages on a system channels but can listen for system notifications, such as updates to API resources. - -Use ```consumer=True``` to get a consumer channel. Consumer devices can only listen or broadcast on consumer channels. When ```consumer=False``` you will not receive consumer device broadcasts and consumer devices will not be able to hear your broadcasts. - -Both ```system``` and ```consumer``` default to ```False```. Consumer devices will be unable to broadcast or listen to messages on non-consumer channels. - - -### Broadcasting - -Use the broadcast method of a channel object to send a named message containing an optional JSON serializable payload to other entities on the EXP network. You can optionally include a timeout to wait for responses to the broadcast. The broadcast will block for approximately the given timeout and return a ```list``` of response payloads. Each response payload can any JSON serializable type. - -```python - -channel = exp.get_channel('my_channel') -responses = channel.broadcast(name='my_event', timeout=5, payload='hello') -[print response for response in responses] - -``` - - -### Listening - -To listen for broadcasts, call the listen method of a channel object and pass in the name of the event you wish to listen for. When EXP listener has been registered and can start receiving events, a ```listener``` object will be returned. - -Call the ```wait``` method of a listener to block until a broadcast is received, passing in a timeout in seconds. If ```timeout``` elaspes and no broadcasts have been received, ```wait``` will return ```None```. - -Once a listener is created, it will receive broadcasts in a background thread even when not waiting. Calling ```wait``` will first attempt to return the oldest broadcast in the queue. Queued broadcasts will be discarded after ~60s if not retrieved during a ```wait```. - -```python - -channel = exp.get_channel('my_channel') -listener = channel.listen('my_event') - -while True: - broadcast = listener.wait(5) - if broadcast: - print 'Message received!' - print broadcast.payload - listener.cancel() - break - -``` - - -### Responding - -To respond to broadcast, call the respond method on the broadcast object, optionally passing in a JSON serializable response payload. - -```python - -channel = exp.get_channel('my_channel') -listener = channel.listen(name='my_event') - -while True: - broadcast = listener.wait(5) - if broadcast and broadcast.payload is 'hello': - print 'Responding to broadcast.' - broadcast.respond('Nice to meet you!') - -``` - From e8241d084acefd2caa21a222ef0f799362d0d96c Mon Sep 17 00:00:00 2001 From: James Dalessio Date: Mon, 28 Mar 2016 16:50:09 -0400 Subject: [PATCH 050/104] docs --- README.md | 454 ++++++++++++++++++++++++++---------------------------- 1 file changed, 220 insertions(+), 234 deletions(-) diff --git a/README.md b/README.md index 72f651b..a5d9333 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,8 @@ -# Python SDK Reference v1.0.0 -## Installation -## Runtime +# Installation + +# Runtime **`exp_sdk.start(options)`** @@ -77,53 +77,34 @@ Returns the up to date authentication payload. The authentication payload may be print 'My authentication token is : %s' % exp.get_auth()['token'] ``` +# Exceptions -## Custom HTTP Requests - -These methods all users to send custom authenticated API calls. `params` is a dictionary of url params, `payload` is a JSON serializable type, and `timeout` is the duration, in seconds, to wait for the request to complete. `path` is relative to the api host root. All methods will return a JSON serializable type. - -**`exp.get(path, params=None, timeout=10)`** - -Send a GET request. - -```python -result = exp.get('/api/devices', { 'name': 'my-name' }) # Find devices by name. -``` - -**`exp.post(path, payload=None, params=None, timeout=10)`** - -Send a POST request. - -```python -document = exp.post('/api/experiences', {}) # Create a new empty experience. -``` - -**`exp.patch(path, payload=None, params=None, timeout=10)`** - -Send a PATCH request. - -```python -document = exp.patch('/api/experiences/[uuid]', { 'name': 'new-name' }) # Rename an experience. -``` - - -**`exp.put(path, payload=None, params=None, timeout=10)`** + **`exp_sdk.ExpError`** -Send a PUT request. + Base class for all EXP exceptions. + + **`exp_sdk.UnexpectedError`** + + Raised when an unexpected error occurs. + + **`exp_sdk.RuntimeError`** + + Raised when [startup options](#runtime) are incorrect or inconsistent. + + **`exp_sdk.AuthenticationError`** + + Raised when the sdk cannot authenticate due to bad credentials. + + **`exp_sdk.ApiError`** + + Raised when an API call fails. Has properties `message` and `code`. -```python -document = exp.put('/api/data/cats/fluffy', { 'eyes': 'blue'}) # Insert a data value. -``` -**`exp.delete(path, payload=None, params=None, timeout=10)`** -Send a DELETE request. -```python -exp.delete('/api/location/[uuid]') # Delete a location. -``` +# Network -## Getting a Channel +## Channels **`exp.get_channel(name, consumer=False, system=False)`** @@ -133,121 +114,6 @@ Returns a [channel](#channels) with the given name and flags. channel = exp.get_channel('my-consumer-channel', consumer=True) ``` -## API - -**`exp.get_device(uuid=None)`** - -Returns the [device](#devices) with the given uuid or `None` if no [device](#devices) could be found. - -**`exp.create_device(document=None)`** - -Returns a [device](#devices) created based on the supplied document. - -```python -device = exp.create_device({ 'subtype': 'scala:device:player' }) -``` - -**`exp.find_devices(params=None)`** - -Returns a list of [devices](#devices) matching the given query parameters. `params` is a dictionary of query parameters. - -**`exp.get_thing(uuid=None)`** - -Returns the [thing](#thing) with the given uuid or `None` if no [thing](#things) could be found. - -**`exp.create_thing(document=None)`** - -Returns a [thing](#thing) created based on the supplied document. - -```python -thing = exp.create_thing({ 'subtype': 'scala:thing:rfid', 'id': '[rfid]', 'name': 'my-rfid-tag' }) -``` - -**`exp.find_things(params=None)`** - -Returns a list of [things](#thing) matching the given query parameters. `params` is a dictionary of query parameters. - -**`exp.get_experience(uuid=None)`** - -Returns the [experience](#experience) with the given uuid or `None` if no [experience](#experience) could be found. - -**`exp.create_experience(document=None)`** - -Returns a [experience](#experience) created based on the supplied document. - -**`exp.find_experiences(params=None)`** - -Returns a list of [experiences](#experience) matching the given query parameters. `params` is a dictionary of query parameters. - -**`exp.get_location(uuid=None)`** - -Returns the [location](#location) with the given uuid or `None` if no [location](#location) could be found. - -**`exp.create_location(document=None)`** - -Returns a [location](#location) created based on the supplied document. - -**`exp.find_locations(params=None)`** - -Returns a list of [locations](#location) matching the given query parameters. `params` is a dictionary of query parameters. - - -**`exp.get_feed(uuid=None)`** - -Returns the [feed](#feed) with the given uuid or `None` if no [feed](#feed) could be found. - -**`exp.create_feed(document=None)`** - -Returns a [feed](#feed) created based on the supplied document. - -```python -feed = exp.create_feed({ 'subtype': 'scala:feed:weather', 'searchValue': '16902', 'name': 'My Weather Feed' }) -``` - -**`exp.find_feeds(params=None)`** - -Returns a list of [feeds](#feed) matching the given query parameters. `params` is a dictionary of query parameters. - -```python -feeds = exp.find_feeds({ 'subtype': 'scala:feed:facebook' }) -``` - - -**`exp.get_data(group='default', key=None)`** - -Returns the [data item](#data) with the given group or key or `None` if the [data item] could not be found. - -```python -data = exp.get_data('cats', 'fluffy') -``` - -**`exp.create_data(group='default', key=None, value=None)`** - -Returns a [data item](#data) created based on the supplied group, key, and value. - -```python -data = exp.create_data('cats', 'fluffy', { 'color': 'brown'}) -``` - -**`exp.find_data(params=None)`** - -Returns a list of [data items](#data) matching the given query parameters. `params` is a dictionary of query parameters. - -```python -items = exp.find_data({ 'group': 'cats' }) -``` - -**`exp.get_content(uuid=None)`** - -Returns the [content item](#content) with the given uuid or `None` if no [content item](#content) could be found. - -**`exp.find_content(params=None)`** - -Returns a list of [content items](#content) matching the given query parameters. `params` is a dictionary of query parameters. - - -## Channel - **`channel.broadcast(name, payload=None, timeout=0.1)`** Sends a [broadcast](#broadcast) on the channel with the given name and payload and returns a list of responses. `timeout` is the number of seconds to hold the request open to wait for responses. @@ -280,16 +146,12 @@ location.get_channel().fling({ 'appTemplate' : { 'uuid': '[uuid'} }) Requests that [devices](#device) listening for this event on this channel visually identify themselves. Implementation is device specific; this is simply a convience method. -```python -location = exp.get_location('[uuid]') -location.get_channel().identify() # Tell all devices at this location to identify themselves! -``` -## Listener +## Listeners **`listener.wait(timeout=0)`** -Wait for `timeout` seconds for broadcasts. Returns a [broadcast](#broadcast) if a [broadcast](#broadcast) is in the queue or if a [broadcast](#broadcast) is received before the timeout. If timeout is reached, returns `None`. +Wait for `timeout` seconds for broadcasts. Returns a [broadcast](#broadcasts) if a [broadcast](#broadcasts) is in the queue or if a [broadcast](#broadcasts) is received before the timeout. If timeout is reached, returns `None`. ```python channel = exp.get_channel('my-channel') @@ -299,21 +161,15 @@ while True: broadcast = listener.wait(60) if broadcast: print 'I got a broadcast!' - ``` -[Broadcasts](#broadcast) are returned in the order they are received. +[Broadcasts](#broadcasts) are returned in the order they are received. **`listener.cancel()`** Cancels the listener. The listener is unsubscribed from [broadcasts](#broadcast) and will no longer receive messages. This cannot be undone. -```python -listener.cancel() -broadcast = listener.wait(60) # Will always be None -``` - -## Broadcast +## Broadcasts **`broadcast.payload`** @@ -334,17 +190,66 @@ while True: break ``` -## Resource + +# API + +## Custom HTTP Requests + +These methods all users to send custom authenticated API calls. `params` is a dictionary of url params, `payload` is a JSON serializable type, and `timeout` is the duration, in seconds, to wait for the request to complete. `path` is relative to the api host root. All methods will return a JSON serializable type. + +**`exp.get(path, params=None, timeout=10)`** + +Send a GET request. + +```python +result = exp.get('/api/devices', { 'name': 'my-name' }) # Find devices by name. +``` + +**`exp.post(path, payload=None, params=None, timeout=10)`** + +Send a POST request. + +```python +document = exp.post('/api/experiences', {}) # Create a new empty experience. +``` + +**`exp.patch(path, payload=None, params=None, timeout=10)`** + +Send a PATCH request. + +```python +document = exp.patch('/api/experiences/[uuid]', { 'name': 'new-name' }) # Rename an experience. +``` + + +**`exp.put(path, payload=None, params=None, timeout=10)`** + +Send a PUT request. + +```python +document = exp.put('/api/data/cats/fluffy', { 'eyes': 'blue'}) # Insert a data value. +``` + +**`exp.delete(path, payload=None, params=None, timeout=10)`** + +Send a DELETE request. + +```python +exp.delete('/api/location/[uuid]') # Delete a location. +``` + + +## Resources These methods and attributes are shared by many of the abstract API resources. **`resource.uuid`** -The uuid of the resource. Cannot be set. Maps to `resource.document['uuid']` +The uuid of the resource. Cannot be set. **`resource.name`** -The name of the resource. Can be set directly. Maps to `resource.document['name']`. +The name of the resource. Can be set directl **`resource.document`** @@ -383,68 +288,128 @@ channel.broadcast('hello?') ``` -## Device -Devices inherit all [common resource methods and attributes](#resource). +## Devices + +Devices inherit all [common resource methods and attributes](#resources). + +**`exp.get_device(uuid=None)`** + +Returns the device with the given uuid or `None` if no device could be found. + +**`exp.create_device(document=None)`** + +Returns a device created based on the supplied document. + +```python +device = exp.create_device({ 'subtype': 'scala:device:player' }) +``` + +**`exp.find_devices(params=None)`** + +Returns a list of devices matching the given query parameters. `params` is a dictionary of query parameters. **`device.get_location()`** -Returns the device's [location](#location) or `None`. +Returns the device's [location](#locations) or `None`. **`device.get_zones()`** -Returns a list of the device's [zones](#zone). +Returns a list of the device's [zones](#zones). **`device.get_experience()`** -Returns the device's [experience](#experience) or `None` +Returns the device's [experience](#experiences) or `None` -## Thing -Things inherit all [common resource methods and attributes](#resource). +## Things + +Things inherit all [common resource methods and attributes](#resources). + +**`exp.get_thing(uuid=None)`** + +Returns the thing with the given uuid or `None` if no things could be found. + +**`exp.create_thing(document=None)`** + +Returns a thing created based on the supplied document. + +```python +thing = exp.create_thing({ 'subtype': 'scala:thing:rfid', 'id': '[rfid]', 'name': 'my-rfid-tag' }) +``` + +**`exp.find_things(params=None)`** + +Returns a list of things matching the given query parameters. `params` is a dictionary of query parameters. **`thing.get_location()`** -Returns the thing's [location](#location) or `None`. +Returns the thing's [location](#locations) or `None`. **`thing.get_zones()`** -Returns a list of the thing's [#zones](#zone). +Returns a list of the thing's [#zones](#zones). **`thing.get_experience()`** -Returns the device's [experience](#experience) or `None` +Returns the device's [experience](#experiences) or `None` + +## Experiences -### Experience -Experiences inherit all [common resource methods and attributes](#resource). +Experiences inherit all [common resource methods and attributes](#resources). + +**`exp.get_experience(uuid=None)`** + +Returns the experience with the given uuid or `None` if no experience could be found. + +**`exp.create_experience(document=None)`** + +Returns an experience created based on the supplied document. + +**`exp.find_experiences(params=None)`** + +Returns a list of experiences matching the given query parameters. `params` is a dictionary of query parameters. **`experience.get_devices()`** -Returns a list of [devices](#device) that are part of this experience. +Returns a list of [devices](#devices) that are part of this experience. -## Location -Locations inherit all [common resource methods and attributes](#resource). +## Locations +Locations inherit all [common resource methods and attributes](#resources). + +**`exp.get_location(uuid=None)`** + +Returns the location with the given uuid or `None` if no location could be found. + +**`exp.create_location(document=None)`** + +Returns a location created based on the supplied document. + +**`exp.find_locations(params=None)`** + +Returns a list of locations matching the given query parameters. `params` is a dictionary of query parameters. + **`location.get_devices()`** -Returns a list of [devices](#device) that are part of this location. +Returns a list of [devices](#devices) that are part of this location. **`location.get_things()`** -Returns a list of [devices](#device) that are part of this location. +Returns a list of [things](#things) that are part of this location. **`location.get_zones()`** -Returns a list of [zones](#zone) that are part of this location. +Returns a list of [zones](#zones) that are part of this location. **`location.get_layout_url()`** Returns a url pointing to the location's layout image. -## Zone -Zones inherit the [common resource methods and attributes](#resource) `save()`, `refresh()`, and `get_channel()`. +## Zones +Zones inherit the [common resource methods and attributes](#resources) `save()`, `refresh()`, and `get_channel()`. **`zone.key`** @@ -456,19 +421,39 @@ The zone's name. **`zone.get_devices()`** -Returns all [devices](#device) that are members of this zone. +Returns all [devices](#devices) that are members of this zone. **`zone.get_things()`** -Returns all [things](#thing) that are members of this zone. +Returns all [things](#things) that are members of this zone. **`zone.get_location()`** -Returns the zone's [location](#location) +Returns the zone's [location](#locations) -## Feed -Feeds inherit all [common resource methods and attributes](#resource). +## Feeds +Feeds inherit all [common resource methods and attributes](#resources). + +**`exp.get_feed(uuid=None)`** + +Returns the feed with the given uuid or `None` if no feed could be found. + +**`exp.create_feed(document=None)`** + +Returns a feed created based on the supplied document. + +```python +feed = exp.create_feed({ 'subtype': 'scala:feed:weather', 'searchValue': '16902', 'name': 'My Weather Feed' }) +``` + +**`exp.find_feeds(params=None)`** + +Returns a list of feeds matching the given query parameters. `params` is a dictionary of query parameters. + +```python +feeds = exp.find_feeds({ 'subtype': 'scala:feed:facebook' }) +``` **`feed.get_data()`** @@ -476,7 +461,32 @@ Returns the feed's data. ## Data -Data items inherit the [common resource methods and attributes](#resource) `save()`, `refresh()`, and `get_channel()`. + +Data items inherit the [common resource methods and attributes](#resources) `save()`, `refresh()`, and `get_channel()`. + +**`exp.get_data(group='default', key=None)`** + +Returns the data item with the given group or key or `None` if the data item could not be found. + +```python +data = exp.get_data('cats', 'fluffy') +``` + +**`exp.create_data(group='default', key=None, value=None)`** + +Returns a data item created based on the supplied group, key, and value. + +```python +data = exp.create_data('cats', 'fluffy', { 'color': 'brown'}) +``` + +**`exp.find_data(params=None)`** + +Returns a list of data items matching the given query parameters. `params` is a dictionary of query parameters. + +```python +items = exp.find_data({ 'group': 'cats' }) +``` **`data.key`** @@ -492,7 +502,15 @@ The data item's value. Settable. ## Content -Content items inherit all [common resource methods and attributes](#resource) except `save()`. +Content items inherit all [common resource methods and attributes](#resources) except `save()`. + +**`exp.get_content(uuid=None)`** + +Returns the content item with the given uuid or `None` if no content item could be found. + +**`exp.find_content(params=None)`** + +Returns a list of content items matching the given query parameters. `params` is a dictionary of query parameters. **`content.subtype`** @@ -511,35 +529,3 @@ Returns a boolean indicating whether or not this content item has a variant with Returns the delivery url for a variant of this content item. - - - -## Exceptions - - **`exp_sdk.ExpError`** - - Base class for all EXP exceptions. - - --- - - **`exp_sdk.UnexpectedError`** - - Raised when an unexpected error occurs. - - --- - **`exp_sdk.RuntimeError`** - - Raised when [startup options](#runtime) are incorrect or inconsistent. - - --- - **`exp_sdk.AuthenticationError`** - - Raised when the sdk cannot authenticate due to bad credentials. - - --- - - **`exp_sdk.ApiError`** - - Raised when an API call fails. Has properties `message` and `code`. - - From a91853a3fb00138d2e25a899daeaa711aafc6359 Mon Sep 17 00:00:00 2001 From: James Dalessio Date: Mon, 28 Mar 2016 17:05:46 -0400 Subject: [PATCH 051/104] Preparing for pypi. --- LICENSE.txt | 7 +++++++ {exp => exp_sdk}/__init__.py | 0 {exp => exp_sdk}/api.py | 0 {exp => exp_sdk}/authenticator.py | 0 {exp => exp_sdk}/exceptions.py | 0 {exp => exp_sdk}/exp.py | 0 {exp => exp_sdk}/network.py | 0 setup.cfg | 2 ++ setup.py | 16 +++++++++++----- tests/utils.py | 4 ++-- 10 files changed, 22 insertions(+), 7 deletions(-) create mode 100644 LICENSE.txt rename {exp => exp_sdk}/__init__.py (100%) rename {exp => exp_sdk}/api.py (100%) rename {exp => exp_sdk}/authenticator.py (100%) rename {exp => exp_sdk}/exceptions.py (100%) rename {exp => exp_sdk}/exp.py (100%) rename {exp => exp_sdk}/network.py (100%) create mode 100644 setup.cfg diff --git a/LICENSE.txt b/LICENSE.txt new file mode 100644 index 0000000..3b8dd20 --- /dev/null +++ b/LICENSE.txt @@ -0,0 +1,7 @@ +Copyright (c) 2016 Scala Inc. + +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. \ No newline at end of file diff --git a/exp/__init__.py b/exp_sdk/__init__.py similarity index 100% rename from exp/__init__.py rename to exp_sdk/__init__.py diff --git a/exp/api.py b/exp_sdk/api.py similarity index 100% rename from exp/api.py rename to exp_sdk/api.py diff --git a/exp/authenticator.py b/exp_sdk/authenticator.py similarity index 100% rename from exp/authenticator.py rename to exp_sdk/authenticator.py diff --git a/exp/exceptions.py b/exp_sdk/exceptions.py similarity index 100% rename from exp/exceptions.py rename to exp_sdk/exceptions.py diff --git a/exp/exp.py b/exp_sdk/exp.py similarity index 100% rename from exp/exp.py rename to exp_sdk/exp.py diff --git a/exp/network.py b/exp_sdk/network.py similarity index 100% rename from exp/network.py rename to exp_sdk/network.py diff --git a/setup.cfg b/setup.cfg new file mode 100644 index 0000000..224a779 --- /dev/null +++ b/setup.cfg @@ -0,0 +1,2 @@ +[metadata] +description-file = README.md \ No newline at end of file diff --git a/setup.py b/setup.py index ae3cf3d..d88fe1d 100644 --- a/setup.py +++ b/setup.py @@ -1,9 +1,15 @@ -from setuptools import setup, find_packages +from setuptools import setup setup( - name="exp_sdk", - version="0.0.0", - packages=find_packages(), - install_requires=["requests", "socketIO_client"] + name='exp_sdk', + pacakges= ['exp_sdk'] + version='1.0.0-beta1', + description='EXP Python SDK', + author='Scala', + author_email='james.dalessio@scala.com', + url='https://github.com/scalainc/exp-python2-sdk', + download_url='https://github.com/scalainc/exp-python2-sdk/tarball/1.0.0-beta1', + install_requires=["requests", "socketIO_client"], + keywords=['scala', 'exp', 'sdk', 'signage'] ) diff --git a/tests/utils.py b/tests/utils.py index 39abe56..d675907 100644 --- a/tests/utils.py +++ b/tests/utils.py @@ -1,9 +1,9 @@ -import exp as exp_sdk +import exp_sdk import string import random -from exp import api +from exp_sdk import api class Base (object): From c82c1257dab3b1173c809902a84a04ea04f1b937 Mon Sep 17 00:00:00 2001 From: James Dalessio Date: Mon, 28 Mar 2016 17:09:21 -0400 Subject: [PATCH 052/104] Update tarball url. --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index d88fe1d..3307c1c 100644 --- a/setup.py +++ b/setup.py @@ -9,7 +9,7 @@ author='Scala', author_email='james.dalessio@scala.com', url='https://github.com/scalainc/exp-python2-sdk', - download_url='https://github.com/scalainc/exp-python2-sdk/tarball/1.0.0-beta1', + download_url='https://github.com/scalainc/exp-python2-sdk/tarball/v1.0.0-beta1', install_requires=["requests", "socketIO_client"], keywords=['scala', 'exp', 'sdk', 'signage'] ) From 7a6ec5440b16f9d64fac532beef0b2343405b054 Mon Sep 17 00:00:00 2001 From: James Dalessio Date: Mon, 28 Mar 2016 17:48:01 -0400 Subject: [PATCH 053/104] Preparing for pypi --- .gitignore | 1 + bin/setup-ci-tests.sh | 21 +++++++++++++++++++++ circle.yml | 5 ++++- exp_sdk/api.py | 2 +- setup.py | 10 +++++++--- tests/test_auth.py | 1 - tests/test_locations.py | 4 ++-- tests/test_network.py | 5 ++--- tests/test_start.py | 1 - 9 files changed, 38 insertions(+), 12 deletions(-) create mode 100644 bin/setup-ci-tests.sh diff --git a/.gitignore b/.gitignore index 83658ec..119d8cc 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ *.pyc *.log +dist \ No newline at end of file diff --git a/bin/setup-ci-tests.sh b/bin/setup-ci-tests.sh new file mode 100644 index 0000000..640fced --- /dev/null +++ b/bin/setup-ci-tests.sh @@ -0,0 +1,21 @@ + +set -e + +cd .. +git clone git@github.com:scalainc/exp-api.git +cd exp-api +git checkout develop +git pull origin develop +npm install +NODE_ENV=test npm start& +sleep 10 +cd .. +git clone git@github.com:scalainc/exp-network.git +cd exp-network +git checkout develop +git pull origin develop +npm install +npm start& +sleep 10 +cd ../exp-js-sdk +npm run build diff --git a/circle.yml b/circle.yml index 89a1dd2..9a72c94 100644 --- a/circle.yml +++ b/circle.yml @@ -1,6 +1,9 @@ machine: python: version: 2.7.10 + node: + version: 4.2.3 + dependencies: pre: @@ -9,4 +12,4 @@ dependencies: test: override: - - nosetests \ No newline at end of file + - chmod +x ./bin/setup-ci-tests.sh && ./bin/setup-ci-tests.sh && nosetests diff --git a/exp_sdk/api.py b/exp_sdk/api.py index 9c5b5a9..d1660d2 100644 --- a/exp_sdk/api.py +++ b/exp_sdk/api.py @@ -244,7 +244,7 @@ def key(self): return self.document.get('key') @key.setter - def group (self, value): + def key (self, value): self.document['key'] = value @property diff --git a/setup.py b/setup.py index 3307c1c..eb0b627 100644 --- a/setup.py +++ b/setup.py @@ -3,13 +3,17 @@ setup( name='exp_sdk', - pacakges= ['exp_sdk'] - version='1.0.0-beta1', + pacakges= ['exp_sdk'], + version='1.0.0b1', description='EXP Python SDK', author='Scala', author_email='james.dalessio@scala.com', url='https://github.com/scalainc/exp-python2-sdk', download_url='https://github.com/scalainc/exp-python2-sdk/tarball/v1.0.0-beta1', install_requires=["requests", "socketIO_client"], - keywords=['scala', 'exp', 'sdk', 'signage'] + license='MIT', + keywords=['scala', 'exp', 'sdk', 'signage'], + classifiers=[ + 'Programming Language :: Python :: 2' + ] ) diff --git a/tests/test_auth.py b/tests/test_auth.py index f396a66..1379f46 100644 --- a/tests/test_auth.py +++ b/tests/test_auth.py @@ -4,7 +4,6 @@ from . import utils -from exp import exceptions class AuthBase (object): diff --git a/tests/test_locations.py b/tests/test_locations.py index 07b2166..e566dd9 100644 --- a/tests/test_locations.py +++ b/tests/test_locations.py @@ -27,5 +27,5 @@ def test_get_things (self): def test_layout_url (self): location = self.create_valid() url = location.get_layout_url() - print url - + if not url: + raise Exception diff --git a/tests/test_network.py b/tests/test_network.py index 6dcdf7f..b52e284 100644 --- a/tests/test_network.py +++ b/tests/test_network.py @@ -16,14 +16,13 @@ def test_simple_message_pattern (self): def test_queue (self): channel = self.exp.get_channel(self.generate_name()) - listener = channel.listen('m', max_age=2) + listener = channel.listen('m', max_age=1) channel.broadcast('m', 1) channel.broadcast('m', 2) time.sleep(2) channel.broadcast('m', 3) - time.sleep(.5) + time.sleep(.25) channel.broadcast('m', 4) - time.sleep(.5) if listener.wait().payload != 3: raise Exception if listener.wait().payload != 4: diff --git a/tests/test_start.py b/tests/test_start.py index 073040d..d329cc8 100644 --- a/tests/test_start.py +++ b/tests/test_start.py @@ -4,7 +4,6 @@ from . import utils -from exp import exceptions class StartFailBase (object): From ce2a7988820f90ea385563af8b7e210397877f66 Mon Sep 17 00:00:00 2001 From: James Dalessio Date: Mon, 28 Mar 2016 17:53:16 -0400 Subject: [PATCH 054/104] CI testing. --- .gitignore | 3 ++- bin/setup-ci-tests.sh | 3 +-- setup.py | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.gitignore b/.gitignore index 119d8cc..36224ea 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ *.pyc *.log -dist \ No newline at end of file +dist +exp_sdk.egg-info \ No newline at end of file diff --git a/bin/setup-ci-tests.sh b/bin/setup-ci-tests.sh index 640fced..515ac59 100644 --- a/bin/setup-ci-tests.sh +++ b/bin/setup-ci-tests.sh @@ -17,5 +17,4 @@ git pull origin develop npm install npm start& sleep 10 -cd ../exp-js-sdk -npm run build +cd ../exp-python2-sdk diff --git a/setup.py b/setup.py index eb0b627..11f3565 100644 --- a/setup.py +++ b/setup.py @@ -4,7 +4,7 @@ setup( name='exp_sdk', pacakges= ['exp_sdk'], - version='1.0.0b1', + version='1.0.0rc1', description='EXP Python SDK', author='Scala', author_email='james.dalessio@scala.com', From ac2d7cf21d4e0c84d450de0fcc69367218723d22 Mon Sep 17 00:00:00 2001 From: James Dalessio Date: Mon, 28 Mar 2016 18:00:19 -0400 Subject: [PATCH 055/104] Changing some relative imports. --- exp_sdk/authenticator.py | 3 +-- exp_sdk/exp.py | 12 ++++++------ 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/exp_sdk/authenticator.py b/exp_sdk/authenticator.py index 5fb3248..5973b57 100644 --- a/exp_sdk/authenticator.py +++ b/exp_sdk/authenticator.py @@ -7,8 +7,7 @@ import hashlib import threading -from . import logger -from . import exceptions +from exp_sdk import exceptions class Authenticator (object): diff --git a/exp_sdk/exp.py b/exp_sdk/exp.py index 018aac5..71635eb 100644 --- a/exp_sdk/exp.py +++ b/exp_sdk/exp.py @@ -5,10 +5,10 @@ from logging.handlers import RotatingFileHandler -from . import network -from . import authenticator -from . import api -from . import exceptions +from exp_sdk import network +from exp_sdk import authenticator +from exp_sdk import api +from exp_sdk import exceptions """ List of all instances of Exp. """ @@ -29,8 +29,8 @@ stream_handler.setLevel(logging.INFO) logger = logging.getLogger('exp') -#logger.addHandler(file_handler) -#logger.addHandler(stream_handler) +logger.addHandler(file_handler) +logger.addHandler(stream_handler) logger.setLevel(logging.DEBUG) From 5274abc18e33d32e3e592e083b2469b39f477eac Mon Sep 17 00:00:00 2001 From: James Dalessio Date: Mon, 28 Mar 2016 18:07:49 -0400 Subject: [PATCH 056/104] Fix test race condition. --- setup.py | 4 ++-- tests/test_network.py | 5 ++--- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/setup.py b/setup.py index 11f3565..4165db6 100644 --- a/setup.py +++ b/setup.py @@ -4,12 +4,12 @@ setup( name='exp_sdk', pacakges= ['exp_sdk'], - version='1.0.0rc1', + version='1.0.0a2', description='EXP Python SDK', author='Scala', author_email='james.dalessio@scala.com', url='https://github.com/scalainc/exp-python2-sdk', - download_url='https://github.com/scalainc/exp-python2-sdk/tarball/v1.0.0-beta1', + download_url='https://github.com/scalainc/exp-python2-sdk/tarball/v1.0.0-alpha1', install_requires=["requests", "socketIO_client"], license='MIT', keywords=['scala', 'exp', 'sdk', 'signage'], diff --git a/tests/test_network.py b/tests/test_network.py index b52e284..c78a848 100644 --- a/tests/test_network.py +++ b/tests/test_network.py @@ -21,11 +21,10 @@ def test_queue (self): channel.broadcast('m', 2) time.sleep(2) channel.broadcast('m', 3) - time.sleep(.25) channel.broadcast('m', 4) - if listener.wait().payload != 3: + if not listener.wait().payload in [3, 4] raise Exception - if listener.wait().payload != 4: + if not listener.wait().payload in [3, 4] raise Exception if listener.wait(): raise Exception From 6ef0a7b29e30c91a56f9ce8ca736c7bf2ba1834e Mon Sep 17 00:00:00 2001 From: James Dalessio Date: Mon, 28 Mar 2016 19:07:29 -0400 Subject: [PATCH 057/104] duh --- tests/test_network.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/test_network.py b/tests/test_network.py index c78a848..bb7fe56 100644 --- a/tests/test_network.py +++ b/tests/test_network.py @@ -22,9 +22,9 @@ def test_queue (self): time.sleep(2) channel.broadcast('m', 3) channel.broadcast('m', 4) - if not listener.wait().payload in [3, 4] + if not listener.wait().payload in [3, 4]: raise Exception - if not listener.wait().payload in [3, 4] + if not listener.wait().payload in [3, 4]: raise Exception if listener.wait(): raise Exception From b499d2eddc47e6be19743e39b653aa9ae13177f8 Mon Sep 17 00:00:00 2001 From: James Dalessio Date: Mon, 28 Mar 2016 20:59:56 -0400 Subject: [PATCH 058/104] Alpha 2 --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 4165db6..cd41177 100644 --- a/setup.py +++ b/setup.py @@ -9,7 +9,7 @@ author='Scala', author_email='james.dalessio@scala.com', url='https://github.com/scalainc/exp-python2-sdk', - download_url='https://github.com/scalainc/exp-python2-sdk/tarball/v1.0.0-alpha1', + download_url='https://github.com/scalainc/exp-python2-sdk/tarball/v1.0.0-alpha2', install_requires=["requests", "socketIO_client"], license='MIT', keywords=['scala', 'exp', 'sdk', 'signage'], From b020803b21e3a200db1f096686d46fd04e0c606e Mon Sep 17 00:00:00 2001 From: James Dalessio Date: Mon, 28 Mar 2016 21:01:10 -0400 Subject: [PATCH 059/104] sync tags --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index cd41177..a5cede8 100644 --- a/setup.py +++ b/setup.py @@ -9,7 +9,7 @@ author='Scala', author_email='james.dalessio@scala.com', url='https://github.com/scalainc/exp-python2-sdk', - download_url='https://github.com/scalainc/exp-python2-sdk/tarball/v1.0.0-alpha2', + download_url='https://github.com/scalainc/exp-python2-sdk/tarball/1.0.0a2', install_requires=["requests", "socketIO_client"], license='MIT', keywords=['scala', 'exp', 'sdk', 'signage'], From 052d894c47a0ada4b319bed27e00f498f5873fb9 Mon Sep 17 00:00:00 2001 From: James Dalessio Date: Mon, 28 Mar 2016 21:25:18 -0400 Subject: [PATCH 060/104] Fix silly misspelling. --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index a5cede8..b1303de 100644 --- a/setup.py +++ b/setup.py @@ -3,7 +3,7 @@ setup( name='exp_sdk', - pacakges= ['exp_sdk'], + packages= ['exp_sdk'], version='1.0.0a2', description='EXP Python SDK', author='Scala', From 9c8289f531abfd75a1c378f53ea58136e29e662c Mon Sep 17 00:00:00 2001 From: James Dalessio Date: Mon, 28 Mar 2016 21:27:28 -0400 Subject: [PATCH 061/104] Move to rc1. Figuring this out. --- setup.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/setup.py b/setup.py index b1303de..b4204fe 100644 --- a/setup.py +++ b/setup.py @@ -4,12 +4,12 @@ setup( name='exp_sdk', packages= ['exp_sdk'], - version='1.0.0a2', + version='1.0.0rc1', description='EXP Python SDK', author='Scala', author_email='james.dalessio@scala.com', url='https://github.com/scalainc/exp-python2-sdk', - download_url='https://github.com/scalainc/exp-python2-sdk/tarball/1.0.0a2', + download_url='https://github.com/scalainc/exp-python2-sdk/tarball/1.0.0rc1', install_requires=["requests", "socketIO_client"], license='MIT', keywords=['scala', 'exp', 'sdk', 'signage'], From 951e8890cf24817e927e47f96f4adb278158c0d2 Mon Sep 17 00:00:00 2001 From: James Dalessio Date: Mon, 28 Mar 2016 21:37:35 -0400 Subject: [PATCH 062/104] Change package name. --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index b4204fe..595dde3 100644 --- a/setup.py +++ b/setup.py @@ -2,7 +2,7 @@ from setuptools import setup setup( - name='exp_sdk', + name='exp-sdk', packages= ['exp_sdk'], version='1.0.0rc1', description='EXP Python SDK', From dd9c97b8edd2a4866bf95f1dfafba007f6d9696e Mon Sep 17 00:00:00 2001 From: James Dalessio Date: Mon, 28 Mar 2016 21:51:48 -0400 Subject: [PATCH 063/104] Restyle docs. --- README.md | 246 +++++++++++++++++++++++++++++------------------------- 1 file changed, 130 insertions(+), 116 deletions(-) diff --git a/README.md b/README.md index a5d9333..6cf80a0 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,20 @@ - # Installation +Install the `exp-sdk` package from PyPi via your favorite python package manager. + +```bash +pip install exp-sdk +``` + +```bash +easy_install exp-sdk +``` + # Runtime +## Starting + **`exp_sdk.start(options)`** Starts and returns an sdk instance. Can be called multiple times to start multiple independent instances of the sdk. The sdk can be started using user, device, or consumer app credentials.`**options` supports the following keyword arguments: @@ -46,6 +57,8 @@ exp_2.create_device() # Exception. New instances can still be created by calling `start`. +## Stopping + **`exp.stop()`** Stops the sdk instance, cancels its listeners, and stops all network connections. @@ -58,6 +71,31 @@ exp.get_auth() # Exception. Sdk instances cannot be restarted and any invokation on the instance will raise an exception. +## Exceptions + + **`exp_sdk.ExpError`** + + Base class for all EXP exceptions. + + **`exp_sdk.UnexpectedError`** + + Raised when an unexpected error occurs. + + **`exp_sdk.RuntimeError`** + + Raised when [startup options](#runtime) are incorrect or inconsistent. + + **`exp_sdk.AuthenticationError`** + + Raised when the sdk cannot authenticate due to bad credentials. + + **`exp_sdk.ApiError`** + + Raised when an API call fails. Has properties `message` and `code`. + + +## Status + **`exp.is_connected`** Whether or not there is an active socket connection to the network. @@ -77,27 +115,6 @@ Returns the up to date authentication payload. The authentication payload may be print 'My authentication token is : %s' % exp.get_auth()['token'] ``` -# Exceptions - - **`exp_sdk.ExpError`** - - Base class for all EXP exceptions. - - **`exp_sdk.UnexpectedError`** - - Raised when an unexpected error occurs. - - **`exp_sdk.RuntimeError`** - - Raised when [startup options](#runtime) are incorrect or inconsistent. - - **`exp_sdk.AuthenticationError`** - - Raised when the sdk cannot authenticate due to bad credentials. - - **`exp_sdk.ApiError`** - - Raised when an API call fails. Has properties `message` and `code`. @@ -193,100 +210,6 @@ while True: # API -## Custom HTTP Requests - -These methods all users to send custom authenticated API calls. `params` is a dictionary of url params, `payload` is a JSON serializable type, and `timeout` is the duration, in seconds, to wait for the request to complete. `path` is relative to the api host root. All methods will return a JSON serializable type. - -**`exp.get(path, params=None, timeout=10)`** - -Send a GET request. - -```python -result = exp.get('/api/devices', { 'name': 'my-name' }) # Find devices by name. -``` - -**`exp.post(path, payload=None, params=None, timeout=10)`** - -Send a POST request. - -```python -document = exp.post('/api/experiences', {}) # Create a new empty experience. -``` - -**`exp.patch(path, payload=None, params=None, timeout=10)`** - -Send a PATCH request. - -```python -document = exp.patch('/api/experiences/[uuid]', { 'name': 'new-name' }) # Rename an experience. -``` - - -**`exp.put(path, payload=None, params=None, timeout=10)`** - -Send a PUT request. - -```python -document = exp.put('/api/data/cats/fluffy', { 'eyes': 'blue'}) # Insert a data value. -``` - -**`exp.delete(path, payload=None, params=None, timeout=10)`** - -Send a DELETE request. - -```python -exp.delete('/api/location/[uuid]') # Delete a location. -``` - - -## Resources - -These methods and attributes are shared by many of the abstract API resources. - -**`resource.uuid`** - -The uuid of the resource. Cannot be set. - -**`resource.name`** - -The name of the resource. Can be set directl - -**`resource.document`** - -The resource's underlying document - -**`resource.save()`** - -Saves the resource and updates the document in place. - -```python -device = exp.get_device('[uuid]') -device.name = 'my-new-name' -device.save() # device changes are now saved -``` - -**`resource.refresh()`** - -Refreshes the resource's underlying document in place. - -```python -device = exp.create_device() -device.name = 'new-name' -device_2 = exp.get_device(device.uuid) -device.save() -device_2.refresh() -print device_2.name # 'new-name' -``` - -**`resource.get_channel(system=False, consumer=False)`** - -Returns the channel whose name is contextually associated with this resource. - -```python -channel = experience.get_channel() -channel.broadcast('hello?') -``` - ## Devices @@ -528,4 +451,95 @@ Returns a boolean indicating whether or not this content item has a variant with Returns the delivery url for a variant of this content item. +## Resources + +These methods and attributes are shared by many of the abstract API resources. +**`resource.uuid`** + +The uuid of the resource. Cannot be set. + +**`resource.name`** + +The name of the resource. Can be set directl + +**`resource.document`** + +The resource's underlying document + +**`resource.save()`** + +Saves the resource and updates the document in place. + +```python +device = exp.get_device('[uuid]') +device.name = 'my-new-name' +device.save() # device changes are now saved +``` + +**`resource.refresh()`** + +Refreshes the resource's underlying document in place. + +```python +device = exp.create_device() +device.name = 'new-name' +device_2 = exp.get_device(device.uuid) +device.save() +device_2.refresh() +print device_2.name # 'new-name' +``` + +**`resource.get_channel(system=False, consumer=False)`** + +Returns the channel whose name is contextually associated with this resource. + +```python +channel = experience.get_channel() +channel.broadcast('hello?') +``` + +## Custom Requests + +These methods all users to send custom authenticated API calls. `params` is a dictionary of url params, `payload` is a JSON serializable type, and `timeout` is the duration, in seconds, to wait for the request to complete. `path` is relative to the api host root. All methods will return a JSON serializable type. + +**`exp.get(path, params=None, timeout=10)`** + +Send a GET request. + +```python +result = exp.get('/api/devices', { 'name': 'my-name' }) # Find devices by name. +``` + +**`exp.post(path, payload=None, params=None, timeout=10)`** + +Send a POST request. + +```python +document = exp.post('/api/experiences', {}) # Create a new empty experience. +``` + +**`exp.patch(path, payload=None, params=None, timeout=10)`** + +Send a PATCH request. + +```python +document = exp.patch('/api/experiences/[uuid]', { 'name': 'new-name' }) # Rename an experience. +``` + + +**`exp.put(path, payload=None, params=None, timeout=10)`** + +Send a PUT request. + +```python +document = exp.put('/api/data/cats/fluffy', { 'eyes': 'blue'}) # Insert a data value. +``` + +**`exp.delete(path, payload=None, params=None, timeout=10)`** + +Send a DELETE request. + +```python +exp.delete('/api/location/[uuid]') # Delete a location. +``` From e51900679d49b107b0fb75dbeab524e29a092772 Mon Sep 17 00:00:00 2001 From: James Dalessio Date: Mon, 28 Mar 2016 21:53:07 -0400 Subject: [PATCH 064/104] Small wording change in install. --- README.md | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 6cf80a0..cdd9abb 100644 --- a/README.md +++ b/README.md @@ -7,9 +7,8 @@ Install the `exp-sdk` package from PyPi via your favorite python package manager pip install exp-sdk ``` -```bash -easy_install exp-sdk -``` +This gives your environment access to the ```exp_sdk``` module. + # Runtime From 71d6a9033330b04e3885855c2cf3fc07202c1fbb Mon Sep 17 00:00:00 2001 From: James Dalessio Date: Mon, 28 Mar 2016 22:00:16 -0400 Subject: [PATCH 065/104] Formatting docs. --- README.md | 35 ++++++++++++++++++++--------------- 1 file changed, 20 insertions(+), 15 deletions(-) diff --git a/README.md b/README.md index cdd9abb..d177c3b 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ This gives your environment access to the ```exp_sdk``` module. # Runtime -## Starting +## Starting the SDK **`exp_sdk.start(options)`** @@ -41,6 +41,11 @@ exp = exp_sdk.start(uuid='[uuid]', secret='[secret]') exp = exp_sdk.start(uuid='[uuid]', api_key='[api-key]') ``` + + +## Stopping the SDK + + **`exp_sdk.stop()`** Stops all running instances of the sdk, cancels all listeners and stops all network connections. @@ -55,9 +60,6 @@ exp_2.create_device() # Exception. New instances can still be created by calling `start`. - -## Stopping - **`exp.stop()`** Stops the sdk instance, cancels its listeners, and stops all network connections. @@ -93,17 +95,7 @@ Sdk instances cannot be restarted and any invokation on the instance will raise Raised when an API call fails. Has properties `message` and `code`. -## Status - -**`exp.is_connected`** - -Whether or not there is an active socket connection to the network. - -```python -# Wait for a connection. -while not exp.is_connected: - time.sleep(1) -``` +## Authentication Payload **`exp.get_auth()`** @@ -120,6 +112,19 @@ print 'My authentication token is : %s' % exp.get_auth()['token'] # Network +## Status + +**`exp.is_connected`** + +Whether or not there is an active socket connection to the network. + +```python +# Wait for a connection. +while not exp.is_connected: + time.sleep(1) +``` + + ## Channels **`exp.get_channel(name, consumer=False, system=False)`** From c7f5e5d74b234a93ed91a2e43b8f90285a25361d Mon Sep 17 00:00:00 2001 From: James Dalessio Date: Tue, 29 Mar 2016 00:03:08 -0400 Subject: [PATCH 066/104] Docs --- README.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index d177c3b..1430327 100644 --- a/README.md +++ b/README.md @@ -455,6 +455,10 @@ Returns a boolean indicating whether or not this content item has a variant with Returns the delivery url for a variant of this content item. +**`content.get_children()`** + +Returns a list of the content items children. + ## Resources These methods and attributes are shared by many of the abstract API resources. @@ -540,7 +544,7 @@ Send a PUT request. document = exp.put('/api/data/cats/fluffy', { 'eyes': 'blue'}) # Insert a data value. ``` -**`exp.delete(path, payload=None, params=None, timeout=10)`** +**`exp.delete(path, params=None, timeout=10)`** Send a DELETE request. From 09b51e1bf5e2bda05e9b0f308c0ef7e4fa13d38f Mon Sep 17 00:00:00 2001 From: James Dalessio Date: Tue, 29 Mar 2016 15:06:35 -0400 Subject: [PATCH 067/104] Official release 1.0.0 --- setup.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/setup.py b/setup.py index 595dde3..15f0414 100644 --- a/setup.py +++ b/setup.py @@ -4,12 +4,12 @@ setup( name='exp-sdk', packages= ['exp_sdk'], - version='1.0.0rc1', + version='1.0.0', description='EXP Python SDK', author='Scala', author_email='james.dalessio@scala.com', url='https://github.com/scalainc/exp-python2-sdk', - download_url='https://github.com/scalainc/exp-python2-sdk/tarball/1.0.0rc1', + download_url='https://github.com/scalainc/exp-python2-sdk/tarball/1.0.0', install_requires=["requests", "socketIO_client"], license='MIT', keywords=['scala', 'exp', 'sdk', 'signage'], From 78e25c5c11b03389d92fa867e27a5f8c3a028b13 Mon Sep 17 00:00:00 2001 From: James Dalessio Date: Mon, 4 Apr 2016 16:36:17 -0400 Subject: [PATCH 068/104] Fixing up some issues and adding listener exception. --- .gitignore | 3 ++- README.md | 10 +++++++--- exp_sdk/exceptions.py | 2 ++ exp_sdk/network.py | 9 +++++++-- setup.py | 2 +- tests/test_network.py | 21 +++++++++++++++++++-- 6 files changed, 38 insertions(+), 9 deletions(-) diff --git a/.gitignore b/.gitignore index 36224ea..2b03b36 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ *.pyc *.log dist -exp_sdk.egg-info \ No newline at end of file +exp_sdk.egg-info + diff --git a/README.md b/README.md index 1430327..1d19ae1 100644 --- a/README.md +++ b/README.md @@ -86,6 +86,10 @@ Sdk instances cannot be restarted and any invokation on the instance will raise Raised when [startup options](#runtime) are incorrect or inconsistent. + **`exp_sdk.NetworkError`** + +Raised when an error or timeout occurs when attempting to listen on the network. + **`exp_sdk.AuthenticationError`** Raised when the sdk cannot authenticate due to bad credentials. @@ -144,9 +148,9 @@ responses = channel.broadcast('hi!', { 'test': 'nice to meet you!' }) [print response for response in responses] ``` -**`channel.listen(name, max_age=60)`** +**`channel.listen(name, timeout=10, max_age=60)`** -Returns a [listener](#listener) for events on the channel. `max_age` is the number of seconds the listener will buffer events before they are discarded. +Returns a [listener](#listener) for events on the channel. `timeout` is how many seconds to wait for the channel to open. `max_age` is the number of seconds the listener will buffer events before they are discarded. If `timeout` is reached before the channel is opened, a `NetworkError` will be raised. ```python channel = exp.get_channel('my-channel') @@ -172,7 +176,7 @@ Requests that [devices](#device) listening for this event on this channel visual **`listener.wait(timeout=0)`** -Wait for `timeout` seconds for broadcasts. Returns a [broadcast](#broadcasts) if a [broadcast](#broadcasts) is in the queue or if a [broadcast](#broadcasts) is received before the timeout. If timeout is reached, returns `None`. +Wait for `timeout` seconds for broadcasts. Returns a [broadcast](#broadcasts) if a [broadcast](#broadcasts) is in the queue or if a [broadcast](#broadcasts) is received before the timeout. If timeout is reached, returns `None`. If timeout is set to 0 (the default), will return immediately. ```python channel = exp.get_channel('my-channel') diff --git a/exp_sdk/exceptions.py b/exp_sdk/exceptions.py index ba62fb8..f910cbb 100644 --- a/exp_sdk/exceptions.py +++ b/exp_sdk/exceptions.py @@ -44,3 +44,5 @@ def __init__(self, code=None, message=None, status_code=None): def __str__(self): return '%s: %s' % (self.code, self.message) + +class NetworkError(ExpError): pass \ No newline at end of file diff --git a/exp_sdk/network.py b/exp_sdk/network.py index bed884b..e3f5268 100644 --- a/exp_sdk/network.py +++ b/exp_sdk/network.py @@ -94,13 +94,14 @@ def broadcast (self, name, payload=None, timeout=0.1): params = {'timeout': timeout * 1000 } return self._sdk.api.post(path, payload, params) - def listen (self, name, **kwargs): + def listen (self, name, timeout=10, **kwargs): if not self._namespaces.get(name): self._namespaces[name] = _Namespace(self._sdk) listener = self._namespaces[name].listen(**kwargs) if not self.subscription.is_set(): self._sdk.network.emit('subscribe', [self._id]) - self.subscription.wait() + if not self.subscription.wait(timeout): + raise self._sdk.exceptions.NetworkError('Listen timed out.') return listener def receive (self, message): @@ -131,6 +132,10 @@ def __init__(self, sdk): self._thread = threading.Thread(target=lambda: self._main_event_loop()) self._lock = threading.Lock() + @property + def is_connected (self): + return self._socket and self._socket.is_connected + def start (self): self._thread.start() diff --git a/setup.py b/setup.py index 15f0414..96f6d51 100644 --- a/setup.py +++ b/setup.py @@ -4,7 +4,7 @@ setup( name='exp-sdk', packages= ['exp_sdk'], - version='1.0.0', + version='1.0.1-rc1', description='EXP Python SDK', author='Scala', author_email='james.dalessio@scala.com', diff --git a/tests/test_network.py b/tests/test_network.py index bb7fe56..ab0b1fd 100644 --- a/tests/test_network.py +++ b/tests/test_network.py @@ -22,14 +22,16 @@ def test_queue (self): time.sleep(2) channel.broadcast('m', 3) channel.broadcast('m', 4) - if not listener.wait().payload in [3, 4]: + if not listener.wait(2).payload in [3, 4]: raise Exception - if not listener.wait().payload in [3, 4]: + if not listener.wait(2).payload in [3, 4]: raise Exception if listener.wait(): raise Exception + + class Test2 (utils.Base): def test_responding (self): @@ -61,3 +63,18 @@ def test_listener_cancelling (self): channel.broadcast('test_message_3') if listener.wait(.1): raise Exception + + def test_connected (self): + exp = self.exp_sdk.start(**self.consumer_credentials) + while not exp.is_connected: + time.sleep(.1) + + + def test_listener_timeout (self): + self.consumer_credentials['enable_network'] = False + exp = self.exp_sdk.start(**self.consumer_credentials) + try: + exp.get_channel('test').listen('hello', timeout=2) + except self.exp_sdk.NetworkError: + return + raise Exception From 60003856cc92be85fa46d674cc65aed08e05044f Mon Sep 17 00:00:00 2001 From: James Dalessio Date: Mon, 4 Apr 2016 16:41:33 -0400 Subject: [PATCH 069/104] Bump tag --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 96f6d51..19a1e05 100644 --- a/setup.py +++ b/setup.py @@ -9,7 +9,7 @@ author='Scala', author_email='james.dalessio@scala.com', url='https://github.com/scalainc/exp-python2-sdk', - download_url='https://github.com/scalainc/exp-python2-sdk/tarball/1.0.0', + download_url='https://github.com/scalainc/exp-python2-sdk/tarball/1.0.1-rc1', install_requires=["requests", "socketIO_client"], license='MIT', keywords=['scala', 'exp', 'sdk', 'signage'], From 10b7c6c765e1b9a9d382c0033140f3b0337332a4 Mon Sep 17 00:00:00 2001 From: James Dalessio Date: Mon, 4 Apr 2016 16:46:22 -0400 Subject: [PATCH 070/104] Use propert python version. --- setup.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/setup.py b/setup.py index 19a1e05..844c1a0 100644 --- a/setup.py +++ b/setup.py @@ -4,12 +4,12 @@ setup( name='exp-sdk', packages= ['exp_sdk'], - version='1.0.1-rc1', + version='1.0.1rc1', description='EXP Python SDK', author='Scala', author_email='james.dalessio@scala.com', url='https://github.com/scalainc/exp-python2-sdk', - download_url='https://github.com/scalainc/exp-python2-sdk/tarball/1.0.1-rc1', + download_url='https://github.com/scalainc/exp-python2-sdk/tarball/1.0.1rc1', install_requires=["requests", "socketIO_client"], license='MIT', keywords=['scala', 'exp', 'sdk', 'signage'], From 42981a73d17a204cac3dd5e7ab1e6fe77f5501c2 Mon Sep 17 00:00:00 2001 From: James Dalessio Date: Mon, 4 Apr 2016 16:55:36 -0400 Subject: [PATCH 071/104] resubmit due to pypi 500 --- setup.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/setup.py b/setup.py index 844c1a0..8f0f2e4 100644 --- a/setup.py +++ b/setup.py @@ -4,12 +4,12 @@ setup( name='exp-sdk', packages= ['exp_sdk'], - version='1.0.1rc1', + version='1.0.1rc2', description='EXP Python SDK', author='Scala', author_email='james.dalessio@scala.com', url='https://github.com/scalainc/exp-python2-sdk', - download_url='https://github.com/scalainc/exp-python2-sdk/tarball/1.0.1rc1', + download_url='https://github.com/scalainc/exp-python2-sdk/tarball/1.0.1rc2', install_requires=["requests", "socketIO_client"], license='MIT', keywords=['scala', 'exp', 'sdk', 'signage'], From a6005ba37a7d0bfb74edd28f81d195f6143bffc7 Mon Sep 17 00:00:00 2001 From: James Dalessio Date: Tue, 5 Apr 2016 10:48:04 -0400 Subject: [PATCH 072/104] Remove log streams. --- exp_sdk/exp.py | 21 +-------------------- setup.py | 4 ++-- 2 files changed, 3 insertions(+), 22 deletions(-) diff --git a/exp_sdk/exp.py b/exp_sdk/exp.py index 71635eb..6955f20 100644 --- a/exp_sdk/exp.py +++ b/exp_sdk/exp.py @@ -15,25 +15,6 @@ instances = [] -""" Configure the logger. """ -file_handler_formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s') - -file_handler = RotatingFileHandler('debug.log', mode='a', maxBytes=5*1024*1024, backupCount=5, encoding=None, delay=0) -file_handler.setFormatter(file_handler_formatter) -file_handler.setLevel(logging.DEBUG) - -stream_handler_formatter = logging.Formatter('EXP/%(levelname)-10s: %(message)s') - -stream_handler = logging.StreamHandler() -stream_handler.setFormatter(stream_handler_formatter) -stream_handler.setLevel(logging.INFO) - -logger = logging.getLogger('exp') -logger.addHandler(file_handler) -logger.addHandler(stream_handler) -logger.setLevel(logging.DEBUG) - - """ Terminate all running instances when Ctrl-C is pressed. """ signal.signal(signal.SIGINT, lambda signal, frame: stop()) @@ -89,7 +70,7 @@ def __init__(self, **options): self.authenticator = authenticator.Authenticator(self) self.api = api.Api(self) self.network = network.Network(self) - self.logger = logger + self.logger = logging.getLogger('exp-sdk') self.exceptions = exceptions diff --git a/setup.py b/setup.py index 8f0f2e4..e7cafb0 100644 --- a/setup.py +++ b/setup.py @@ -4,12 +4,12 @@ setup( name='exp-sdk', packages= ['exp_sdk'], - version='1.0.1rc2', + version='1.0.2b1', description='EXP Python SDK', author='Scala', author_email='james.dalessio@scala.com', url='https://github.com/scalainc/exp-python2-sdk', - download_url='https://github.com/scalainc/exp-python2-sdk/tarball/1.0.1rc2', + download_url='https://github.com/scalainc/exp-python2-sdk/tarball/1.0.2b1', install_requires=["requests", "socketIO_client"], license='MIT', keywords=['scala', 'exp', 'sdk', 'signage'], From 4753663cb02b932c5cf0e16a67a8541d54ae68f3 Mon Sep 17 00:00:00 2001 From: James Dalessio Date: Tue, 5 Apr 2016 10:50:02 -0400 Subject: [PATCH 073/104] Add note about logger to docs. --- README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 1d19ae1..cfe733d 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ Install the `exp-sdk` package from PyPi via your favorite python package manager pip install exp-sdk ``` -This gives your environment access to the ```exp_sdk``` module. +This gives your environment access to the ```exp_sdk``` module. # Runtime @@ -111,7 +111,9 @@ print 'My authentication token is : %s' % exp.get_auth()['token'] ``` +## Logging +The EXP SDK uses the ```exp-sdk``` logger namespace. # Network From 86cdc3455d0a1972e57d4aab13e7e2727318923a Mon Sep 17 00:00:00 2001 From: James Dalessio Date: Wed, 6 Apr 2016 07:58:03 -0400 Subject: [PATCH 074/104] try catch signal --- exp_sdk/exp.py | 5 ++++- setup.py | 4 ++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/exp_sdk/exp.py b/exp_sdk/exp.py index 6955f20..9458ae3 100644 --- a/exp_sdk/exp.py +++ b/exp_sdk/exp.py @@ -16,7 +16,10 @@ """ Terminate all running instances when Ctrl-C is pressed. """ -signal.signal(signal.SIGINT, lambda signal, frame: stop()) +try: + signal.signal(signal.SIGINT, lambda signal, frame: stop()) +except: + pass def start (enable_network=True, host='https://api.goexp.io/', **options): diff --git a/setup.py b/setup.py index e7cafb0..ed25c96 100644 --- a/setup.py +++ b/setup.py @@ -4,12 +4,12 @@ setup( name='exp-sdk', packages= ['exp_sdk'], - version='1.0.2b1', + version='1.0.2b2', description='EXP Python SDK', author='Scala', author_email='james.dalessio@scala.com', url='https://github.com/scalainc/exp-python2-sdk', - download_url='https://github.com/scalainc/exp-python2-sdk/tarball/1.0.2b1', + download_url='https://github.com/scalainc/exp-python2-sdk/tarball/1.0.2b2', install_requires=["requests", "socketIO_client"], license='MIT', keywords=['scala', 'exp', 'sdk', 'signage'], From 2001d3179b3478271e94d7ee84f9a83884f2829a Mon Sep 17 00:00:00 2001 From: James Dalessio Date: Wed, 6 Apr 2016 08:08:58 -0400 Subject: [PATCH 075/104] Use relative imports. --- exp_sdk/exp.py | 8 ++++---- setup.py | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/exp_sdk/exp.py b/exp_sdk/exp.py index 9458ae3..d184c91 100644 --- a/exp_sdk/exp.py +++ b/exp_sdk/exp.py @@ -5,10 +5,10 @@ from logging.handlers import RotatingFileHandler -from exp_sdk import network -from exp_sdk import authenticator -from exp_sdk import api -from exp_sdk import exceptions +from . import network +from . import authenticator +from . import api +from . import exceptions """ List of all instances of Exp. """ diff --git a/setup.py b/setup.py index ed25c96..fde3546 100644 --- a/setup.py +++ b/setup.py @@ -4,12 +4,12 @@ setup( name='exp-sdk', packages= ['exp_sdk'], - version='1.0.2b2', + version='1.0.2b3', description='EXP Python SDK', author='Scala', author_email='james.dalessio@scala.com', url='https://github.com/scalainc/exp-python2-sdk', - download_url='https://github.com/scalainc/exp-python2-sdk/tarball/1.0.2b2', + download_url='https://github.com/scalainc/exp-python2-sdk/tarball/1.0.2b3', install_requires=["requests", "socketIO_client"], license='MIT', keywords=['scala', 'exp', 'sdk', 'signage'], From 0c1376b2ff96e4dbefcbacf44dbbedf5af6c27e7 Mon Sep 17 00:00:00 2001 From: James Dalessio Date: Wed, 6 Apr 2016 11:32:19 -0400 Subject: [PATCH 076/104] Fix network port host issues and wait for connection. --- exp_sdk/network.py | 3 +-- setup.py | 4 ++-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/exp_sdk/network.py b/exp_sdk/network.py index e3f5268..f61968d 100644 --- a/exp_sdk/network.py +++ b/exp_sdk/network.py @@ -266,8 +266,7 @@ def on_subscribed (self, message): Namespace.socket = self params = { 'token': auth['token'] } - parsed_host = urlparse.urlparse(auth['network']['host']) - self._socket = SocketIO(parsed_host.hostname, parsed_host.port, Namespace, params=params, hurry_interval_in_seconds=10) + self._socket = SocketIO(auth['network']['host'], Namespace=Namespace, params=params, wait_for_connection=False, hurry_interval_in_seconds=5) def stop (self): self._sdk.logger.debug('Disconnecting from network.') diff --git a/setup.py b/setup.py index fde3546..9623b22 100644 --- a/setup.py +++ b/setup.py @@ -4,12 +4,12 @@ setup( name='exp-sdk', packages= ['exp_sdk'], - version='1.0.2b3', + version='1.0.2b4', description='EXP Python SDK', author='Scala', author_email='james.dalessio@scala.com', url='https://github.com/scalainc/exp-python2-sdk', - download_url='https://github.com/scalainc/exp-python2-sdk/tarball/1.0.2b3', + download_url='https://github.com/scalainc/exp-python2-sdk/tarball/1.0.2b4', install_requires=["requests", "socketIO_client"], license='MIT', keywords=['scala', 'exp', 'sdk', 'signage'], From 62399ec5e7edeba2911d30247b8b9ee4f59a664c Mon Sep 17 00:00:00 2001 From: Remco Schiepers Date: Thu, 7 Apr 2016 09:58:09 +0200 Subject: [PATCH 077/104] adding rss enterprise test script --- .../enterprice rss test script/RssReader.sca | 63 +++++++++ .../RssReader/background.jpg | Bin 0 -> 1281995 bytes .../RssReader/exprss.py | 128 ++++++++++++++++++ .../RssReader/news.jpg | Bin 0 -> 339163 bytes 4 files changed, 191 insertions(+) create mode 100644 tests/enterprice rss test script/RssReader.sca create mode 100644 tests/enterprice rss test script/RssReader/background.jpg create mode 100644 tests/enterprice rss test script/RssReader/exprss.py create mode 100644 tests/enterprice rss test script/RssReader/news.jpg diff --git a/tests/enterprice rss test script/RssReader.sca b/tests/enterprice rss test script/RssReader.sca new file mode 100644 index 0000000..3b4d138 --- /dev/null +++ b/tests/enterprice rss test script/RssReader.sca @@ -0,0 +1,63 @@ +!ScalaScript1100 +// Saved by Scala Designer Release 11.00.06 at 2016-04-07 09:52:39 +:"RssReader.sca" +{ + Group: + Template Integer(pageDuration(20)); + Template String(uuid("110c7cba-f12c-43cf-ab62-e525b8de7f68"), api_key("682e0a9d341b783c6856c4bf8f4f741c08f6251b641aaeec165e052efba7fbd164f0027f5ca3e310b38c247021919d64"), host("http://192.168.168.38:9000/"), feed_uuid("f14fb720-6a66-4b26-b38d-283a724695d9")); + String(message, info.source("http://rss.cnn.com/rss/edition_world.rss"), info.lastBuildDate("2016-04-06T14:43:54.000Z")); + Integer(info.maxResults(100)); + String(info.name("CNN.com - World")); + Boolean(item.exists[9]); + String(item.title[10]("How Reagan never left the campaign trail", "Death penalty: 2015 a troubling year", "Putin and the Panama Papers", "It moves, it glows: Vhils' ode to the neon city", "Celebrating 100 years of Bavarian beauty from BMW", "Joining the 'gold rush' with Italy's Tuscan truffle hunters", "Female Sumatran rhino dies weeks after rare sighting", "Nigeria plans to send an astronaut to space by 2030", "World's first dengue fever vaccine launched in the Philippines", "Then and now: The home of the Masters before it was famous"), item.text[10]("Ronald Reagan may have left office in 1989 but, to listen to Republican presidential candidates since, it's like he never left.", "Countries that still execute need to realize that they are on the wrong side of history, writes Salil Shetty.", "The Kremlin introduced a new word into the English language Monday: ^"Putinophobia.^"", "Whether it's blowing up a building façade in Berlin to reveal a carving of a man's face or drilling portraits into favela walls in Rio de Janeiro, raucous street artist Alexandre Farto, who goes by the tag ^"Vhils,^" has left an imprint on urban landscapes across the globe.", "This month ^"the ultimate driving machine^" passes an important milestone: BMW, aka Bavarian Motor Works, is turning 100.", "On a misty Italian morning, we crunch our way through green-yellow undergrowth as Pepe the dog darts on ahead, nose close to the sandy soil.", "The optimistic story of a Sumatran rhino took a dark turn when the mammal died weeks after a rare sighting.", "Nigeria has announced plans to send an astronaut into space by 2030, as part of its drive to develop a world-class space industry.", "Dengue fever infects 390 million people each year, and kills as many as 25,000, according to the World Health Organization.", "It's one of the most iconic venues in sport but Augusta National Golf Club -- home of the Masters -- hasn't always been a pristine golfer's paradise."), item.date[10]("2016-04-06T14:43:26.000Z", "2016-04-06T14:34:05.000Z", "2016-04-06T14:33:42.000Z", "2016-04-06T14:00:23.000Z", "2016-04-06T14:00:08.000Z", "2016-04-06T13:59:42.000Z", "2016-04-06T13:58:43.000Z", "2016-04-06T13:58:12.000Z", "2016-04-06T13:53:29.000Z", "2016-04-06T13:52:29.000Z")); + FileNameString(item.image[10]("content:\rss\121102125319-41-ronald-reagan-president-top-tease.jpg", "content:\rss\160406083051-amnesty-death-penalty-report-2015-tease-top-tease.jpg", "content:\rss\160404174156-putin-panama-papers-lklv-chance-00001517-top-tease.jpg", "content:\rss\160404090216-vhils-street-view-top-tease.jpg", "content:\rss\160404173013-bmw-slide-3-top-tease.jpg", "content:\rss\151210180650-truffles-balcony-top-tease.jpg", "content:\rss\160323212452-03-sumatran-rhino-top-tease.jpg", "content:\rss\160406100823-nigcom-sat-1-launch-top-tease.jpg", "content:\rss\150731180818-aedes-aegypti-top-tease.jpg", "content:\rss\160406114700-augusta-slider-tease-top-tease.jpg")); + Integer(count, loop, index, totalCount); + BackgroundSettings(Size(1920, 1080)); + Config.RecentPublishLocations(PublishLocation("localhost ContentManager", "RssReader")); + Sequence: + WindowsScript("RssReader\exprss.py", Wait(On), Engine("Python.AXScript.2"), ShareVariable(uuid), ShareVariable(api_key), ShareVariable(host), ShareVariable(feed_uuid), ShareVariable(message)); + { + Group: + XMLFile("Content:\rss\rss_data.xml", MapData("/rss/info", DataVariable(info.source, "source"), DataVariable(info.lastBuildDate, "lastBuildDate"), DataVariable(info.maxResults, "maxResults"), DataVariable(info.name, "name")), MapRepeatingData("/rss/items/item", NumRecords(10), MaxLoops(0), StepSize(1), CurrentCountVariable(count), CurrentIndexVariable(index), CurrentLoopVariable(loop), TotalCountVariable(totalCount), DataExistsVariable(item.exists), DataVariable(item.title, "title"), DataVariable(item.text, "text"), DataVariable(item.date, "date"), DataVariable(item.image, "image"))); + Sequence: + { + Group: + Picture("RssReader\news.jpg", Wipe("Dissolve", Duration(1000), Direction(90)), Backdrop(Pen(1)), Margin(10, 10, 0, 0), UserPalette(RGBPen(1, $0, $ffffff, $999999, $555555, $712068, $df449c, $dc110e, $662200, $ff5a00, $ff9c00, $ffee00, $8800, $dd00, $cccc, $66ff, $aa, $777777, $bbbbbb, $dddddd, $465a96)), AutoScale(FillAndTrim), Operation(On)); + Text(1060, 494, info.name, Shadow(Off, Softness(5)), AntiAlias(On), OnReplay(Replace), Under(Off, Thickness(3)), Font("Calibri (Western [])", 50), Tabs(Relative(On)), Wrap(On, Width(1890))); + HardDuration(5000); + If(index<1); + } + :"newsPage" + { + Group: + Picture("RssReader\background.jpg", Wipe("Dissolve", Duration(1000), Direction(90)), Backdrop(Pen(2)), Margin(10, 10, 0, 0), UserPalette(RGBPen(1, $0, $ffffff, $999999, $555555, $712068, $df449c, $dc110e, $662200, $ff5a00, $ff9c00, $ffee00, $8800, $dd00, $cccc, $66ff, $aa, $777777, $bbbbbb, $dddddd, $465a96)), AutoScale(FillAndTrim), Operation(On)); + HardDuration(pageDuration*1000); + Sequence: + { + Group: + Text(550, 333, item.title[0], Wipe("ShorterFade", Duration(401), Direction(0), Wait(Off)), Outline(Off, Pen(2)), Shadow(Off, Softness(5), Pen(2)), AntiAlias(On), Update(None), OnReplay(Replace), Under(Off, Thickness(3)), Font("Calibri (Western [])", 50), Bold(On), Tabs(Relative(On)), Wrap(Off, Width(864))); + HardDuration(0); + } + { + Group: + Text(550, 760, info.name, Wipe("ShorterFade", Duration(401), Direction(0), Wait(Off)), Outline(Off, Pen(2)), Shadow(Off, Softness(5), Pen(2)), AntiAlias(On), Update(None), OnReplay(Replace), Under(Off, Thickness(3)), Font("Calibri (Western [])", 35), Bold(On), Tabs(Relative(On)), Wrap(On, Width(1678))); + HardDuration(0); + } + { + Group: + Text(860, 429, item.date[0], Wipe("ShorterFade", Duration(401), Direction(0), Wait(Off)), Outline(Off, Pen(2)), Shadow(Off, Softness(5), Pen(2)), AntiAlias(On), Update(None), OnReplay(Replace), Under(Off, Thickness(3)), Font("Calibri (Western [])", 35), Italic(On), Tabs(Relative(On)), Wrap(On, Width(1890))); + HardDuration(0); + } + { + Group: + Text(860, 473, item.text[0], Wipe("ShorterFade", Duration(401), Direction(0), Wait(Off)), Outline(Off, Pen(2)), Shadow(Off, Softness(5), Pen(2)), AntiAlias(On), Update(None), OnReplay(Replace), Under(Off, Thickness(3)), Font("Calibri (Western [])", 50), Tabs(Relative(On)), Wrap(On, Width(1003))); + HardDuration(0); + } + { + Group: + Clip(550, 429, item.image[0], Wipe("ShorterFade", Duration(401), Direction(0), Wait(Off)), Transparent(Off), Update(None), OnReplay(Replace), AutoScale(FillAndTrim), Operation(On, Resize(276, 276))); + HardDuration(0); + } + } + } +} diff --git a/tests/enterprice rss test script/RssReader/background.jpg b/tests/enterprice rss test script/RssReader/background.jpg new file mode 100644 index 0000000000000000000000000000000000000000..41e964e8f726ce374932a60346d7de1999fec6e8 GIT binary patch literal 1281995 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 Z^{RV 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

  • 7)Lvwx6{i_3QZ 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

    oUyCJSh~>6ZUy6nAeb&S5>DxGSff~OJLnW(9 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�Dp7}}i!CGeuq9qac= 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

    =A`eR4%S5XPCr+IZ)j^z3GQI{j8j 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�dM zTs^g}8?L2>G~oY9>HyyekF|jns%3iC0Io&#*UGjsl*T(TuaYAK(d}VR4~29<6a*qo zpI25r_PN=;T&<$5MoJO`-At1#Dp)>AW9n+MoTHXEY<^@oA5~D5qdVqW#hvI`^|d^s zZ8zWrqIKmbib_qKR}39Z(egu_Pr!y#{%D8!OK=zEW8Z`M&q6p^k4s}}vl&&&@0EUP zArMeEkru8ha(c-2)ZW(P@H0)y=X@4bnn7CnvI!yK*weDCa25%EOu*>qSEo z6&8|u0=3J#@~kfo3iD^y4jyKdq92Z6{Hyr*dvE&i*;3~!p8f#b_`vaz*z4T`?#%Mc zz{g=HsWozUBxJ1-br<)55Ocp?F?6G)z|_G@A& zVW+MY92xT%+kq1rDfJsa1eaQR51ORbR)!O1 zMio^C6zP{?{GeP93&_K-zT%wVEiJX&-hSe=)Dlcd(94UV8R-+NyCfPfIb#k<{Mgh- z2IU*&ucSP{yEUpqLcck(Fo!(91Ktqb`-Ds$~1RsntcPQUI?NWB6ic@sQ! ztzqz~xV#zuJp-Zm&jYc?@G1#7(EZK(7X&^}zNqUl>QXhI3OvsMG(W>PCf_Spi-8l~ zppl++ED*ZWp6}mutVS+$RsjSE@S#m$-30uAs1~3xfd9FJ{VGTQ=U?RSp6}ow)LtND z&j#q7e|BGh{IRRNwk0`Gcvriv1OTAip5G*|z`@x7^dfG+SC(rjv*Ku1AzAiegFD-) zO;gpbbdc#e<~9#NRxA$pnC$N@0`_9kS3NGk1!oPv=dK}V=?jj#+{~8L75ERsbBhS9`VWQY3VUk| zuw?c-mUr=mK8LfX$bwR6sEM}v0n|-9Fo2V_Eq_6Ezm1;%m%d~3z4E_6pPnA0|L}Zn z5uyJ}LuWmww^ua)ljrAh(5VGLoO*Nn+r)Tp0RTS*)d8Ms@LwMr8l+6`$`qZmr#f{P zNlPJtZIL~o$M`niZRIaw;Nw+HVPtBSfm#cvZ}@R5zwzStBKAwCav%nKTUB%V4c}Xy zu=i`ccduwZf$D{!{1g}VQ^CjvJH92oZiMaM!1 z;6A6fw-Sg23>7i(VcmbT&;q*G=z+72ekf@8&N4}k8`b_F(2vZm!O@=KUTeDE2auUj z2!DlO9F2DY?f`|(T=M;m^ndH&k2%l;u#5xiCIN&^(7!)JZ?1q<(n5K7{(JJ@Z2q6G zX)dsxXhFWx0KIW-V)5VJs2!){d7I_T-9-J5=>^?L3%RxmJmm{tXkjM!Fms3Fxd$nT zA^ZgY4LyKyTWv9!1XM4$!WjH`a3N@Q82W*{*PU9H3&=wN+qN_KQu|C?$ER@L>b2pN zFjh2yTzEv%Q>hT09=eKxy=p2KzJpI``nQb}|jaB^%Vmhx;3OKEgtd18E96V`G1Cob>?}bxpHtNZSHF8po{{)smH-nEfgi~ z{c2v1r4?P?Rm(OE>gf84E6CteIY1dYFaH#1rbcygoYhC4*`!9E#FUWRt!>UQ)zkO` zXz{FmtM}sR>D~a<=E8G7D^t;tguZ@e^LZw9TC(bD@?VIrq%V4?x?U`V)lsneJ{Dn< zTQvWbK;;lW&1>($<>W%anAs&^RNobY7b@_6y~g$E2vE19AwQf5^i)1RERuJxZIuv= zx24{xq(tY*+7vK}B2U@cipBRn*>|X?l`@+*hSk>WHc$xO(9&&3o?Ib=4g=5P4);d3 zb%__vy6Dk&&H5A=$N4K$=AW=z><#z$=f5_oqaE^x4+B4+Apo=ayharb?|M+@X8rsQ zovZ?p1Vn4(IbMkBhR^WAYCl-!A7bd8_Vz$wY!aaZzAr3Q_i+xf6E9av!>x&pQMGcl z^#15Iku}L^b?RaYsFnr4kHTFfd`Y(NeVnU|qoCl(m>$uvfqV zr7)O*A=mZ zZ!4e$v>e{JU;gRYap)A?BkJNGfZD*q>$B<3? z<(6Qug&;0frHdg_0Kal#dO;pCUGj-ARbSDFls`*_DR;y4E?)iOg^yJ54N&!Wb5)pnj8FM>*DPcSOz}*kZYyM$E6Ui{t4n-}U+PFG;%1M)Qn1^%Ixsj5X&> zJ7Iho3-+yD|o?DvbFKk)Fkv>N#M zZpJf?=z053s&jrHC`(}VjlRO83o(J-lx>#)f$0kSMOQ4;ky@D>Gf)^OF|}`{UY_`A zL@|mzSp!!&hHJGS_eJjM7I|5voJr1%@mfgvZ3Qn(B)pS+e`ExU@|5C+p&Q^)%?Wks1gf2<^AWMUe zj*l}BzFVa%6pggNCBJmOSUzxjJ@<3}<_!tf+~<-l{a8GC_E#c1Yuc;iX7rP7b7cKV z4P~h@*5F>fDDR1Yl;fY8SH@0BIw*Q8ghq%GWMsX+i2Ugh09OZ?jgIAc>&F*G&N*wD z`i9({dScYW#!RA(T>U)U$yH%va<;vMUz*G7NW4pIjmN|@ zd8!A)r{es=&h@{9mi-V0`2>Bn7^u@W*j(t++)u4fS4(XXCK;t}YN|`&vQVIYhy&CQ z9{yg8?d7V2(V&K}rQusY$T@$cnYy9namPZGJz9T$x_O5cvQh>7NM*{D&&n!u8LeJ> z91a`tk1`}l1TdNvXH*F%F^pj(*i+^q9wAFclozgno|z|fdPolJ@Z`;E2p>iu>i+v- zJpIj?N;*F7u|^Ve8y>M2D07%vQdU`+v;c#X>AuO_i(jjcbzd<7VWSM!SwNs)l4QcZ z1|B2P+51%WB=&$q@goeIO6QzdvJq7E+-GK-vjF^&Roz&be3iFc>Fsc`vim5rKxMjp zaeNNRw6ci7P&t-A=P!s*WHwc?0Q9C)RPItgb6xY51JUF{nWO0gT77@FZ^DL$Mj8ez zKD99u4W)`qX2t*;j*g<^TNqu}EqX-lfmj_|#@+D}Blozl$GZuC07o`6*rEbIs=yV4 zA2(QdZOW2a7SSlVP3<;KHobh;AvL}^VsL53gW+DJ!KaM6@o~1C%Z190qy@JKDNOLl z@I**4%@O0?y&7fdXL(wh_R|_hc(AC#&j#qxA|<)TP7WB3Q^qp2prC3;4uu#}7Kl<#;k!BBJl93)+zj1k(T#aT;O7@QB8wkEPy?!5MMtuwd|Q`JarYKoZNrt z(aOXf*?f5}F7)gr)&t+}Q}IowtCr4H(`=rzlPl^0EM}Ve(#dzAQKf|N1oRn|xG(5s zcOd54sK(T-dP55Yo#;QI2_UN^oqVaz_I&Z9Ns6}|6@}FmC=saUAGH*yeiTg_W0rE- z!Lz;VfP7a48e=2BM0xgz?u8si2NGX@5}&&9$qksb)Qg!{aY}l+y%DhY$xXC0;+OZ5 zt@An$$9eK4eyW&gDX49v$9i(L=7?p6K;Wh|=y;riJk3}m8B-}@mPdqARp!PY)~$1= zw@fLQD{X(K}ks@=Mg^`Gg)Qer+;J%TkC>hHK>8zak z8i}--gP1fA4;X%PhG9i!l&GctRE+JRk<}@Gjl2zd)BN*BPE$3Ah)s+eI~{JBd*x_S zvzux*kfBR(hnr6FGB-^{GP4dxFM;+{?7}Ukvf=A#c~pMh&UTJs<0yOD-7Pnjit@l6 zYyVGNQUj5da!k`tJJihhPy70YaC$HQTnlV(L7H6`h#gX6g%owh=cDpVaI|qdRdv2< zz84*E7vMCt>}O^19XlH4&NzCT|8`a(?K6vpeko05%QrNipzb}?XgX8@Zrp8Hm#?#A zlTE3e{+TdK1h7CoqZ%rUDA7GE04;z{=VVMcv{GTP{HG14R9_Bb?9}Xv9p^K14ZeV~ z0{5hHPaC-3;O~hX6emW%p2L51i8W$DWXepO_S{i05%A{a*MinjK4kKi>I$y>&0{65 zCM=jtD;@&*h>qoorA#eMx|?1-{pB++Tx5p;&KBa)y>mb95v}W>7SUBZrPSex80&p1 zNQ*3k6tB(%T0c6<9XVoB&A4AMXka+0ozc?btemqYqEwwPo;-L?%3P|H5X)3y+twJL ziD&+c2zF1;SZT(`Kye6Q4It9Xv*sM%tJoMC;uaMj;P!m!4(9jP;|kr%;m@&Pu-7Ms z+PQ9|vgt4>({PsNjF4Bvx2AM9+_ckiW&V_L?(t%ib(YQ6_zQY9@a$PstyrnZ`#jl) z#eLnPRI#P0#;`@xtq9RMs0LYs@axvMRmHE8TzS0hfATmQlbF4|_RkcER8+S|erdVw z?iv67(Y^xoQa>6E6brCl0@Yy6<@!2yn;ZV}f2NhohYe43xzi;@qo{{kKI`TdC75zR z%ny9;=qDBp$w2C_Vb{E}BY|I+ij=*`$S&DfB*9Y1BL~Lh+w%$6T;9Fa*|BaY|BsRk zi4rO)lyEN3U<9{JOdjWVdVbhrH2`39E{0-D)6>^ayxFy zyOi|TGx)4=LkTL(U~7*Iow5O&dr%TrRwiB&rxaVBp_E{stQ$A|EHLg6@luqwkiw6G zeLK7c2le3i_;?a}zhOq4_m(P6Kt0S|0R)CP>cZnRL-lm!0ZUGCze~DYFDbhp;`U@g z(jmereR7TR^alg!%P>*bC%PdO?yrS@(lOGae##FK=p1of!sVu^&3X;_G<`}fnh&2p z4mJjDSrv7BAwO-s!f*KA~2Nh zz4;?a1;XjSYsDiEdtC`NLeRHZi#X4hx3<>=cO#tq4G$TK#ILk`F5EzQ4C_)d2&o84 zl7Q5z^2ITyQGLVDI>|*96&233oZ_yS-1ml;_VECO%pXanNw7E6$Of=8y30ULO*{P!$%-V(?Salxj16!(jr; zDarXSYo?5^p=8;!j0jDSh;#NJH#4?%4U`bm)%ty7)f zE(gWq25Uzoj@FW4jTx!4IMX}hPNv+$ZEHlNoI@$nQVo~eRebpB+hTX8LdExB;gtGkGj%OaMmM!k>SZQD1Vq*(QTQL&2daUCSB=NB1D4*PE4F~<#?Jx zgH)5NizP}0GR1&qLJ#xlp8mJG?Hw7v+qdi6=BKoZL*r6f+@54HqoQ;wak?4<~RT_wRG-P9-%@8^UafxuPqMhpqPbxgb_>4#?X-*fuNZt_AyS`L%UHzzva7 zJBzCM47sWqlLI4(73&A|T@10J=3WHp&>=%?I$-5 z4cLbnQvx2ZK7Ph9?ob>FZ1*xfVRH?jN-)nU4IE2}rR&?;% z{q@sF&l=oRHlgx%eL+MPNG$>obc<`ai(T)j0liu07!?Dv2)b6B`Z;-jJZELGfGKH1 zN16vQ+xM$*9mP2*PUyda0h9gpdDE?V4`QjufjI_m^oIsx@W1(k_5E264^Hxuab*GJ zjN$sc(c92F;5DFnE94>ZF(!&@8$DRnZpavgHbc1u?tfjB|dM;WfawTcuEd-~e)S&xkU5S~)-YNi6g!T?A)I3q5 z=X$th)LRFs$>83PYB0hT0#d*SjxUgwCz(H_b&`A)X*tKlnHy29r8a7Ur0?3MEEl(T zC|gpO?`zZ$$=BM9(`jh>$QKI^hbN1@GmGuc0Kfq z(6+r?o$|~8B&75`sU>nZR2Ho5bxYYa4$~sgQ8pL|?$GU*iQ@0e%*ce>8f9kevPaqz z%i6e5eNlz2Pb<}<4ZU}Nk`xHS5#7KJv;M4FP3P@St0`r<@n6u<B_6xzIS3p#i958S3L-_a`y2+PS*7%~9!slJ(Rh|THq*gbOf5`_ED7`h za1>VlN-#$%A@L_DAO#2Pbcl@JSlt|9#VQ{=bToBVI!$vJ)(m+t5l@6slH}Na|3GJ$ z+GF6#L&W}Mc2T0URZ1M&_ZKAZ*X)@c8p{>Y zU=0@8o3nl{y!~#~$Pm5P+~HFBDAWC^8|s|a`u7UWtbzYmV$`lrP9joB?572oON&1f z-xYYYwQg*@;6!fiQh#iEF(3aRB?=Zft8R@C*JVM`M_|o(Ourc?z~UAp z*+RSYx80+7*$iFKmD(^($3L`dH&v&X<8U#4(A%}QG3>E73HHmVl|57 zj;Wy=evNbZW{e^7ces$w%|vV=)R<#ww|C1upEZC;`}Y^meY&&7C5d;;*=gpmN79HE zcQ?o3x!z1ezsIcyMJsyZQjgGO*+yndYUu40wXMz%#{4BXHXPW%6OOi}0}?{p3AJO{ zt#Y{~gxF+0`RVUhV4K* zOM&U7#-5fZk5*V?o1Rf}#Edvvfa3-y4cdI$tF3Lk=Co8J2Y!k?5?&d%QIBVFYM#Lz zaMRnLA9HDc-5gcRl#i6<(7C9MIcIikdn+gxGZrDL?MpoRU`WON!lE-QS|ybnc1DQgO?KJg3~-NdDdwu`FpwTn94qoHnewE zR_etire4elsm?QcwpBN)C5<&L<@^0ogq3wiW{p$F2yK^Fg*YhM*%&`Pxu?(4R z32FX+Zp-pGmk&54Jf@1G6_`O}WNd6<>6h>qJ)ec09bc&qE?zD2qjq=BzWnNSd^}a- zsVR2qZ`onu>6qjuxt)~TVfx~7@s5KicQSoq9PA^tXK3z0q9m>Wmhw;PG~4{z!#*iB z_K;3QFQs_~L`ok@R;=0ZY?w2RB0DjzW10GnF*w=Iw~LIjVr}NQ>d6Yi| zWeM!>xiU-AyPvGvZdoG~e4x6#g~zfN`1Gf&+g_OGAi_B606Xyg`c@%Bn;g#KmE= zF4C1#qCyajuYtImI~}(bse9A&wNCD~*1;>Z@+;a8IeD84ugLQNdz$DG#F%b+haCkw zu8KmhRz)f6%N2Xw1ZI+Lf3qO1y%Gz19<@HTdiTNmU2Zs&Sz%J(`}-^K4gU%O1 z;(FzW<0TOe_OdejNes;#rSAA4|Eu z*fqn+>dZo1c+)d?)A8_|GoeJ}1XJ=P(HZ#9sS&Z~Fc8%Q{RJgn^X(j0J-iUlFIZ(( zY=J!SR!f_55}9MnC4q^LbFIHHjP!c$6zkZ@N6wwB%*nrdJG%1W3H#VUEY*+a_=sqt zqQp`2r_`I0kviW}vDQ@|ABT;23iY?QW|@gOw$8irL{$T^J`!vq&RzSyUSyP;CnG}M z7@wM0W}J~kMCB(rL^5$+iCP#}V0LJHdCua=OV5K@)ta)tP1n6e-mhGSvxEG%n~X1w zNiv%g4n-OoKb$+-Cou%Db#dOgTX@V$*&`%zDk0GDa>34lM252Ak=U;}@5-3++JcBI z;iqtJ7?ROI38O~P+(6&ZwBUvxA^!)+vzuH(wCpzq?7dccF z-V#E|k;%^y=bu!umB^z4&V-Yd-!`1<@F%fa#xb$IZ~L9qfmc0jzPjVMw2gRMEdh+ zN4r3XbW3a7;KP$i!!J&e2YXy0#n8N7y+LI@TJy?1RJi#|qd5UpmN@Rnlh0-^Hpq33 zh7Y9gC+#!J83hNV%HQlDY^5P5zb*Eqo8~3`KT@_^J||wge!cYUUfuD#I*z!gR#%Md zYbW2JD&d8n<};%j-H?cuvR*%FyV@_lUH{&(0--%iGFNgv(VW(} zSCTSj zb@z$y`7a=BT_Y|ny>e#&-Jymg*KKgY1D)|ts^XCT9buEo9ui8OOSAipC_qbUPbZ@kd;kDbow0N$Lcf{GrSYqjVMGE01}6xn6vQVauq73 zq@4RY6;jEE*r@Nnt!LVI{X8u%Po_Cne;gvtBZLl~C1r7xYn}7%vEST9ELkL`s8eA1%a)M5{@cQ7Z;Dk%w12(PeADM?fUse#jIGVzygR6oMK zeU1Si){UhdA`oQ($Qwjozi|uQIR0&AQQLK02i$ybCFpy{xiw&+v+)1J3up*N&rCy* za4}|%QTAJEfFh#zK_nLcS_ckl4=+AGx(@EpbAQ^F=X7*@4J2&l_NKTp3dSAx;tU-2 zoZ~}LL3awUA$OQfJVzsvN-_C&+%SwLmV_!w;RL{89+!Lb>-60lN(b_2 zrw|ltIsXEMig7cER3j0RCD4!Tdt1qh6&+~&d04JEzv<t79p7Ke_Xfl9Fv6cl>bpkP_fEk3fS(b30eD+`RzpkIUQM_)V8;diEqhoO zS_hoA0?Q-%dh*5{;B)-*KU%{A`+FvU;&C7yWOIS&1g3h|RaekI2c`dU&3_G%{=Z*? z050hP6d!PaMKGTSv^a-lyVV9n9c}=yGPL|(vxw*(gMZ#jJ^kNN|2~b)+HW)<4cVA^ zEaJ?Q6Z>!9F7kvseU$t96!J%xfQX_U$cX&k*8XIxw%rkJ(T;}$Vlqf{}_G+`u;yRtodL1-2~|O>*>jVg9EJd z|7VYUvl|R2Tm_`Z&jaw(>Svz=Mt5!h>9QIdVBmhMvIgl`W*yiT?VB0tx;-4M^JM2E zN+IZO<6J#mh$&}d4KSsC7#_p1|GSi=z}fM+UaY&1Gtr-?T&sP1=yIg#DE^;9f80-J z?Vx)Y?vo^j>b3UWI5F!y}a@B0tWBgeU_$j$1`2J$UVRV8eV2QFUNk zN-wTq1Rm(qkL1dF{{9wM1Ry8*qKdJ7;FGB&z|4WUTSKWmgDWG-Y_n@CV@O&`m;9&_8_`;7E55@ z=8?kM?R#`GfEUcL3Fw8l%DXhJYOmqzg{t|J(^)msEcYz#H0THJBj|%P=(&oxw7(Dm zk=3Q3;cHzJtOA)L9YN+P-__!L1K#dwH!$gMu6ERv_lY>N(X?;g(n>wzJd*-fNF{|_ zFu}W|z0bR>yy~cX@j~yg2fq0ws5O5TcIvIxa1%n=<1=XiVr##oy#9Sj4-A4U>#I%K zDFAL#oYYlZ7l2qq{F_{-)?L8%_xG4=DX@&(<@b8n0ftt{Rrvy-vI8VThYauz_-O8G zd=-In6K`wPSL;oqv6N1{6KurxN~aA!yg=$|h@lM!DsjI*pN7t%_liFrxg&k?2(t*= zMtAU=36!b(=klbf%yb(&v8W1GZ>v9ZRgPH>^H9TfpP4CO9+Yj^QDfYu0z(yVx|EsT ziWmw~!Jq1?DGOFG#;|2R{9%bz10w^`5Z^E#slaiP>QY^<<~KQhg}wt~9(tcjsPpcM zVtmF$-+`XW+|acjV`_{(0|w`B*EeVP+1ySmQj1y?W(ExhV{DH6w@}7^xZ@HUUi+>9w_-<>sq6sPEQo+MFTKfr>J36~cnV4UXA;u)!}j)!|#lQ7(T}xljfq^eUYZ zMTCi#yoUTPD#A=ha_oUMzIG|vse1iqafm^`5e~E)JDaQ%<)tL}2o8zr;$6$0X`z{^ zWaQgQ6HnzYrfQj|-q3j9#OnpA(8tiI8yHG5*!=u;BK{ zn~v|vOkoph+`*%?g=_t^>$~Bz_by?ZX-`bzOc&K0EmFop^fVZ`{wSl93)vQh6ty!! zwT0mX+Nu|-`ekK_wy*-2{RKbU`ttH|>VHs23T;Ay?~;twdB^ z!j&DWG;|FeCZnk~v6R;J(*uq3vx)*SbY}KUD|oyM4s4}PPJBGW+_ViJ@Pnqx>Vb$} z7-^D{8Pgb-LMi{<(?DJWs-c75?L(-qMwZJ+J5rvKHSc&T3PnAn)vY81S9~R(6qC4{ z!guxkc+cnZM0CUCRcGc~{rZJO#NEq)I5zIBJPrvu=uVRm=mpj=x47Nd=0oXy|CLC0 zeFY-A4Q6aeRDkw=e><~pkdq~%%rf#?-0%L@M|qjm2jzaA$dj4~Sx;FPMPe_SN>+3g zKfV7aLm2d@{=6!GlJhHIQr;gbN;)@(9ig#R==ba|9JquYhG0 z^4uwudY;mI)VJ%4d2TggR}vDk|FkT13o(baMisWWd4Qx)I*TuMcccU3W(pLlFuq@E z=LsJ(h%hX2Vz`Z+jeAJ^zJ{uu7xNHN)N+Z}Wm^pw6s6N7S$olyavanM(#YjHiY~7_ zTr-Uj^aweyQv31m`2E&sZ&1rWPQM2uli$Ag+QEXqiCcc|y;NxgOl~8!lreXcBp&bO z`sz^NHyYYejFQ2qww^Kn6BiTqvF174Ig+@Ji72(7uIg^|Gb7NsKCbUgb7pw+rD2zb z-hjmA_-I#;LOY#3V^~O4JxczOdBqIPou6Bj0(2&ZnZ+smO|06{5!IfJ6TDx!kuogq z$K~Z}xpz1p^xDHTU5?9-);2;^wVOo_OsC7svxw3)hcz2}QdIN{T?p@tLC^NI9t-dp zlZ8>6CR)$ZXVjaM1pfO*$xUVNSJ@WOlHRzP!*7o(ZoA(@xC)mZ<67)P&lvN!wLkTM0AaC&!6 z1k7MwRgmztghTI>Q*go6_q~pVaxuSxvI*{Hal^TW&N)qne)BmEmR^VkwbCRfCX?DN z&6J)#cKM!N$pxKcMXA*Ar4QB(Fj{aglRM0PP%5%#vmwW?NcikgMxG zB+!q(x^yUy%*<-6tr$A>#cjYk#+RJ4Au zC2)f}o6jg-P)|3v)d%wR8anE1VtuSDoQ>BJNfpM*F;sZ^iRS{kZ%S)YlBqRR$`o~& z#qJ?1%+p3_>Ocg#S+lD_y#;u)Y#FFamzg|%4QaB{)4Ni)((=b;MC05G(%f$RZl#84 zP>hS2-u=`KYT|^{aBX6jzaXjU`x!3mtd2>_IeBO`Z1T~r`lmQo5WWMr?dY_;e4l}? z;?2}fbw^HZA)S^vN!_ov-%jvGEr38Re0vFgBnA2Q+PGjFwU?Q{%NB%c=^6}0xN$6c!C;ypF0i8dyHPRhVtHLY05nI>6 zorCPEg_~0yA@{ z+w?C;@_3ioO!OIq$>7Z+9TtZuSE4M~5k@S4$;@oW?Ez|hwr5FnVH_MYXdb12&t~fO z03PVnSX{;EWXvS(6g6+Y9(K7A+P9rN`K2IrE`g#zEllO|;1mqGD|s0iuK6?XLFLaf zr~}O$OwhpENYuU> z<|(TF=xcCOWliUUc7j@|=*`QUs|YvzByoP#imUgysF%KjW1K0Kg*A@4AQsYFt9ypq z|2fvBs9DpsO$P*qKD)|sqpGZyOgE|18ay=A4BLU~t`F1=V=Ga++BYH4E|LCqKrA|Jdh>niIc}mM|+)S8x6D403qO|X9+-jVKYF5-@_w*g= zP;%GeFrzjMlFe89id$kWVKP57(!D2=K#=eX%yBIBIQg4SHnonj5Q&nUf};0jN^tb} zaUsnxTdDEWitF~0^qVv{KV<{HqzA^-x3lIP%&vzSIPq*IuZhr$ae7O7E4}NoKaQ^v zvJZ1i2OMcGsy1ZC$ZqyFv9|duZ@04Ia+^i9Oq5b|je28Np}VCOG5WWKKKHYeU?jLz zsD9>Kv7pXZv?-}JcM_HTZ;rKP5ZlLAZn%$Cv_y5-Xjpbtpjf{jx8;|@yoHuZJGqRc%FiJa_zacgp5I6H1VeQAQ9>~IP zeB|hEigRW7{nQQa%p!GF7+2OZ4EUU43$!upj)Bo(hUzw@x7SorT1!r3+%##%)Y9Ms& z1$$7@EGfh+2)%>TtYT>c71zLhEo}d-!@;SS;(S7gnmJNUKQaR9ZJ~$HOQL)d)&Zph z9eITcVm@oK<+{EwKb5$X5#>cX!A0^5kBW)s2Jx|!6d&NcglL?$&4rUt8`T7AAEw65 z{FCYuAkdI49;de1o$@GSI$H+Pn!#SFThuEmnKCre5cl=}K5 zPtd?)t8RD!Fmm4$(wQt{qL%c=co-3wX@xi7TM7vEwRhC>0u{yzf+3YjzQi&i_x;0J zp=Kj(8;H6CwcO_2s2I2qKeQgG-fBtJRH4)q6Pwh}(ifBi5&&nmX-s~3Y3;jT?#Npy zzv<_l#-RHmse5<%oMs(-DJ#rxzFBHZ-pfxNXM@c~FAJ#ss&aC6CLy_!w+8?uu;JM^ zdbgl6`!v*T7njIgvHX9Gv^-X|`t{pwqLfbAI3|NwZi$ zHO%Edl>Hj%fW2CsSpQH5&HtVVc@b6djyWXct*ST7I%O2Z`b<$oV*T5D(R(v@xbVt5 zV9$$g0F)=T|Jwj%Tz>rhWNGPHG|qEb@WPF%%on>HW#YAI%c=BPG@M5vOCK*Dwe_V< z00m`9R9C%P|29ESgWD<9A(Tdvt}P9bH^-s-k$iaOMgEm>7r(XSaCa4QP32;;riHG* zRcAn<3s&wl+*o?6ZGTI6l_?b{&r?2@2a&gif`8XxFM>qlc91eD8m^FTa83`|$F_Ft zlQZYRfz#(fkMqXtoGfX1Lw>K-CZ7QH+9_)gDbxxsb4NwysT67((YNdLVSGE%9IrOltPr` zeJk^mciF)P&INa!+_Kc2YgSrl!jR1FC3a=K&loL=i52QO-l!heAdpHX^%Dz%hY=&a zY+!zaOOxXrYOE(>e0)mO$&rYsSI&(yqM#5f&XM@bvKG@@ag5ObZY#!WTa|T%ef(Ks2ma$9^We|{>din@? z`+u?b-ce09(Y|o3sEF7=1tBU}kfumcTEs$AjEI1M5CIXDCPXGeDeg``<`>Yd%v~5b?>_Co_}<))?{YSp1ptjx98b2b7S_j z!=)*Mn(?fep-=1BiI^vyJdzK;f|~(m$JQA0J$hNuD{1xeLuSN{)ov#ZBMkN5KJf^S zi+e3_ZHsQZ%L@7nhimb2$w?zs`VlEM+Te2Kj*MEhnz%MI??Wby!r?1To)q`EWSzS@ zsUzaPBT-BPYpgDpCHhiHH*e*o{o1RN*96=erR(>ebQNG8DqZz-CS~_m*?y6!IC9^v znQAwDsly~VS%s4feOuNvmUmLSEJ4B0S5xy|?0WmRQUsge@=w}_Pv;HA=rxwL7`CsM zFL@=vQwOS(%;X$JK|5 zLls|=;|_kgBT--V*ql@*E>&Ez5kH_xe)65<8k~4+|LybpH&2W0p1sBLQh}-x84&y7 zgnD>coqfoYcJCO^3mF2BYHQD^D~(%!8r-k2^4#QcX`dZdx$T#v%j;LY5o9hIJKnR> z>SaFw_A(u*9dRpXlpoyzux*^`K<<7CJ;8Fi!Cfo!sKH&}1S-*@9DWLQtF3e+60v4> zjI+>zj)YuAc%+!EIrj8qV{>&6Y0Zv!1FMa7{rj?hTuh(37of87jb+Mf&Aj*TNJ~Y1 z#~jAj`M-%h+%)UIEIeNq!KZElZ&e+t<5{Y z!QoA3=2r(c&HAxvbz#5;%Xe>81#St2q+BVKKfnH{q=c1*`e(&pSSnd&#l~&S)%^Y6 zbm7w85h3l3k-JZe)%VS#lxNa#w$N_e!1jBc`2hmA=dxO2f&_x9Z1%fuXX;t!~w}-dg6n$KKx^IuTF`js&eAn9x@3w`0s_Xye1w~+| z_MqvzIq3jFa9(xdJAR{KEZ3=UeXw0@iX#r)(pbtXnF5f-KDbj<9K{bnrLsaLt%c@$&+;sqjzoT zWmoUKC=b*v@auV7R(PuabAsuyj|tya)udIe^1l-LI^!F)4TZT3wkQ~0{B0%D*%8oH&=d7jVR0$j>*i83#A~)#1S#buz1cgYcWd2Q$o)mTe5U~v z?^4dwhqtkIdN#RNy$-(MrK}XlCm`;W4t>?qJn@noMxew!q0XtX%t+1x=`{+5g19Bw z1T`~71HMNIYwPPy=tq5YU@CaVTgs&+>Z`7ci3xjiAtP+%(z{Kg0^KYg9T{7@`wx$7 z(R#7Xnf+j>E)xpVs!(qn*ZWjt(~_TPHTJAi7;RvbDQpzrHt>dYT=mlJ4pxS2lpxbm^mjSr#ZrD(~^rhK_g`OYTe{k%H*IdqNVb@9TIE__ALSBVq}^i)CHK=l~G zENB1o2U-JIMxLP)rk{_t!OdPerOB{Ex?NziNU}NBAh=xGy&0a~DUIp;B(mXR>$6@p zd4V_-)#|j=4bhW+OcOas+PERZB%Kiri$9Jy1uV1+2XypCV3bMCW`>hkb zcltP9z0D>vMn75YkVx+FZ|j3*T|W`#maso7Ni^jsE9GR2^_l2Y^*yiHu=nl#uyFIZ zXN+|=7lcPHVd_~9jhE)$K!j7`Lh4JOGuDJEP%94%o-cQ0+l-!R?D=LTTUG~Kkek~4 zgqfnxR)9~nR5Y!cF;L%+?nPhZc|a5yniMV=6ZdX2#3oHnDczC)dWTmEps)$Fv6EVXN@E zM1gb8%VQ6K48_WaQ#g-mIX_C7DWdYjpP`aAw(rQN@Nd%tgsAg4hNTZ@H$EsfdI>L@ z=7U*%NE0(|%0_$r=$lsalo&dC8#aa1lXq!G^?+yOH2JbNVDESNf9aKU^ljGKEhL&$ zUWRhLme<=AP&JqBXS?Xx9K9%Mp!DLNsW#s?mnXwcY%7o6FN|MzWGK=wfpu4RFJnuk zq%`kM-pC!NXEUBy^%q$$XWa32`|Kc`o+?;aUN7p^U=%MTd5AHy&b#mnsi3BEnR~OT zd2OpoAP1Z1a(uE0?sP(x%qgU)+ za*f1EWOrCiTeMCMKRVUqirEhPg=ImNy#tnwn|i-8T`Dj5oTKX02Pc{Bd7jSes3C-{ zo!!2)V4~tq56s^RRo7&ycT9aqaBmC6J_1mt<3;T6m@*8yGB-jFo=3$WeDwu{#u5s1 zYP9H-Yyb6+%vH#M!pFWPGK1LqBZnEs1F#N0sYMe*K5;C!0w`O`; zd}0B=-X9ctqu6tdjV=%HE>0TBe zcH`aFW-JSoSy~eqSJ@+WZM_()n0b`;ehiL?88>F)FJBx;^{*I5gE`br_jO(|FtEmq zq6dfKpnk$sF+Q<(H?XBab+IY%p9=1|dY#gNQLVf6xXgJ}OoB5l;J_M11HWR}@9AsU z1gEAW?qfOX?`>K?hyTF?TXcff_Fy~HvTXeiqb-CfqQNPL9z@ndP6F*@f;8pQ4uL!Ta1#Te@wlvr@m^ zirwP$Iu6t9ZO)@$@3+<{FYwh?tj@)LnTw6CE!9V9$P}FVp{dDBkF+M8GERCuc*rT* z7Q3`V15cl{#QBRqII&x_Dz|~>uiuo10CMWhcDphK_Xxgo;Bm)eD71N*?K-7*ThyY@{FzD zJSxHjx&SQ5;y-ChoZuwFXT)G%Z)OG=s*oZBLm)z*%;!=2WLYI4)c_6c!3lKpLKYpa zn@35k1E5*RF`<%yn9UeU6S+HW9u>Wb(^CduZ%|Qe9+lR~Va=nifKl(yqX-u8+X*Ty zNEWRBNIIK{i59@Kdzvos^4`3XzLd1>oY~$R>10Jg(HR!&*H0LEO*7Vr)H+SB<_ze6Lh#1({l{UoS7Id<$!GDJq+vx01cl9`vZAGP(=x(CGX}@91l8qWO>6+ zxxp1ZzvYI0G~udv;Yr&J%oKqS(0IrTe&>Lrg`%fMs130!Sf8t1Z&+xAaSQ5{nze~qrs%UWqxaQKxhI)Q2Ez&8_X2_rMSr#tKEF5nF`@)zQFIVGid)E2e)3rq$~otAuz;E_?w3P6bZ$KAVH!d=`Di zA23S9sIUCXLIxS2T(ncl9Wjx46k;R)E#*H^=YN|nftzEhR(*nG_;A__k|X^kKb zKY}=Z0CCU&aX?|xKfw1t^Uz|hha!0Q2O`~ThZ{#(rd2^{N!BFmI{l7Mz z_&>67=l_BUBAcsRlnLa-AT$U;Z0bya?H|N0q$4q%F#0F55nITNYHnC96#cn)NE~qT z1wS%@Y{A78TnFPW-XB7N8(H%wvU&dl29|=zW;{w|4{!r8h-};dj6}B73Gz=8#PzQY z@FXq+nu_@a17p8nfK)V(`c1+R9KfWqW{gbL}1^0B;Xn_fBL1F~HGrBD&v+{b8c4 z#PRFmPb1iSyJm%gOHDkbr_r}Kenm(s@RK!x*OBQn9zMXgu3&&~fmNn5ZZ$(+C1>5n z9o3uo?3}ImCxgQD=22UrLZ?52kvQg5OurCFitw}?_`);r3-=4}(svNn4fn;w46G;& z2G6Y_Y+H*3V+H}+jG?k$onY~Ryus{0IMB%NegX7O43EtG!lH_0*({uS7kRWq^G#6H$?bh4%(n> zcW61OiAv7q@=wB)8*z9XRK9>=K#G!wt^ocQbPKS6o0gXr6V}Lp8=*LrGzApY1jboV z)*U3XbzoAji=jsKVUMwwCV4k!A z`e!p895PlJEf0um2?cVVoqDoliC%!DOJZqdqTC%9zk9P;j3R;`hm#4B>yQj+07$&2 z7Lv)~?+5A6U626^u=cf|1G_ZSG`^Ayl*Pw(@L+7yLwXcsirG&YRF6uH1y2eKYnN~K)v~+zFQz2s|3PUp-NlSohND>B? z#p7oUSt^Dl__pp;N5>Vg^cPh>&v}&Buc3h2(<}2kp!O>g$gl8-v2kG!Aj;1)E}KAS zLzEP-t3@LJ?3~J00Ac+ddNjq|rg*n;TaS#B-4b{}+S2hL88hNd2lD_a7niT)a~FsF z&aH{9K%V+1@OP5^fd4%rAp03a$e2V#x`#55Tp1I+^Cpahz@d&ygA zjx2@j$7zMALu_W#j5pYsjJN{X0t-G5aU;Oj-)Z>wiF{*V=dE7UpUn_oa)%;8TD2P= zSopMHk)_^)tPd!H(S?}wO_-oKse^PP_2+NsGv40Zt}l$dU?ZszW(xeoQEKJu&&(Uk zds?o^S~@yP`LgH(4KR4J7D=8gI?2d_DRVQo1=Tp*+6^zZoT$E)3ro8kWcu>3&|Vxy z7DVpl6nZlY3n5^%8JxIu65_5=ASVSOF_E3J0OVA%dpaCK$egjPY-?h_(qUm*M(|v< z|0{O-jX@LBu&)!7T)=gMj5G=j#F^ADbLlOgn|Yu59O^!7xV0a?bv$6sdy?v&QR<3h zg~s$yurm_RiT#UsAwUXB4?s>%q{bMtI2f?gpkx6PCor_&-CPX&t!O0FE+pk6^ns>+ zWxqPGWtIX?6D&vyf;T{kg$TC5;la$afiobg!gWGGRV_`p|D+K;YaaEFR*gs5slfhs z(A1@8&Agu2OQhqD*V)eWZ@l6?$w}k;&;xU37JkD;5SRw_$Rm3*Z!t*k!asv3A2*HQ zfNznAKSIOE4X1l(;J#x*zX!WGG>ssrqTsaojFtb!gPxhXiALuYn)k29=;SV`n<(>X zr7)Et^%StgE2;~g`FxluJ8#TQyA5qDCxL~ouzVA~YGer0s#z%)TDC1{9XQ>4mgK)xzT&9xP2Q!_V{TFV#+=Jb zPu%|?Ie-P_DOih8bC=G!3i&qeQ$&qVvy(&uEmgUkT0tL#WAH(~5srrz6V|zjzeD*y z-lS+Y>0Hj99P^k7x81_LPevB~D1JRKvknBkwD#P>NK8+L-(ll1HrSoHotS(Ee0{(V@Gd(Y z#~&1nPd&C~Ji>EdwfrN4EbYc8r$>q67Zt-_mjo=T9=(CTB%LyBm8MEz21^>VFk z%rL;be;o%6mpvaACsgtbvdZM&`&f}qU$bSrAz`#X{McZ_ciP1KPd zPpuMGP!Ntz86r$UDRqg)k4Y7G&2;wc<6F*?NMot{nQH(R+z4Mdi|zC8DYMUDKB*1g zq_xt`U1+HF9L@DZY4@U9V*HZg<#5f3YVRG_V)T2DYtEzAJi^5C=thN;TJDN=261+s z^cgw>MS9u9Fr1-%BfAIzBGu>8tIlcZi>`{3d)u1u?tI6wpg}wHn@i7ytt7o~xzSk; z*=$U+o4c*`d)B`Cal4FhcQeaN!;#n@!2Y5neq!yMUF%tWt+Q7{ z#RAqzn4j3|Il(@&v-agFsaHdz66EWvW2$zbS6UzUJ#r`fL*TU~l)4v2WtGwyy0s%W zM8b96d~;r2fK4RoDD(HLu)plFyYi;4XScn@(~D7os!B=Q^LMi?)Wf8kIM$=LYX?r8 zh#ub*xDVrS5MO2eY9vK5FWcMQK*roOt1q>FVLX(q?W+(BheNC>n&PM zk5qix+r6w|BU)$Yea<$5%cIWO+N{a2#4!*nnE1g>u|b}Ei9>zQr^*=2GLRxTv5PlK z-~u^u=K|KG9=}`r{PruIL-*=+38@juOz<4EN@H8UoN+4L)4?9=OFV-|aBSDXlaPS7 zx|*`lxz=zVxlLxO;V-M7)eC6|F^4a8!zN@=Cl6o46UKc9NMaT z?m7BIlU2RLIAn>)Xi)Hq)CfI#xE+$MH`SV<6=0nkF$YHqNN0 z%fPZe)bB6`T>XNt#uxfA+t>rxwkEQ(W6^0Hmhvbh2@R~$=IJ3_N)lgO8ngDu)EC7{ zLBJhQSh63ZZG`jQU`)-9E;t6yh0nT`b2$QU{7bg+=*JbCq_ytwUGJ4zvNeC-qOcFt zAt<4oPNoV&{97a%@0H{mz3x1f4t+9(_Cc%?3vQ%61eio6i#p*z=CBLR=rSYRZg&tE zX3)N4v?@Q@*;x7X-J>fmr#r~M-zOQbJYcQ;ir3fPbXHaJx`QA6ecCQ;TLbQ5hu)^4 zXUV;$1{@1Qx5z1=sE_c4m-?suq}=t*byR9Jjt9oShC2Ms#S9Ycj&zlp>s;FY$i;DS zR>;JKjeBCXS=t=(u$mGpz^yLvLs-Gy%h)(OQzn!mwv0~_S~YDb;KMwy7_ zi=MFqQ!k`)?+;6_SJ|hy=u%H(A-gTRolo$hBus3Bi85~$)#@inK9gk?1Y4GfO#tJ9 z4{RK#{Uda9(bqV@`||Srk}sHl*>!L-_zA`PD1GRluf5%m(FCkd>3{QCR${+ zt(iD1$U{tCoO5sn(8Qz}MO^<~_J@}@U;3OMeR@B6Z&;$R(3$QRic&=J@ZFO;&b_7a zYJJbSvSQU@voepi5^NxWg71?U8G=_S<}V*MecgE&91kFlDojsNqGG+m$dwxyH>)fT z0TnHi7Ewtstvh=3e&C_2p_ZQJJ|;?+2khisVLqb9e4v_S-qDrgyOL7~ zr}&fhgt1LVi`PfrarTW9xnemftoboku4HTJL!;5O5}iUyAz#nuXXOOS762Iib-kAD z#*#~)7Wz;ILG^OIHNw0!BZ4@Q7Bv^~XB|fb_ua5Ou=1ETTJq(=qrq2K;mib;x_Fk4 zkeb#fs8eT^J?Jun<`zrp*#Sal$WBqBn@5C!f#U{vZNz$rm-1B)?_)_F@iaM~wB(ez zVauz-I_X6jkg@u-hn=j1L4cXC;t()j{Mbkk1b(!JcjH`g>#CU22Imn8+K6Q5l;j3ICgN7%KGsr);D!e&kwxCTGE9yEp^hGw=2^*2C z__p}q1oM>-*L`9fs`~dN+HQIB#Kf;|xpnPFJ;x1mf$<(|UZCpFrl7HjonHqp9zT|15u>te z(9}~lk%PmuF`Oyjd(}B~)n-GX`>@2U+kp(h!x3|lfy*D=+7-Ly=$<=b`%cFzFP8co za#iWXJ}3*<#`1J+vp0JkynK`8>}pVzW6VIG%7^-Iu#0^jLM^99EAF$y4~AU+F1n4T zR+VI+peOT6C~0SKYZK=1l^KDy-4G}Uo^Krdd5FvJImnYh*;zL zuds_h7J^^ju)aX<%L&?3*z#$0Ei-5F=Y8Ap-)@Z+=QoEwF)Fkl6OM$^z1+eu{SNGp z`VTMy-l`%>TBi0Io_&IzEvp847X|G(qc?q9FKE#OR4}%P7)U0)i6uCTu{LpH-N_Xc z#vRmWw&7?cF;OG&bNldWh2)bT40;tYxa6(tAV1;Q=y}v}Y+GXpB^Ls3cwHEI#ZHBy zmBQgKW|=2#$hekn|L=uamtloOL$XjRL>cTT9kRuZ8O7SsHG8q6IAaS;MIFN*p9qpu z$Bpf0dhBHui9JhFI%5xQ+Is%__&lmm3OtAv)JS$6$4o>GpM)yP82%dpKexf9x5TeP zd7X4Auut0A7`FAP=F0T@_pIWx#y;lcv^(%K_kuij3QC$en&qfK^xw(TLXgE#ILBPY zFOVN&D0y2i?rZS5IEq)%ey{yt&DLcr*6+C!rWLtdGh{`iUi7`i?IFITU4!;jdq(ai z_=1;iX{lb4xy^K)t(opLgHe_pUMjq!tE_sY(WiaixL;!>;m=HY7VQ;*5R(*)hg+}5EK?L54w zdgSy>8EdQH>xNtbq3*Cu(Py4e#?VmQ3$X10?Q>ZNwHSGZZ<}_44RX^X0X#j4ms=vFYYA(4MvQ0hJ)f1C z>d&rQ&yn(Fh8#ZR!JSF&K_JD5nRTlL7=#~K|JD=}$ax;0A|Z*rZ;(*`DmnYSh|EI` z^dpC{nfBCzc#BN;lwtiBPAyFVv~1XN3EXdxF^_6wod;Ca6P6vT$!&v#W+pm3$~Y_Y z%E=(C-SGnv34&dEFrD+LAa7SkqcYAqtX}MEkOuA=KEjRd>*ABe?sh z)YE!%#${1DC=$^aE6!z%oJ~rzLzJL32p|dhmd)Mu>@=@vhhFX(4$h?BI0o9hP2uKk znBE@ZWoU30_j`ATc?Gq)X?D7%b%NakwbX-8gy8eQ@|h)*1{ufeTb@R38ro4MwL!`k zo2Z>5IXpl;&7`(5w83H;+A%M@Cp) zSF-b|^g6$<@%-S!RB;*{@2Ufp13(%%(CC^h;wec4>Gq$EZU*ULIQ5E-H?XB5&Oz@Rjtu|R5;vJ4H4s03~ zA5R6jCV47q9gLpw=xxoR3wvEU&NDXjDjQ^HMZnM`Fp>1BiB!eE?HVDmHlOoy2}(cjEeKY|5E#B%Z8}-!cD#H>Hkek=KQzsXyk#t($6 z;oj+~LY`$TOV}@5z0SLT1EDe*>@rBsqXw3HVg$VvXLEkma%Aa%T-ReCes%=Z`V=+z zV7?J%-PC3Kec2y^iIa922j@|*eZW6`WMnK9u+)2BP;9QHQGj!LDny-xzL{@}y4e0CrRz()51uiP`cnU${Md=|?#3Ls zg_kn0RhCu8itb{Wqt#rJdUKok8RShI9xx;6vWk@$TZ1~rB{owG|4;-!k2>??W-E0z zhcwt)KM0?(P)VcW7~g|n{~e-V&ElvL@f{eaUIm8z;39J-V^PejQbXxAnknnT63ZG(1ms=L7*Vw3U9%*%Cp;4?R*}5-t!o_< zTSwh?%r0rMDs#v(CqhC&A<^{m?CxqpOqmOL8+JTr(pkd#{S@zjiU< zq))4LN%ijAx)R%Mtgvfj9DT#I>!cpue750R2zq6=37_1n+36;pD$2l?GyJWygzNRK z%p$7OlT-&KGTuocH3=e#&Y0n)ag@|XY84S=v$iSEVf~It8t%PZM+XT$V&lC*dCz1% zTH*bOQg(yAugF^6)M|~~p&_~Jmg%r=V4eD*JImsy>+<)Q9kOY>)nZo4XBv3c7wl8* zJ|0Ee``%Y&)!K#^!~{%bLohp!@UH1Osqtv)fkV%BO?^IN(6?W}+gbdb~ zy8~(pBFlWTPHr?ejWvr^-1~HG`H>}QfQ=Zp)NUTPF*%#>Zmay&+7tX~zGdjN{ zBz3|oLCWAv-Pq^TU8yxs&#g23aKYZ*HHLF&)_9r~@FPq4kW&6-d6`Hy^X!>kDAv&S zVVSJ^QQ`8W+Il1Vq{eM#Cv?O$j~eu9e5rk?y>+oC-N3>3yVd}P(g>5C;BAl??3lf5 z?sDPe(aHR@WOdCi=K5{ZBe`|1_5D4cPlU!jn^?_`aTulwFE)6x!S2hkC>s-BbgoBz ziPbS9h4llvwv?(mhQZ6BG~TMx^~Vf1(r5y`Q$)7$@qj@dQ}zH=rEU?XL=Ujy>f&dL zYEwl0xKgYAgc7Ty>SEK|EBBk|PD<8r{AELAc}lyngwr3AXz^- z<)c3=Ias4o!nT6132e3TJ6YSC#yon)QOf=JVzfwncr|GratWVip=lN6*pQ_yI5_H7gnYxf9bjB>VWP+7s)Npq|O{TnzYQy zVa!ZJSOTS=9P%ikez@zg{L-^tc=eP4{~k=`N(+-INI73UHjz*{+r0Wx=f=#MFJkf$ zKemRfG3nT`yymeIiElbrhgC2sr_{%D;?y|xo;drJ52_F(N^wwFeVSKJRPY*0bW9igtIr%?YRVNgGquJf|N_ zq?uPE4@k}WY2kWW@OAh{6JfZ&a&8m&d zh#f7fh_$HxcuwiAkNweMP?Hj=_5(OHGNK2$LueX$V8|&rIkm)K)m5+IFOR#Qnhpi~ z(Dt3qdusYZlK0lMxg=CZT2>$?q@}0OU-LBsSNZnR%cQf0hJkNhDVz_jlzH={b8U@= zfO4RhZD?}Rx3aZ6Esq&_=PqlBo)~(*D^F``D8u)LuAMyKEV&QEeD|6SIa=Q(f4@vo zXFI%D(k)^)0ysZ4xL)@md`0W!yPKHBs~j;S{B<3CZ?_qqb%Tic#M$6l(+pR~W8yMr z4B6+8>KUC0yo}q|(JXXb^M7ZkRNT}vZ z`Sx3WzTZmtt}XNO7q~O`qH5G~;-H!A3b=&Al zF^Ztt2`YB#-nRLp6wj;PTlw#_-VODnPAVVaJ{`Gr{ynRl+p2k>tj`uLm6-PGY9WJ9ZE-`Lg8kXe-CM(IrBdkowWQBByjBX6y!m1M>(9^; zYQ380DnDOkhZ4RmDS*MI*VIB0zI1VY3bxhzw)XDeUH8sz?s&_anmlNwaF3L7<7<#lVGMloLHe$3z}>tKI|$#+ot;KikvQMsEN!r<79<4WNoNhO5O+SX`GgA z7fvdBQ-b~RQ3@)X^SwoX6Zdwd*kGeFIhwHRg^O_x>D0Z9ogs>?SPZValaqv3&wh*5 zJHW1Hs90^v^*EQq-g*IKniP-1v(K7D91C~HGZJW#7cJqZN)E&_wiyN;3ilSpwMjMf z);+)F-z3OLq&>Rj_`TzK>pbc_P8{k}?&PEvVvWj7hSab6yp-efrQ|gWuU%QvI>HP% zIC6akdHAZ~i>g;mcSt~7E+dw@)^=Z3%Xr1t{z=!F`u8?__TEz}F~rLxn+pp(=ZCU9 zY-%;u2$r=?zFWProTy{lc=}BkpR(MOw3+cy1z!sV7FzA}LVVl{;}pQPZH(aiK_N) z3cIk%I7V_jYUhRMyW>lvn|U6_b?#C;qZOJ2&iT_Y1z}8Yv}yrl;|v~ED#O?SZs|2^ zxqBPUO@dX#-wY`99^bM-|05ofPkmppJH%7e_2}&v?~ZnS%m($EWhf0&9Xo)@l8FjE zq@%a&4jOD76JFi-Egn=9Pzf#@1DJWpkVfbe!eOsCD!@;Th2b*zG`P|HvUp#bA$oE- z8VphMOqE_edOZir(qiOcDZoz74D+_Ko}o+bDKRamKb9~j$AoE4lK8p1{G*t`As?TS z>W2q>m5|;8p(wQYS#$5}uKeuTNebsMgwJ&P!p_3d*T^E$PY=pmR&~0>0kR*!PA%zs zmt#TgI1Ke2n6kq3Q>V$9(iJ{=2ThNI&3sC2{lNI8Z5wWIJAUaDP2F`<+(U3gki-V} zLI$u$IEju%{a~v;Clta=O{X790hFp=4%dpQu5%?EP(LkOm#cJ+)C5aIF=nti+^kA` z$3GXe2{-~q*Wl@)o0Iubv-l~u5dJ5C*A*lHc4K|s%ZV*}TZ#&pOlnzv8;v6C`P8Cz z#|p3vgjBt%_RpKnbW})y9<1LULO-GIdAY8@ygFZBujZ|@JGlYsr_StvTiO{o%llI4 zrllLZCVTq_XQ%rK^4FI>jU4aaeV@V-k*l1-^&dj^s|83i?b!`Ygb3yX=^tHgIAV<~;)Ho_@{Sn-fcpL{BkE5Ib@~MsQQG(_ceV z8M@&N2-GHk$pB>-P0XXh(jkr`ePS4{2HDIcvh6kl>Rnl&Oz{jP3rb~X-QlwkWw--e zl5QkBk4o+2aBwZ;Kl6tA_hS2D$2^Q843bXDNJlRxN}|U+iiAuj zADIpsOb5Z0CQz?9$d3O4?s*vjrp8^K-{yo%PaOVl0|D^*zrow`enHajaO$O+|Nh??m&uJQf1xXZ zi>?EezcGP4vVaLcTfs%i2XMXmqJ_^j*O%;&~5uupErVKH<6gMupA9RS}5AXu=J0@#rc0f~Ufkg+1} zUx>XBp#P;vP;&W4k)Xtz3$Fi_BF+C3MgCbtWHNvuf!W|1#sGWz8`&3T^X~}FVis}( z?_UvZVKN9qa4P-Le*#9`0G>~X6grAJx;ir?F(uPFSpW#QC8-4)0{uwI%5He7? zc=4}g{ELzO4wS#G63{1m!k=|?LYM@?8p%kgYDQ1D<)`TgIX%ol@os>7U0aAH!E^H{ zQ~0l60n)|gAIPtWKLgz8UoJPxr3%~t2aXpm1h^V5`6)6q3;8cvc2_M#S1`R^gjJ9k zoP6>Et3bkV+b?qlmiShMip1dsMPR^3(7{O&l!{X?J0R8|vMMWmheKvPAAiweP6_>;*3PibSEO)YaDA4u;4iQN0wW&W2+!8J=_h@mL4UzH&a(@2IU zaI{b^OEw3L7MU>C-Ero$zL`$R0pCM5nlF|;sMYD?oh&IMi(0hgaJY$-Hy!4#t}%jb z=ON&e#uunVM9=21MMW~7gVryCMXU~6LNlBuCOPA0@&(HA!N!4v9D+2h>^8<3Psg#2 zE>xdzPjN&1(5B6$yGD7?*vYK~Z%#2{aomCI76w|6{HP2`NpJ{A{G`VtO!DuBpm(lB zNW(UE6XWa_jd4!PF!RwIX{GH&ulxo+=d}1z25!DNr3-cYJFGqZ4asdaZpqzQZXGb`(6z*Ih z_&u2kh;t6W_JzDJ6^4N zumV5{PlPAv1mr;zV?X}?^_(;ZeHvmCtNG@^? z8pxgi@mCPjncfMy1Tph>m*9%?GvY`bdIiwp+#p90X@~^s2|v!Kj9)eej2To$a~;?j zgz4-3I5ynT`#VhMxI;fRpW?>><=H#9xj7kZMHmB(l7RSM&T(17xCr4_9!>TDrw+K8 z`NXV;I?z02tP}H#ocvS94n#&|r@@om&M=u9A45h)Vrs{PmQ_m``)9JxN53(Zv#^+G z(4`URavb41-288bg-53CisV%gnFJ1v{8@H0AY}f}wAY(Ht_EbR1JQ;+^n*JwU^2hW z$cYV){fg&^!OencgL*x`LKX|m5g;P44n#lR{kEXxZz;#gIoUs3rn07fxAbIpB1=PZ z;{U+x3HW9<8A6|E_ts){W4Mc5*o(}1VcUc%i5a9jp?`%YPFW^Yv|&L+77%eec&dQm z)(gzY2G73qmx#EWSv>vYg1LE@vEwlS=X0QpTyUlaatC`W3sH&Kp8s=H(nJUKr#;+f zkZJGMLUryxx~SoSV))@T2LUk9ch$jj$dpN}31lJxh=9DirzriGgzhwHwXsUGUS8N` z{HEksU`R!JMbf_a;V(p zv)Vh-w(UGdm;}mwex_MQ(M81dGsRa3hk4{&Mov`IJxW2lz*6Swk z%cjN5+*yGb*Gy||dxv#b21J5=?UMNWSza5CXIpIJ*tI2IWeDhFb+7E6B90CP;s!*f zWVCW7VyQwuJ}USxzWo6=1FdL0y2$%{C)-;x%;8d=nEv;5S`0}x^MLj${t!*|gywnF zV?t%p1p5PQwSSPT^uZ{7`b$9l%ibxrd{DD5zk1zw>?s1@zOEL)N;y8|QpMgZA9ONG zQA#G9X{KM=EP+0S-BH$AAU{3QDIf7g4Y7`i0c{f7cI?M-IAK!RY{)su+BI@1M}y7E zz6)AO5k7l;_kkIY5@FlfSqv`4YDM9FGTW4q1)P#zC^5ieHYtv|KSSXVCZZ{699Ch( zS~Xo~vE#()>;uEpQ+}`8Su;Djj8i?s0zE`uU?>w$v<2r;+mt96!Y;{Sh=)_kYued7 zma2S4gtFvYVz!@$&-*V{+u|&yJJ(aThShy-oiq(9HLM45gO9IDE>Zkk3{6|Tj3p4# z;!TnYI~FV#_V)Vci%_Zdcb-D(xVxFZ*4D4-`&Pf|y9Pz8Ndgp9C^9zGwN~pW*ys|5 zl_=S4wLa~BG=GLaon5de|0mZwIpSVazS7OW(+XKfdhg(S*5M^wpUOb(sgo^QVA0)C7F9HF?u zs+06sI?W?#T8u*L;U`wUPWE&1N!7ei*k8CRc-hkApg?0Sy{pV+OOsOUAp7>U#~B$= zZuSSsuEeRKnNE@wh6I)4r`$?0&v23&^&K4rTX#MFRvpB}M(DefVbfRFKE#%RQ&p=a>1_uDhcFDre=+25op=`x@RCDjM)0>3w1D)aU zrlxV<6wWk(k`g@VhWSomX`L4y5!s^!sfO4S1i8&ZTr zMaFq0TgD4pw%+X2DKFc;w=bgp0si9Nb+_u1Rtok8Wq)nRW_*l9cJjV;cpTQ5>=Ubf z#isUyJ+<}hJ_b%q*u#Q`+HvpOT$Z8YxjP{=>*yVsZ+Xvy0=l}A0>#vfuL6j&8$w3% z_@_Z|2Bp4PxN1s-^QYqmCpznyewdMtcZd`bD5mFQdK4l|Rh zD~v^p+`zBKhVFFG1K$@=ZXWc5_7Z0Noa+WCm5Z%u9)3SUp4CU1WLdjm1+#Qc$~`j< z)E)|xE1AAY8XV>!`+aY_dicRUKi_i`DzC4oJWgC~UHd8oyc!+^w0rEa@&i_sJLP1p zZ+zzTh^|GP+qwtwwR3@&3`08%^@FmSht$LPLz+n6Zml7sU7~zHJ zxO*yzGaU((KuJIsdK`d3G7JQvvsB4aZ_?bDu}iYa#VQR-Fs53JkLI z2{=SQ!^**LvQw4U*BMtIr(N340jJk2+F%BbYeKL4VHj#_gl2CP86EVd`%T=xvsxFx zhZSuPxWsFm$cRSU=j;yJ*_2N)(c zJo%)w6!n)oKcjTe*@n=ajw9%L88`md1V2s(2`U07Jf_;gm+$H(+~}mw`&wbnK_}^5 z?e|L7u9QOAS%*AU=DU@?4L3C2Yu|RCcqi7Ip4M;bNx{wdG2Xr!n@x6N9Q2LV#t#}- z#94yTR6cif8-|koU#ituM;DZB#;?BGd*0zSxX}Q@QU?OD}Qcr4P5lVW< z{J0i8<|rF~&R|fhQbawC*3qgR*4id`D*ho5UMWN_wWTyBcv(@w5(mH0cZ-YG8*p&y znI3!}b0SAzN|gb-&<&<2RURDyO1f#)h#B7_VQE$D5X?JNe-M2F@5<@0Ec>{y>W{nj z_mIGsnra#ah91)pHi58VXHqkMdglnWz=NMY0;SMKq8XL6N{vesBHtG&YSum29;-WS zZo5@}NJqxO&%wF6v?Ms-*$M274jUrTmKZhj*|;Jv6DLXLDDV0X!Wh7m(J7N^Zb{gtX7kh5i0W1*fB+S^Q1fuyfMK z*5bovTQ16>u*r_{9Q9WnAsrVAtSm6WpcHqUiBx8$5IP$$Gj5OIE%`? zxv`s5`r_ff_dnbORfQj^joC~2yab0b?t;q5)H3w&6u5d$d1JD=lTXZ>(w+7q#d-1I zO%J2yUb(zoSq4iz8=WK*LLxaDx6E$yXAYEpxm5doahL>N3}I~Xa*p2bgLfDu6)T7q zxL~fZe3gT-QyzVs!}oRC^W&agxpPb6_4%_d@OZ|_w3qjeq%F0Vy+(gcUY^}v4V`dLDfUAuicLfZibyx?D@d=V4>VBSY zrJAapB7=7B)Kl_s(pA=NJ=oWjxMq5?K`|^vJXuG!Ej~GZcxjS&YgbkwUuuxyYP-`| z@K_6FGpKtyRg&w761ZX#&jS!I12fzm5?(fFTX}Txy>U)gYrIQ_?2i&j>l>DsacTYg zQo&_=UpJ+{^D#L$^w|z~9{w7~Y#YP1lGdO>h~RH)(wmCve}C6wLS*3OiTAggA8B_D zuLFnamTI=3`M&H|K(c4sPET+1@3I7%U`0-KrnbOnSud97*-`ZKGG=zxV zmY=j7f=RPK-e;iJ34sHRSu;%ym^U4sK0}Pln1S30+1WWFoXxu7U=Kjd4pa#WSJstk zjwrK77`BWF>3*0h0C5_p#%iuy(Ty;5t;sK>h2~YMyZL}@j4)VGK9BOZx&@w_y*AsG znF%jT)Dy0x(tC~5ng)$4v1K|#|NXr5r901^W z@SXh!xtOg^H4*LT@>yD|DUrE(DBR=5XKgC!Z3=LP=VI`IxG54_SO9QN>Q%w7^@B&I zTM9r{>8EriY7kqg&d!eG77a*K znpBY%5EQ8j(u_bTBB0WHM~c#W6oL?nih>ddO+kwE&_a=_5D*a%5d=c$AVnmhNC_p7 z{LX{#`yr!1h=H;2p5tlADbz*Y{IP4t-Pl zv0D7zX!;?3duYlyd=pA)aK$djw~sRUg?S1o`1|*q^DpYzsaVDU2#?_}#FEU3Gcl?i z*Td7`X%gB$4S*g>pH#%=eU8aVAQrCFAeXRtFlfGTII{hH?4&IH ztJI3WRkgbAYG?On2y$!R`Y|l`Z2|EAUjbyPRDGpq)VrGk9pn2sSkLG3o=P(e#O)mT zsH^G$M&!f@t+X*gaa)f={sDP!-Z$;?RTU}KpIz8|R_|2OwS!sC2R00bD**=@SCEwZ zM~fQW)!*Zn8vy;07{HVq3X67Y?Vkd)W_j?R(=^mlh0oAeY(!5Og*#9lbT*%+<4a6=7m*}E9IU_W+xTmd z<-xk?kH__n>|f@ZO0y7e0iO8g;G>Nl#E}J}2?%HisDG~9R@^QfW_+Xz%zL`24o=}f z_bz6=^4&ST1i!I9Lt!uFpmqSM9qT8)2?zs$2|PKqKqdh%s{f0X);2+L#1w9cBZ|FU zJ(*p&RaH-)2AAW*M*{)kX`Y&X`UmvzY7q?uq^O6CdMA+t$+r}Q2Ng_2K+n0ooXqwS z)OjC0m6hTP~wYQ-SB;`qDDAODVtC&jg+te%DuG z_Lu7tFO=0GOLzCpY@1Q=?WcD^j1zbT_N54nRVNWetKYOZi?nhowuuSZ-8a|S+x^{R zlUT@E{`(sZM2B|z?CBbn8u5rR)V1ZY#8c6Frndf38PH9vFsUdiQrVzd71mJ|19gUt z;2L1G_ecQ2XEqIXi@=>o-J$NUcjDMWMy+`FUEuFXL@p8kNL-rWA;~T!WmHcaON?S~ zdr*~i^^g=@wz9PuX~ES&;?xB@M=U_LdQ6tIKT_F`=Gu}PS>fXUfYd+4HA1fC6F!_O z!^U-G7SGrSIz>olbR+nv^i6gB_O}&YlWj{J5VP&`irh`*31N>djomptVTR!{HJ#N` z797sWK>L;6@s*u#Zs8bo6z6B-5u+sJHsCN+k35x%^q=TWso$Gu@Y){K*3CmOdMs0@ zZq?%!ebl=c!GA#JYfm;*O6mVHa8^3W8^<4%pcZXW`w>!)^Fn*8S{ zxo-@fl3VLsl3LC0a#x-{q$?TJvDDQShNSE+5K6)llT*Idw(x4I6Qqgnq%woeP0F@Q z^X&3`>OF;AU){_yWY2kw&9mk?#z`KXi7TiP=)bo?8ED)(V4YxP+lcpj5IpX=9cDP7 zzss$3{ZhB3d5@!^?wf>MMFW^z`G=&alBu$E7q%zS0*V@X`P8qjDwnQXa9Z<#mf+UQ z&J$zxwv~Gk>L&FM>TMt6ZaQfRPCQP(f7|mIQ*DB>GQ3ub(t23Cv>o|$x>o&j?lb-J zf&>s)?v+ZXT&H@(c1ZO~RDx2?#3Ay2-dO?zl>oy@8=Vi;5(JtOML=O)vbpN5yQ1X8I?;=I1>Cx^l%KzTmrV zTk(Y)8HP5Sp1Tk5A+UCi2^{fi@4=Z4B^1g{yI8nhRL04{tz#{~@2;PZ&6TGOx72R| zwmEWvb%3pEq-^=(d^Q4@^&=0lfNUzD59B6vNooNX5p`N@8v)&XFrzB`)_nM3ITRrK zSjZso;5!IRKxoiN{%$5FiHI0fS8q!t;WiI{EsSQ1~71J`K&`a2}T z+~*fh{t>!%d%jL{&{RG0mX3EZ4~r$vK*UpW-mljFMS{pnW;HWj6aRs<+};4%l?N>o z;?Rl!k(9{Ws)aP$Rwl;ZjcU)$wIxpmJp8ccwp)EczCo4oC4nx_%1VS?fADTYZDbgE z$5kVvVVz3axS#x>>^8&ps*p2V#dHwhQnX*)J)t!^D_DfW{C5G~BbZ^~bEMWkAOXbZ zBgr=*MoI7dQ8;}qGJ7u>@sR?>9L?433-N!Rk&to|u|bxOby3=yfu;!XED}2Zd14i6 zk}1f(Kg~h?L>KbExFmwgui zsQYNHVL#%?@)Eudj*lD zSPRldTZpTiufDi*tZs*Rq#O-&okA`JM0q1d;lQSL-4VEVuMpq5q>eVXWCWCg9}*qi z?zvI1ZLmNeyc21R{9iO?PY!%&{?$(c1sjO`xx?%wm>ktq1I6b>5NBYUClCC$814V` z&i|WS?f>Bwt@~!4@HxPGy%Tlee--3^`E$I5%>2ig_LwFZF$Rht0ab+gdkglYa+(R? z#Z3!hsCtA6kgN#Mp?ybe4}CT~X&}Wvi~7o47q@#+hZza@1kEw#iaW$Ao#RMy4xp;s zCm0fun0%#2+-904f_V#p0WL5hjlH940HU@!_y+c^eFBUi+*XD_{ynn}+;|YnX#Wm) z#Edtf1>073V#)~q-kR2_-&#wws?>a-^PSkVt{&FBm}p~@5*2>w*V4{FbJNINIeae_ zZx{csvo%fF2N?5eVG6Ew4U7Rz+i*o)2Rc9VxP5ja7|>b{%cg?nnVG2|x^06NwY+Z1 zi2`tIst&$&9d@dW`FFiQnFsAWZAWY|?CkaVxw!`6kCBkILYiI=NRIx!BHCz9CHZqf zQ2-WAZKs&yTi4)*(QLjjG)?@whO?EGMmf^`w5*r|@<})97?c+a zEQL_9&7>M>Jm)-xzL^$SV@1=bq5OMN53p_KRjcaoW8T_fCc3F;!(KWHji@1Z?4+W? zVgJ4A?!9^rN|yN+8mq~u4{0x#n0u<1uCW9COiCe)u zw8rsCGqjFEduGvNzu|lTx#`SJ{+?1Nlmc?V-PN+26GRsiQrBGToFi8NT#4vnUERPQ zg(kxP-nRRHJqTa+_sFrHT+z--Yz@AFK z>B~z|s1k2mO-?Q8&nwxSRem~@qL?5m2H*%u3iw8nl;Jm}f ztv}r^VQ!;&MJ|s7c52p;Q|?h)WYp_N^4M*9p(#FzN$eNqoCn9|h!J>uqNGommY6^} zEfd6F<2i_$AvivLv~LF(iZ7I!jdy{uN_KHRiF1pRxzcm&Gh;yfQYcnwh-S@>+imD< zA}{=!rQ}wE{DndmA3fYm?gl(O-zEdpZ+!bU{N;t% zYutH76*})Gh|LTmq_-28b*#zQ1u|`zU7y=+csPF`fR!9u3(6{*>!UvQ-4PIrO~6Qt za`slyT>eyyvXL15K5+CTC0O^_XIv-Yr#`(Vu)4188F~J5dBn9-P$QK*P%G7iJY`uZ>I^+ZJTKQL5M8ZV8yOf}Q4kW{z&7GZwW^NFX~<;rIj-Ydmy?kbwG)26*GkRJM2Tdx!O@H{x;k#6HwWP{zNXSL#0$ej${2bW0DyiY>d>E{*VL=s7K^lqe!PM){ zGwkqqzq@9sC;FrkwQ4(Nk#uQW+df*;#O4``j2vszf&#Ms5K*xgijO;6dWNQFZ2>Vj z66yXO$MwGa_r}_?t&OeoXa*o7YDnj`nCWVRH&nT%l zz1uE^uc|-L1o67eYKvb|)OB5YPKp|LHisL~0AA{!$nsHovKjKQ|NutV(=9rFJJletnQLbg>rF=$IoVl zj?}8V?85JPR?RL)LMIMk&#y7p(T=?$t&XXA??2nHHf#wxl=7!(>)rQAOHu?hz%{UI zq>eh&H>S13HavyAIBu38>`~wc?VqDl| z!oZ1n5v;3Y32xOs7dzG9x`R@z)#sx1W&ES!vO{(mXUpL4v?0sOZBP4#W%Bd#TYPAu z=*RO&gIMo-Ne%f)Z5RsTA*V{7-3b1=H!St{nMMe=VrA{j)A!*QLsnnWGN=AFjYvWG zpAOQ&XF%AX005-|gn<-KO&F9F-*#=cllq!sw5_+KXZON4g(uimTrS$M#R5U_+Z=vn zI%<-xggLwhjEIYH;~=I$8-7)u(%uKf8b&F-31J>#oXQV8ZL{1BvUIMO=|8#FuU7jF zU82GLbY#^&cG_GJEuJ?KGpxwX9n=F>b4j|87a(|;Tl4V+t4`liqM?s2k3M>=^QS1( zo7K+DTm)Y(-+zs(*PaK`XUDM@S^b5IxzvMqN*MblH2rmh`2O!Qzin(0=fvLEmDzqH@W4Ok9Y!%F8OJTP7vvw zvhwy_n8~p-fzsx!%xS4F+QTt=>cTUKLBn^HrkU=6%)8f;Vp|~r#P#(D3A?{y^=vu% z8ANSkFF)`w)0Xtm90(Y0on{6i?HQZsO1EIzX*X*R^PU(c*DG6K49y&zhUcSM~h{xmBZx>B{xXMxkHEE~4L>nAX0o}ls z2;7xLQQe);j{06Nv$)f9v`L}>nq{l?9NR?5*L*I*607)GkP5?+&9oPGcV*|`%E6~M zizBzLjainF9S{Ke_BgV&u0cxDC|e%(M(X@|N1Ir-2B|KybRPzKyWbs{b1OR(b+m!~ zGLHYOF1xwa22Y^@=pLV@jy&oXMDd*kC^LmGhQghQx*l%ZoFpXSQNo|&kzkK}cd}dC z+G~_NEu^q&RMI1|@DE6*bFWPP=kNoa< z4TpM7svQTT&3NC+-O8G*MLp5{n*NQxNw9zZvUajPEZEdq9pvoCA_} z24Lzv?~4hYGdO1)IviB;Y2S=*mgp192=SeKx^bn4_t%Q@cGj*fBz*UX?ngNWok;Mh zPAN1@d&geA)}2UuCJ}U9MMT(s;O?%q%-zrA(0zKl=#nLY>_k?GZk1d4ayvH0CknwB zI6WU~E!(0bj)*@pl&bs=aeOGfJd%(;zKOFyjh~~N&6kqkAVUy zsN(zAo@ZpP7ygMsJC0)CXQJ+$)j6Lp0_0Cw8J^;M_qO$`ltqI3OY>!%33SKyaCaX! zhXQurXre}r&TbP9Lek%R?&N^{44t(;o;fenK1AiB?gGK(YHRGjUTp7a^bsAU5PK6%D!@r*WPPnWeayBQ$Wh)VX>@EkA3_z&uC4@Q+a%JN0T#O9Up5e zT3E&0lFmITdU^{>|pvu*y0G93!ZUpo|1c103n*_SxhzP%GV3kdp^ z1?+IH&!x0QY{%C%4uE0%R&!INH+OTQuKEAiyS3B(Q&L{(`r|fbWqv_RUT>CAMN1pc z`8OP5mRlBVw8`q}PIuHo&Q=nNtfG_Y5#zsi0-jCU2Y!FP!763Jsed?vFaJ87T^hP6 zh}?QL({k;oLUwP<48c7e{xT^srV{dAeoEzdrBo_s=Lw#kVRaZDv&ybr&TB9 z=6In;B`pns*U2WfwDl>aP1%sq#5Sss0L6S5N-Afdl;9;PHoPZB*0!} z`~yl-A5ALPRe&UlyJaKVv2DYJLY`R<%6zf0>Ua^7uOR9C7@qMrLSuhN$RZ)sIm!gq zypBY+eW)?Z-XPXTNL-QQ%*K1E$nlEiP2#t@)LN~BTvu(L+zMtaa++OLi;`i<^$6PA z0uPq1-~Z#!snc;4GXb~{q)0^@_t~5JEH5@c+POj=p%DeN9=`#BYxfvBe@8tlhxqP0 zRgK~K;LqC~5VF6|TGUjmv6`C) z*PKNbEP<5RsXbG!%W99UWB8#Y>nGS}ddHj@aox`bN3m~4nIsP@hxn5=Z^2?I+R(P%Q&~&h= zi3P65K&Sb=-phK}L*>fLr}RkpQYH!Cs^aG%O0CqL#qG?igiqdd6t;+7w+ zde3;IV*^2F75A=epO@5)D|||hjYbZ(M)Ke}-yi9Nf2u=|zJGbo?pzD>)aX#RBj&{w zmyM{{Np-*6cu-qnU5HJV6o0GQ3{Mm%i-j$3SLC;rO5gOfouMJWtnRF!yHeuG!*-XK z(h7Ju?R)<)P3B(6bAShde(y#fN)%fVY6gT{rc1#_*jg`sh-E>TwM8=g7#bmMrR9KjYQ2 z(9Vi?Tn~0P7|{j2rhWrk%_`}_wgqT3^+tV}a`4dTh~x{6uSs$60HvJL7lT<=S+aI+ zCUIK#$DiI4mlS{84gCr)oL1YFd#Asv1TY6z$LiQ@?4PYrkSi3QJPcO2Ts1VkJmFgy z;6gfvF3{}X5^@O5DSPSIjqO?WX9u>q_Os1NZyAfaL#~wfr3dW z{MAu`KbX~fo#ZX**2Y_QqaYy#%jy^6=DRIT2V33Pac~r&e;8hfLiA7;7O0auM88qK zIRmj(Ds;2`BjV%HoNxpF?xdrGFwF|Zt&@z1$8zA(cF~_K!;0@@rvwW!|IF~Gyjy709<_<_OKqGl|q`ERHNJBd<&YCC^(R9!-f zdmFSuRfaC;z31fmk>-ACd-d77a6qg2$W^@{wRnp^bO1{5hZbX}Xx7C9LmMDIubCU- zhd(3Yhp8jAt9DB;4PHFU{e9)jddHj{h$j1pYfY&RVxw+*h}z-P)_04-g8*%60(X~M zEeBaB8c4r9OF)cQwLRH5+jT?l(@VsI%6S8U4Qly8jP|uj7u-RJ5kVpVMtmfyF`XJ|VXO??fp(LCGhP zPDCJy1POqyn?|w|1-ASHx?3cWzJpiArZUs>3D$@VLM))GWb-xX^@xK9r2e%hrv<;`@WD-l( z5{zXBF!x$!2d8;w6n!r^n>jIWPi&S1i~7ja+$`>#%=!YHEg%5iW1MR^dAJ==n!jcJ zFZTXD+PY7`1Hd_)IP=IKfUuEeSl;7EP2kl4{7LKnMjPH0e@>h^FnWYGpd5gT5Y|jOd4WpTBrrb?0Xtjkh(46 z`G$cP7k&YeIZ(Q2eTKP($Rlg3f8+gsy#;fE)5(6_PdEHk)e@PV$vc}jfttYoEnk5b z;Ol550RQ>dA+p*YKUd+@s7gZ4lAE0N|DJ90{*SZ7*=E4A^4Hn1)d7r<8JKVzp7Fvl ztt!|&%5QK!(D*3eAuBF3-XZeHlrR%9`{os7-!Y;D@U-k>fu~d-s?=Qiw1UiUJ_j9g zI6Co4AX|Brh>;Pq4Z>|>S00VRu}=F7UvdI40*R5=&+&>-^_Na{F7B+i=Cek|0S@s*DUFP)gcg$u5*ORiN2qN;e z&^#IChJQe(gAVsj|GMMIRWD`mn!_ z6Pcr;Vgb$CGmzy-P1-MeK7Uv#)=k0{QHGR*MNA+u9%?*ra4LnWymG8QR?;RpFmbs{$ zlg8sS*sl%8t-2ql607PwkwW~~*^uK^a1FV%U+yI$49jL8Z|kwJis zgQMzu%G%@NlZlCNn~GYHMuwZN~EZXC7uo1+7M} zG#NbTKcxm8UigLIuhOgVqO=8X&uG`npJ9K=yZcU@S41FD<-k5K3P1LKL_LJAwJp=( z>C0E5uJ-zjCYH^t!QHQOZhbli;j0^~A-Kx`U5)bTm;UVJ1Y5mC;yzztI;%`pcDLv` z73CUNgvRDV6IiZ6>$Y%f*6I9YG4VbKU2p+(CmO9j6ZzS?OWeLKxc9Hoa}?H=gjl<%r=Z7J=q)U0FGY`sRyof!S2 zCTxl1Aix2;@cFF12Fa;O6aKI;A*;aB$_?$Zb>s6k{9D#Mo%oMy(J5C)KlOeLq^E3r z)bBe*Ny-ZTD!uHy2hTRwF*RiQkr{j23MT(C;R^r3U5*bQ)Ho`7-0jO;{q4St$yPRS zb7Ka}E1rq0SRX<2220}EFhW_XlUeCGQd~VEfg{{3inTW10z`*9!gZLb6*R7sHC~!f ze|VTl+?OYR0334rxHVNTMHj<4R+??VAvSVe2b5HxXv+QLv?wpO?1l=Up!Qx3DK7v_V&B>A%FIIPm1d=Vbr>NRU7GHe*5mT_`L=;RZ{P4b zx|BTEk&NgB(u5DcwoLO1a6Z6hl96{`&VNbZrY-Kf7hvM2jODLxq_+C)f~NC$6<0OS zW1Ot(nluV5LIz7wW32)LF}*H+JtEnj*H2-1=tvVmAugINH4>(@$F#or$xvnC{RLnW z4R*FYlV8sXGC3`t3pzzGkeE%hSghhOQV%_o?$T*d(#WvQnB4f(raSHn-Tn1m`@~n) z!|LPm#PGi!3LpjZy4@$}dCB`P`=n`(g5xuF#GgO2&^k{S)8}IN>gh)ZJjVPLocQdy zy=@pNvqf0x$$M9IT5?0UyPtEC-oepOFhZ`W;}CF*##hI9M9R9WT%Oexzer(4ht|_( z9y7=7uFNG0zt_Cb7M>kjeWvbg;}GL=qK}$#{+H=kze8T(UA{kR@IMZV@s5~$-uLBO zZ7+nAD}hlvW#ONc*@OcoXv6A&sx-If>b?n!-0Js>{jOAAx3&!D&=lt_{b5pYufsF% zi)L-J%jrep`%HX!b6IKT;nvuN#1a*@+fAHsUsF=^<@}oM$*Yz6HT==a`!2Y_O#1AfK z;x-%|_ef>C*N^7(E>RFqS~j<2=^befk1g5f&imOOlOjpg+l(34E_g;v!z(Q~ zil&bu-Ocr?*o)=&QjlNY!d)(_8S+{8BiGa+1BD0XpIZyI%cJkSPEcW`h8FTYmnJ`V zWuO0}UQ;LEQsw>WBlN-HW9$Usr=m{v6C8GM`7kt9-%MT}Wm}YR=6Ol3br_mrUct0% zE7ou)+=OYm*5unatx;76L55*N_Dle@b!vnuDMp{>-ceI)({Ni8cGQ0JE?q6vV)Xlu zqB^GHq>Hay!+vURbuBUEZ4{;3sW2J;NJv%^dZ8m^BYsGE{SPRC&}nPP4LW^(aB%Q@ zS}$g08E-ONlXA&c?8vS;Cp|}$#r?GSL&c@?CrM4l`HtcC-Zgs**Kw1&0M&m_W3d0* zr!3Xcbl$ZlP-_G;Q4l0e5<^NztWF_!n=lxp!a?z*YGHb zATlXV*!uEHsVX4bXfqWNKyIJSo=LPp8S}P?|H%hkKL*J_yXG2_eQ=}ZLhhVWhdg)E zM9Oo|*2P;1-=y!PyRwSwY`Qc%W2*~6n|VGa|2iLSA9w$peXN|E)jUW;wV=%Xg+9nkNmB} z4#+Xoxv2u}Fqm1|;^>p)E$R{F&))CkkrG=*Go2^^s^w72Q34c#n~q{T>SYAVdgxs;;hhSvWFq|MGq**{|U zUQ9#Q7p=8_glE(tYW6A(i6it76l#kE)(#h8rU{nQXN>7tqN#y z)JKT>XTSOA44cKFw{AxQ{@inBTIWFN&D25@ph?#HB*aa&R%KGip(`vOZBolPN@HAQ z{0&B#L}Ko_k&Yv-#s&w%buL7G&+%GP0T2C&%<7cH=wG@Ux=$A!X*{w;-Hm+PEz77I z{7wxL78{IEZUO?robM-3bM1vX$~Z#0O|AgTBTD>Igi0(SZe{qi=<0W=4^djcMF0Rj zQDmJq57)ie1|JXk$?&HD2y#BFp|SyS$o|DgV(ag8uT#MBe|@oYbcfyeebeQNaxZa| zckd$xwvy|KwW(iURp1bh^9*JAQ*wVCdF8E)Mf9UD!7k*ifTgk!QZUQBL1Pa+(^_&~ zgn9l^tJ;8*TwB(C=3@n><+FSR8qd<)ua)yrt0c%*8Uxaln#G<6EdK*KH(b@Q`mp1V zI~4{vp6mwGsRca~l0r;6u+IQF&3U%9LC>D>*mE|Vt^0uZ%^^@X&Ruv2TAmA0o;K3jk%rH5>BmJ{cGT8Tv6L~g^X z=XUDH4W4Yu)_=VBW=WusM#s`xpsUzx8LsuD_?e_hy1R7!uQVb}|F;eYSxWx&Ss~e; z^FYeAZK3n)MiIklpv9G6#_Q^49&PE|yT51KSir(ZtdP_;@}v6F3QR%%cw1)OzTABW zUy=nW#1Dv%&o7&{p!wZpamP5VQ&v}Ti#|w=OU<{s+JY?h^J^sz#Nk{&co;fOaDVzk zQ^sPry&d5AA0E490OZs=h<0l3AcXbQ5d?_ADm$8UQrAfaQfq9s=k7_47%q>R$!_fK zuC^EU#t~Ejrv-Iu(}HoGfcpY}_FHRZXLsMuoDrCWVDYoh32iPx0A+38Qp3?CApyoS z3Ou@fO;g>=fRD&serAhG8ax7r7PI$PoN^G(ALj!4aTK4rPgP_Sayw`)VzB1Xckk4? zhZ?;Tq=*lJ7QSntd=AfiF7l37e6zH4y@z>M;J_wvUiKngKYzEeOv;2F2fKEf^60R> zy@>_7L>jp~^i<{Zvx|-_-$CfI^QFHK0Y-dzOHU02pBZ*;D}>K8%=tYf9IRftS1wkG zx7utSwJ&KH+57dm!dqGD;fKpm?z~#W1flfxRBU%%He#sF?n*b}l`1BI;$}W((ZiTO1WOvfTqju|+!3Gi7Ol&I}hF55Udb{sCo{ zcDj7_#D?hg2Zy=>F_ zaKo{c?{P{v^O1^MGDSKvgM9LHasF2}Kc~jRYtATuFk0WW1sE17K2iEhG*rNa6tJE; zl0O?oeJrpkF3}# z8(Wk4yndQlyxm31KJZ}S9-=zZ!JH-Fp9)$t5;D|#ETjkpB}WZk%)1QkUd*`Mt*bY^ zB!WI$mYjFfs6%pL@57~q3wpw7%m&fb5Tl2g@E>dHKPg|SYgS`RcCmS4LVkv!+}vCv zqk3YJMO$5t<|d{KDSVLb`?qS9=^7M2T~~wL&ruKX^+0xyO4@~<9ozpERibEPBQMUoHx5;ZN`K7n%3)sYWU5dh1o=H7_t!91;^_l1@ENNmO z5DR4iqW#t)KSK1?+3)hCBq3B<{sH+LgNc!mL%S=P`js!?1{d&**$B+ZR5rz~@zy-) zO+}kNiekWTf6UZ`pMkgTW};WDp~m~!k%%dz-yx}SwI=&2;3BMHJcgbqF4~_LdT0l{ zhOlE|Cs58jB;;Mj09RZmrEqWv4M2NMbP<0#3Ak#=HFcHNdz~A;a@y6G37+>VUj&B& zF)JP3-z;yp@!Dmf1CEkM-J~vRm&R30R)pJ0TIq@_B>N2CXtwgSy9BDBWzvBC9(7;U zy=dscIdy>K#mmIi$WO+t{ciNMTtsceEE^ypS?e;-5E$|STKLBk>`~p04G!?g3;ozwe3&1;k$Vdbz!Xq5Ry%Y#ZY+V0Iu1p7_ zuZr5Ux~}`~ybb_{1jZ?! zh==0mD)40x(jZ9#&@R%#KZ1@P&FDFmIxM;c%K`i&=WS?!wyqaW(577r{4CGxzzMnZ*PArxaQtd*@B zwA`d(1ykG*|6%1GmGZCF04Ul2t}Mh95RrY-Qlr3d1&~-Rd^G}CZv|}5z~lb5EOl!4&@|nDU3_mEr3kgioyOLqUpR%W(*`3An7eY3OL*UJ%~%*EBfc zUmfLtv}JrWDpQTCdl2N1M~W6cv6BFFr~mP)UX+X+Q;SGq$oE{sxR}*>fFQpzhsM|9 z({k=~2AJ$7a!yk~qA?N_x6LjjP_ECl%poBG)dlwYbx$=4i{x_YW10@^|CR{vG0a%L zc1J*|)1*BAZ0Kr>3fK(M5%EqPo)c<&z%tN5M{5i!kJg07<|ArO&DO7s{e~?5r`r8b z#+@M8Q6(+QmGpy`zRDD={9y#0Fw(y)(E{%1Iyo(kWDsv#2W>|bbn4_d2B1s_ipTZr ziY2p;o()quS4*a<6Dw$vnls$`T*FSGj6onqatCt+-YTCshsXIaM+Xdw*w_e%W0e7s zzJE2>znX1fZsB@MbHf375`Z!{g@FvFTKE?WW5=lx+1ZmFJ3h<^|A4jb1#p#{&(Xy{g=i+=>^ru*4z^iy2{v|mM+0?jiby zPXqWc$heU_!|24KMJKF9=fEHs^0lE8JRf$_ZAYx&PplTD(A~3^&j?R=5F5t33b;T?4Ylb?Nwl$BQrfBakF()G0fbp`3Kd*WYe8(nIxAL=n-XOpM;@Z@!!U)#@QK6eR zQH*#bi9&o{YW;K6@$Sp(zplaN1NDX|*O#30yvi-x-HN$cu5~2Elcna`i(@gjRdP`( z^fPJsf4-D6WWKQd$$YP~rLA*cX6mk--*8ap5O|USDp%l>jVNgjW1kO(-h&1R7xlCJ zAm^MZ1RF3q+$U7isy<**tf>X&xrn{}!Tu60^Dd-K73B<sOs z6*Pd1++O`u(pFwj_Vlr{AK%N*%yemAUiMZ!QZNw9V6HHwE8_(4R6C+OVT$ z`p3%WleVTyqVunoUc6N=BFElJ4yc%ktJxt;1~QgFCsw~^wb9y2nqGSFz$+io;3r?H zz(=}Kckm{LjRqu9j^B6b$jaL`7gGEI&qC%RaNDc>2Pk8fw2?P1(wv#$g?dSE^(&3n zZ=5c^1Bv;qBF^4>DUBvk;vi~pdy9~Jew3-VS^+Vt5qYsV$-=cxEMrN!?(1p&81o`k z^L(1abD*f}`@XwH9LuAwkq#*O?%t)C?%s1C!1p%um>d{L%mLPL3f~l^YviSyLn1FO zkr-bw%VsPRivoQkk5=&LRN|PrwMawn{Mkk`3y=&Oibf>TH+*SsmN>S$iU$N$@^OSN zq=Oc<=|azMzkye;*1{Nx6ul2_RU;>3`eY6;0$u~M+Q)G|c3(dS^LAEGwpaCm*Q++i ziKE0XdNQ`JKgi2``<&rw*azSh5;L%~@V%7VY#+Qp5(Xd$h5uWx(lo0F@8GInRW&PC zJfhhJKI8x!a_eyI)@n~4BTrdYGtos~uLVHQ#cg6JIM0!wakcdThtUV;S`htDOM!qX zM4l`#=j7-e+feG{Y72vF#}!?cVrr2k8z@{oN4fP6XqdWZBt6!_b4Z%5yQ{T*gW;lM zoP*laW0Xv_FU!lDMbBLA26}{dOWMM<@u!4fOBX8_`y^jyrYyH&#YWJ!x^G^{Oujm( z0t%NHe`s+z|PZ;0@a3udT*X zF%~GpzJ}-2ObuD12F+OHDcG3mQU2*jVmZdI{8*LV)e@N-$Pqln({dQ^h zW#<2s8+NgyC64`El;Qa%M(|!m1<4PEiZL{<{8xF-ef?av?Q9+ka$*ElWC_+)XSKF3 z0y}k2K~^1|D~>4lC_* zb@`E8qqYLg_V`qNH{*Sf0;`0k=f=0s*rKyVyfur&jlJDVg-8sB+)RUetn7A6Jr3}f zSgG!&#gr8CFxe&;pM6<=fu2iqbeSKP#x!;jWiCLVYnZU1jcmUa<%A^w13S;S##Yh@ zAmoWTfuZuo>5xSs?Un%1vR4cV55U2_jp-%3s!X*7nnuN%&!ZVy$fp8zBd<|0%lJ2v zpPp>}+(X2V73~(qxTCZTubOPiq;{DNEA$SCn_uRqlkXTVl5wmGDqqvWAs%ksbTd^I z&I>rU0e~li^FioHN#iS5a=e^(!eKPunp;yB-$;knpLW5?-V8eQJD=JU_!L!Pm+X+M zDb9s>#OdQSqY0imjB+czENJ>vBWys!Z8^90>yYKA!82(xs|qmoCB#*CO`n=Cf3jb{4W7Vhg7a)oH6)+q?O65kl3@m;$?C5Kz?g+S5Iqexz(NXw zdTFpr@INi$kj(AvMy)&9KCuU&F?SWnBv}uBYhqAfRHGPyuhtbNF!h^0`Z#r6^Ipf+ z>NBLe2P@){?U}47;cT($pkB>}KET%9y;>o!Z9-BU(#*;XdIwR{(sUFMa9XmfU&3M2c6#?-dF?;Ia`m9-@YkBTVW>N?wK&MKYGsWO!HfWrOJt=%Vwp zqkONdYxln;>3~zt6CBJ^4^_a4l*o!CqtuUX=ogaI(qml;+In73IQ6|7I^~=0nmN3h6dIvJoB?Q7umE=J9wKDzA?LW7m6EnH)H5YHOmnjTh`%sDo){E?@)GQbDP=4&<~4+e#1=w`@5V(9n#gBk=tohw$BJ`g+L(S6s4Q1!W0H zrAk!?LV6V$g)aF7>ao3bK|fSe`T2sSGYnK)>q;Ze#yFbGsDalBan?xbr$x$P9v#}P z8}*MvIh30m_HsK1NqY<9AL<{wqJxf}<(`4RY!then+G?)kG49Kh4P%lgc|EJAI$aZ zQ*$7;Eh%(&Yo%00Gq67;j@$0zMpHhn%lMw>j|Yp;L(>r&Z=r9XS^>}$gdm}j-O1(q zqcDx7Zu~;N-OI>xuRO)ddkub7_Zb=+-!U*(=a$#MlbGprnt5~hi~EZLJByJQr{bq@ zF6k~gm%LYb7@~8bqGRo&(7vf(1D+awRdqp)DQ38%+4xa@d~#&EQowrYuRZ7x*yA72 zhhICAM{s4zYQT~>blv#SYq%PDDDk+b!sQZX^_wR|j`RVq+S}U8!t`zeSN+&!dYq%@ z+&*Sz82mYoLZrqJ=h_Ouu{wuW4(+QFqxeFTwhy+p>Z`~Bi2GX~kilzZ`|3)!>^Ur* zZIdz*B|ko5h~O|ZkTc3c)w+IJ2u>c6;%4THzYV87a!BDTv>}$o^k|gg)g*s7)Q62I zM)O#y$^Vw_I_oz$Bo_g#Tv$8JcC~|4C`T+1_S}5h?x}PW@u4Hk>DiFfY4XRAdPLF=PmQdJx&}8R^NYY9?$&Y_D=3N+~|2$-V$C4wa*nF==Jolve zVLz1^-J71Osc~GgdXD1oZOXNg#14s$=P_E4^z`W!1^HNR<>SI&Na5>(h$ff11W}T% z@;LD$Xa5ToY@_b8O%-{|~L_;S>X z7mefS^U*J7RTlub^@k}(YOh<_Ba^v=V9D#B#jy>366U#Y69Um9zDSR}gojQP`E_^6 z+_;xL#Pe?8oE1bIeRf!-8!)t1 z_U9X1_|4ZvwZmY2HB)W#l=ouq>6}gS1+}2!%&IlLR(EBN2C8qFjA$Eux;)bDK(zT7 z*x~N7G-V}JDIEKOP`e(CuW;?!PaPPGAPD5#p4jvAN!rw$sX@xnu}&+TcaycnPw&DU zDr`BC3j(-xcX1YLiEVZLU@Y!u%#stHapo$MPo?s0L?Jev<>CxkeMw<(t8TNKm-%!| z{!`Ji$oj(bP#W1U_*)POl4WoTx@u?E+NV*9{fhI}jJ;jm#j_PpQQyW5><$*ASv-VN zQvMLr&tSx{@W%u*%t?gbk57C**4VDmP=EU(Php=YTGg{ z`wuK4-*I{q^~&WKthl4fsW#-sK7TG=w6$CMwKkJ_<XWiw>QZRTbvyf*Jiq4*k=FFG-yN6nt;J>^EQ2jL`-+Q*I%(!mH(| z)WG7=l8g!om4|h@`Xb31vLoul{o=oV7o?eUe(Di<<9C-?|iFd70F1k3TZO1x{ zxgb?q9J&Z_4WrkY4+eCx_g*f#9*k|+Od6YJm0z5;^)xPfC1$#O?S(O~_^v?4b8iuH zJH>{ysfyq2qcOL#8BZ;p^)md)j6$QQ;g1}|0Pd*`Q#F=KZBSbrbW#Snm4$%b|G+A=~zRBIH3{}*~PAj6q zQXA&?SbD*9<#O^}SS}ywoI(ddt+9K5fP ziZBSK56Is&i-^_D?}<9PJ8WV(apeyP<#gMWrrTftLq5)Bb9lhrR9s;ZMSxHH$=O6z z5x4UJbi&V$?ilDPOHLfP+#{;`U^@TGD6tFKL0#3r)ZGC2bBM-m@m0Hkwh1c1Oa*Q> zlR}~LuSm40Od@o<90Q_bw`>9$5|BR!b24)Pa{a}d0AgV-7hNkYTRb>FD|3+NR)VhEAdit`=dnz3lWGQWyw#vqP<(zR zZh~V0>R+>x#vk_BE?^B*r4@79#a0!41Ev0CS!xVL09rAGA~v7eks%@chSh$PBZaQ+ zKMqNuo~%&X@^DyTB}sm|gn&P^wwgjMB&s8+;wGl?aoK;z)jwoaj3XPcZbS>^#%Z5c zQCGmY4+O_TG2j2A`Cbn!hr_0W=D?HuNBf+r=t&>QACqMFwEKJ3GQIxota6J3o(LBv zST_fLsuP41!kP|E3T1Hr_K4qUkDN}7O(3)n`ma8EI1ietz1TWvR?QMqB*~3+8gH*Y z?Tpu!_`@bbMZv00;XNm`G48MyFZ{<#N^FBDq(P_qQK2w}sjx4MCs~)6OVn zN(qwyF2Op1Z-jlpK_-3+^}YlQ5=Lq95S-Xp2z z@1fPFszS=X^DA7utUGllyIzk36^HLGV~#-VCa@cCHsv$4o<^m10l!L1B+UY27w`89 z)m##y(#2Z@HgVD*Gb@WsP`9??f77qLs zFpLk&3@dLJ(qSlX=!tm1{@}fQj``NraEEPG=L2JIe6q#E533j(`B|OuSnM;_b+gHa z*Sk!mQ<1qx?C4`v^d$=K87;#j#}x&9{$3g z=p0%XcCV*yiW8#Ie98WY1~Sb#0u}aR53+OZ-73liZ4@1!0_#l$BRds?yW4iYoIB#l zn?CU{Id7+171{H8n?Lbs=x&L+xS7fL?RFLy^6_pmQ=%;}D;dk|`Z9zwn>~mBbUw#! zSIP3+NX(4P$gA48S+e#pzteB)`p5ZRgagm@RLAI#np3Gg*KZdYXr}9*l=4nk>lTgE zkT#Uo#y4!(FcI83O*odDjpG+N^wi0U6B~<2sG%5q9LtXC+v;q~VjD|gvR;0ug)#Yf zy!X2Mkov{jcH&|9Hl&uVB5p~zgR)A^Jl^cpjUS4yoUyWWk%~+r5@JrDFDN0eFHC*= zAd4TP4hEU(TljzR4L7jsMXJU=m=m>mAp3x~D*5xI*%dMc1+Cb#ZxX&3neq)(t$r+^ zx3?CjGIkME=BTKN;di;QCz%<~bB7+g*l(BNw-5?Ns9gQnkZcqg)>9m`X4i*TZy9U+bj&I7n`j zRThpxAhIx5YcXO8BU5 zhheRJqPT0JmO}G*1`*ovjtv*viG4X!#XU@2dtRtY7wTZh&A!vTt8}Mg?ts9c$X=>?De^3nL`qtNFDG^rFb=uvbt!76aL8pNlY0wwImqrg6E z#XZ?qHfXIFiOlC$|IF@{=Vo6= zJdBT43C{V|PPttSg&8I0z8Pi$5tPc97e^^q~(OZ3l-F*~NRE7MdT)hsKwN`%U9e~qW(5$J;t>}?2| zN|#nc_Rj|XLspgr^_|q0RW;tP|tFeNE<(~%l?Ji_nh8?*W36ale zDBMN8Q8so4^3PiXEAp__jodYjRd;K40~SN|#>++L%XBr-@uusA%{4eP%h@>N=H+4v zHrsc=qKgMadv>Qs8YPiuy5$``**YKV@%ZGvZp$u-+ue}#R9F8Mc#vA9@OAvgZ{x%b z%N#~KE56$%VppMoOHZ+(rW)s6$h{exD6a+i`+2_`fB>wXthS}7(TzDKvCb!H8U>10 z1{Ao=375cyvOE1}02ae^Z@+k;LLa57CGtwdQ1Dja zr7Qst?n)6$^Y{;Pqqpn5C1M105ubtyASVaO=F4O3Q52~7aoD(HiE;F>N%`AX_aFRG zx4(Lk?A-NL!R3;cPpmGH*AMJXoyRVUgW`YNlo-m4!~o~wtwQ;^yk7XZkSEL!FO9iX zg!0>pds9i$s8DNXkP@fdf2>V62npNDSJgWdto?l_I#eaTw|GH5aW&rFk-y{edv@t@ zry?PTXjSp5@{n6DL9MAc%%faIQV24UI|Sil(Ec4ZIchv^!b~rGffDb>9IGUyr;EaD z%}4RUwG;2IpVg{8 zZP{?frlfXOIjQ2Ge*MwPkI}pf;WSAb6zHC-W6!S7Cy=imFh=GskB63E>feT{LG(Nx z2gSih11P~J;UBWb1AI-f@e?i$^C)(9$<@_`%uJ^CnkGEqBv=O9r#9{f20+_CWJxDA z!Mhu2(SH5F3TR1$IU57xVA(r-;-|XA_K9#hNB#?HJmHMvq&enudTT{*?3ODQXLZu4 zLHH`QnI~Pdq%7RMd<3q|%Bf~PY{^ufN|=~PI_8if18eRrgXaB>A|L`bc=#dwV$<21 z^ERyK6%4;k0ff$%x?ud1>U0BkON()!7N)I|aZLUa_%3(SB8nSuspz+?@W9<7yE}et|GzW;UJqo%VYqh%}+%7m$Awgvpt}q`W-`3Cf z?(*fe0_Fz{jF$1KNIdCys*Kj$&z7&Fuy4Ym30 zbZY0+Cqv2*r_Nx)((6tXM)n*H-EA#`)vOlzw5q5cqA;ZbvL~{ zGBP|tAc^Nqzy4hznzR4|X;fOkC1WLf@*y%@r4g8Rj1MJ|$CU)C+|ttg6y)P9XU&+> zLg$-}TWIvtAuo~iap`^5uitOll)=4KLci{a{PQ~jwyn=N6t`UR2 z4?0OsW~d!48_F#oH5Wd(Ru=BEhrUA<)MP`0u!kMa@5pZ=Jo-xB+&e@&a2ZBpSVT!* zAYH9V`uiAjF4e>NTPl%Dj1L@z=N#<+W~cK9IHbKI|W|ytWneT$j_+$ZSWC*~;cRnyjm` z!}eY+85;Yn8)x`>op)WtEtCwDCoZ@uC-y8ps)Nx}67bGTw=UDh^}mJxdPjn~niFOY zxa2pF=9{E`UlFO>wBCACBEi(LmhgyXCol87agqPB2ZeTu^eV&8TKUAHZNo{@7I(Sl z@}H*DBsEe>@v#nElhJ=1Ne&8Y7_+C;W~J;x>U{VMu4*T+#|e>$i|`oB6~C??o%gfe z7Y)+R@4Wf8^0efA>0)80`=aE~&+#51A~n14eerQg6T!l01KkEm_~K0dv2*cO*Kwc* zj3fRTe@LJP;fLChOilIbbZyAMOPr~L;fUmiamu@&>Wnq%;-60Ac31B(^_>(3k6qY4 zix5z>Pc-{PtykXUU*Da~Km}4e)ME{6`i%T_lV}eXK->E1Iw}!@Zt$UU*>p{`35`-- z`SY2;{ru}$pmkrP;666#y@WyGi|I<%PXb|8?9Q#67QK2}a-UR$*Aueo0?%pfmr@=!VkZBc!mm(MCsJGvyS zy`{#*iU>`Al*L(*6-5%xrw@=dW7jo?Gz}bMU#a=eK8b(Nb;+9RVtIb)xrz!ppZ8i5Zox?4QG*w9DIB4k)xGjCi9a|WaHdF zZ=NgxPw7}z+>8B1UD2jQ2}oo1f*eJ&Z32wvC6@%7JAM9VA7Oa++Y{MBh_(BM*WLV{ zWtMX#oiGMXM^C@r;pjmM^H8ctlerhY`AG-dsS>kgqvFwYq(q?NR)*?o9K6I+%vwWR z8vFFNg4NF{H~EORv#K`e}C@DpuS^q${E`$hGXT*R)Q;rhEPeo?BkU7J2l&t zYK*v$xsFUiO=CDY6Kyg@wUi)s+g6DChIQ=& zS_fAyb2Yqu=3(}HubiIulStGcA%yAoE$XSl9$Z_MhAj`V3Q@EVHfSQ@+~SC;&8 z2l7n=;t0SGPQ;(W1K`W@Wx*EX>ybLRGg=ex#3VW!cgQ<<7|H0A{=V-X1tP3^umJhr_Ad%fmBUfO+f2r&V3EBxD90w-Z1p`v$Ub=D%ST75j8Ma}t+=Hv6jo6^uRYs**HB?`-@ zYI*P2wQ0`svwLm3AL#nxp?GNJRMM=XIs{m4^@uS`Svio919;i*390A9byHSkx_`QE z49_MaZ#Sq%!w465Athp1rbQ)_7*9fm--MMS>zS+7Bv+OT z;w|8MoFR%0$Xrr33hr1}x;)g$k9}LI?+|n$U>SpJ;sJ)u{moXRJ-I82I*b|?4P;HI zB%<`vK@$WMB$`AgcjbXbu|{ub9#E`7b|~g`3*q(5qKvBgCiNQ$o_RkOW}qVO1y{^w z7`yPKVVUK^A=9WwBjy$;1hNAk3N1|1)iOXxbh|ka8IOptc}6F7J~`n+HLGXB{02W5 z8D25mvNyqyIw2mKvSt|y#;37(wKN)R14$d){eHaQreU-bmhkDpI9+`lBWCAYe&rx8 z{+-o62|tH*vB24#m%-?fkUJrVwl)B~3Vw~@CtXN~%2OL42E>N-g`Nj^RRF*(lo?~R zMnh2ZC4o3bn^A>oq#o`sGuJjG7c&YlsenJL{1>T8gyN$BQFWo+XU9GV1wKjNmOZ(1 ziETslZvk`bfn)!(faW_8+XE6-3EgMI<@s-^&fI{+`~OzzmNNbX+&>pH6@@zl_dmt= zVs=L50;8&({~MM2uVPq@FZ?WA@IJWE7UWq0JA$Eh<I*K%Oyg`qbl`qlaFB=B#HybV*kM3 z76Q=P|D&m1nAzfCp@^s>AD@3KZVjWwhlm10Ei(Hr)VT7$@r<9TAl-PD0f-W;zXfo@ z|My$AHjnw((@MITNOBDOP9jX2mWf_FW0YK$eO3H%nH1FhVNyIH=^_V!X2psImNA2kWgof>4_rPnY6I6~3@79Ui}2++y@&;GHp#1N%d!DYiUmj8~T z)@jUuLW1b`mj9zCZR1GPuQ%O%f*21Z|2-AfLgo#my&C)^+W+~8rxfSe@0;qJw)h0q z966~KqCz_niUCLa46@W~aO4FNaW)6nVu)9f)0X)8xm_?kmpB>ML(-|ZRlYWm@(u`Q zCu3kZTZ5rP+yT;NM(bIn71FJ)a`a7Fnt9AWWV%5AE%)MAL9z(#ptM-^n}@5UZPOsl31gSB9zq za>c(SWj`Pat_~Ul-ipq!9W_~*2`(=%01jLZZA9H>D^uhi<_5Tl|t z6&BaE`(9Ez2B{vIIYXW=pMa$tqXZ*{bU0As$EbYve>Q=q07(MK^UGwAR&B^cMMz16 zC*1-BS4omYA~{WrF11?g{JI z-?4Uy3~r6Oy}V&xOa$+PrsZkxKgYo4D<}FwPUpSwhFSPo5M2zkkon%Ck6{*I-%Wtq zyPIUdMQFpwqkcz_m zM0q^OC1F?f$NkQ_<~YMMbozy7o=S?``jMGdO9(1dvnXWnCn8=h)F`6v-j}{WFH9XD z{qiYKSnus72r9c65$gx!yngXQRM(ys*Wcx1;|#cziVImSJ{Q|dM$;?b1R2yzl}6*8 zZ%lIerW|X2*E@XRR`Ti=F_g_Qk?8^!>Q9xeU(4^>;E&r9@7={9^G_ zO?H34qo;f&`HaHiV*_>RLYWcTCB(y9S1!1+&fb)-T_66!MJCOY5K`eWz`Hue<)Ot; zz#Be{c#Gawy{hW}{@vAQcjC{ov7lKaLfBFAXs?dSCujZ6$MI)4p7RaVWL6@ZNi0tM zQp<EW=`I~CZ>b`cXMbb-0jyORc)*M45BSi6s+eU8A`)#OJ*me5FEZKV}~0a3?AI2`Q0mQ#VOYz|Ig|k4e+7Y8kgv@Fby$8x)YX z1-dusw3MTl(XP5=M6wozO|nzLVF*oL7SyQp(jKOfCoYt_K8_-!)h*}_{~SsoRCgI3 zvY3Jj<-rXaC^J8pq>oqAsktb4CgdU!HVqj->|&t4{4#Fk*f>Fv_A=(he5I1}H=ppw zK@LB!8v!4XKL!3lDgUf$s=ZZ$c9awk6P5t>yM@xY51N#zrZGoG3=hlrjqH=cD~oQ^ z4bSDe@F1mVTB?nNoGiFo^?k+U=B@>?o*SzBM1|uM6Sv41Zz_$Zn)$|W$iAJZ$I6(zVpNe;I=8{o~TcvEIxk{lbKeTDe%g}pK&+U zA9VF7UT5U7iR!E7yv%f(YOr)j5u-kKM&LdBJqziHs<)%L)>Co@s#Rq+3-fxU_^#Hp zv$7ZfDhPDVBJ%6u&RRs2>OY_;=NGvsz8Nj&=DnN9&ZAE4-pji064VgfY*JK|L~utCSkiw9ohvUHAoV2`5`&rYu(UcJeAkYl~QTZ+O=qHDDgG4H4jS zN*^c|3gG>C&X~Eb!1190UZTvr-88*dgtfZ`zEX4VJUq-6VO9T zCNWb-S`?O^%hw-#!+Tj%m@MGjJtE(9PAsENri8fo*ZmS!w3voiK{@54KciGET#|RY zTL*IL-t#Ns;bhDTdJtRp(*9t4i=O(JW&-3Ug0-jg)=l`u==>W=-=JrVRV~w)CG)#2 zBOA8mS}V?=<1b;CQIdCFLHXmH(*Q^k^TDCqd(uYlYy?7&_+-N%D|eFh`c z3sRbJLuWLyGH(R-Rs(45iC&eATY9N5?{u=#UlLTZ$5$F4-|(QVo*uczm~Ox}dV|lD z=9ilv)l4bPj5FCTi37QCLzOBR@ANjSKsa2!ZCmBYWTO-&Tiazpu(`8KjX@V>L6-#@~i*s|821gh_5$>}mzet*YO zs4-GPrj9@;UMZC%rE^;OZ2k}oRCf^X?$WtEO4)x*0OUXL=+9W^n+m+Y!EPo>cH^=izh;-l9=`HxpI7Uwq_$;>Gs#rQ5b#ZSwcgmegdQ zYYg13@AR2mVkYVmC3yR8#lI!UxSY@df~g#byV0F|^4YTNB&uwX-ddCpHU0K;X6}g# zi?O1Z^E9KE>4?66*2{Fh$gckP-@oXqoFp45rR2n_x3}!^ED+D4pP^_87fDdI!Qp&X zKEgec^>c;h+9>+wyUpGrUiUs^i6{7rvwa#)LO*ktwN2JN%6=kJXjYxO{wTgwIWXb9 zdUI3j2V4l3>q|nERMvxH_uzO;Y-~w_Fyng+aVs(x7Yj$u_})*aZgH+~yii1Nir7#-hMx56qHt9#(XlKGgo@E+jZ%7L4bdT>qL<5)A@B=r4K+PP-G z+9aJCvH95=f1GlJO@5L`pipp~Oeu!4@EEd%T*|`A)JW%|g-ba}Y6u*F)y4cO2RqNg zz#$A1<=!nA;Bx2-Gm*w#yf$0wbT@lFvFGv@4)tsw&|87_p{k1|p(IfJs12#VKb$7;W{q+YMJVaQ{R3oiq(=ADf^Q{OrHAd81UO8av-EX*=*cWd>eC7$8lgr1pS6q(&QgF|n zYs{=Fg>xtT6Ac#Mu+yXv`atB2#w23w0;8ih04?tFLD`G%q7Rd*xG z2_J7rsr|@`nY}-MYr+y$Q5)3f{rlI|;*!3#mxbq9Jt7M&WCCdJy1K_Bl}((Taw6}g zmC3yF3!@Gdb8*|c`bLvQH)FXj)9=vQSxAFz+kRhv3N}i(u+x`0Q$2 zt;S{&$VN(-^+pD}&gzYHJbY_os9IlpO>Fr6Vpf)qPE8qI@F>pU$CO(szoXPSk21aG z%F&b$*tDP8#)+jFWo>0ejb(O4nq@78C0VwYMKr3MtbcnZ@s_kml99I`DjPWaWB2q` zXc;01Z?Hp}iCX$pKI&hYIWi=YRj^Q&1XEe}Y=RrEa&>Md_gc_r#o>mie& zsfy+2n()?`mDtAEv`TNb`V=0h=a_-RcViXVUZZ%1+y*a~-L0bJ+v@zLh4kktAFz(( z4BO03UV0SIEyZPOX)FOX68G$`XHgsnI(7MonCTFxv^2?@zpP7uc(@7-b;@&J z@IFN{zgx?J-2s@?qvg>{`Q;2;*PYWk!Ojvy6}2Dq%S>TMfBf{91gp6DxDa)~p+)$) zzp{-y&R&4NpS*JECyBFx>K>ZP1i>%BVQzo!Nv&qg`hG3`b(op!%q#Alr@p;CXkh@v z8wm7YIFB*uxck?JDOF)juhi8F=9gr~7NHtl!6U>~iap~3rGlacR* zR|1`98`N|k`QF94+utP^a>!}BUU&j*_zqp2Ps__nJLnEosD*wsy|kG6N#*-9f3)ZR zjTRRssq609Eyrnnp3jC@U(#3L`MAVjKj;|`WX|l555LSZX`4=!)p*hbrN{8b>yvsY zvirCZ=f+}iVEw{aJUp*VLZar}wJdO97aC>MPy#4o7-WZx@lVTv?KP*NPTB&fGpq?% z6>PT#>Nd)Uex4sTsHH=$P#%*$z-owmldcWx<1FxO4d>=<37_&X(i8!zP+iX)9x@l7 z>+H~1(62d@-M#|*LJY~qSWdd8E)P{aeY+8!W3z_0B-YlPhWf_$$lZD~i0zZ+NjZ{i zI-$3nGP(W{gr^iErvK`2t8G$?J4R`tBKN=!w-W|$?0n@=oa)Drl5#WVi9Yt68>trD z0P=+8g=XIhquQO1`jPv^+9*DPTtf+#NRG+#07Q%V)=%7xwAQwB4S*W@@(=+9hMY+W zRv@(VAwp}M&ohGPb%fe{83-ChHDv!eM5t9dalHf>-xK{)6)iCLLqM*y@f1`bFpYw2 zYkIi(Q^swPs(T!%0XYrWMNC`4j~+Q}ZrqK%gUObK!TAU(TU{qiTfJH3Ktu~NgrUF} zwrjc1Bhbag(!fAf`d3nWntKin>h$-L|C@s&H(mG+d`UN^##q8jNkfn!5JW}UpH6^{ zA6T~7T=B2JN;F;E<9b1Um?HoE{4od{X%0Y%-kxNunq( z{cYrl1>>>QlxV_EM^fd7Axpk3PS|t6kW{t&Uh6;Z$L9F;gz*@D6c|#{Z(pii+-<-I zD`DY7j>3m7J;TE{HqmY1T zZL5s|D|F#E&N)k8dp}k1&6r7ve_|p;E}g-CdhNa2R>T*U0Wng zbITB`x{&jjg55|t|DOR(_|q{tCLl%r@3k8kGhe)Lr?LLcf7=;ZEQQ1qy!~bWCcv9J zP3v~JmR-C$a|KrI;1PIo+8;pfS^<7(7<7;5r?U?P0F>bGvA>i8z(_cy6#PBVhE()$ zfDH_2jP%+YitE@?xQoQ55o#hv9^)^Ja^I?e4lS*84qXz zfPYUzAqBL6CD4}xsPMl%jqv)n`>cb8fY26oeTFCvS-7bkg>4Oo)V1# zTo^=tg504~yBG*K7y^{(ph1Jc!~aE%0DuLk!#J4QU)zwIa_cE$s3q$w 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