Browse files

Initial Python streamserver library

  • Loading branch information...
0 parents commit 3fdc813c15750a1ad4befa1acd7caae3901a7ec8 @paulj committed Apr 16, 2012
3 .gitignore
@@ -0,0 +1,3 @@
+env/
+*.pyc
+*.egg-info/
13 LICENSE.txt
@@ -0,0 +1,13 @@
+Copyright (c) 2012 About Echo.
+
+Permission to use, copy, modify, and/or distribute this software for any
+purpose with or without fee is hereby granted, provided that the above
+copyright notice and this permission notice appear in all copies.
+
+THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
7 Makefile
@@ -0,0 +1,7 @@
+init:
+ virtualenv env
+ env/bin/python setup.py develop
+ env/bin/pip install -r requirements.txt
+
+test:
+ env/bin/nosetests ./tests/*
6 README.rst
@@ -0,0 +1,6 @@
+=============================
+Echo StreamServer API Library
+=============================
+
+The Echo StreamServer API library provides support for working with the Echo StreamServer from Python. All interaction
+is via the Echo HTTP/REST API, as described at <http://wiki.aboutecho.com/w/page/35344006/Echo%20overview>.
2 requirements.txt
@@ -0,0 +1,2 @@
+nose==1.1.2
+rudolf2==0.3
20 setup.py
@@ -0,0 +1,20 @@
+try:
+ from setuptools import setup
+except ImportError:
+ from distutils.core import setup
+
+setup(
+ name='echo-streamserver',
+ version='0.1.0',
+ author='Echo',
+ author_email='solutions@aboutecho.com',
+ packages=['streamserver'],
+ scripts=[],
+ url='http://pypi.python.org/pypi/echo-streamserver/',
+ license='LICENSE.txt',
+ description='Echo StreamServer API library.',
+ long_description=open('README.rst').read(),
+ install_requires=[
+ "requests >= 0.10.8",
+ ],
+)
23 streamserver/__init__.py
@@ -0,0 +1,23 @@
+# -*- coding: utf-8 -*-
+
+"""
+Echo StreamServer API library
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+:copyright: (c) 2012 by About Echo
+:license: ISC, see LICENSE for more details.
+
+"""
+
+__title__ = 'streamserver'
+__version__ = '0.0.1'
+__build__ = 0x000001
+__author__ = 'Paul Jones'
+__license__ = 'ISC'
+__copyright__ = 'Copyright 2012 About Echo'
+
+from .items_client import ItemsClient
+from .feeds_client import FeedsClient
+from .users_client import UsersClient
+from .kv_client import KVClient
+from .echo_client import (EchoAuthMethod, EchoAuthConfig)
50 streamserver/echo_client.py
@@ -0,0 +1,50 @@
+import requests
+import json
+
+class EchoAuthMethod:
+ BASIC='basic'
+ OAUTH='oauth'
+
+class EchoAuthConfig:
+ def __init__(self, appkey = None, secret = None, method = EchoAuthMethod.BASIC):
+ self.appkey = appkey
+ self.secret = secret
+ self.method = method
+
+class EchoClient:
+ def __init__(self, auth = EchoAuthConfig(), host = "https://api.echoenabled.com/v1"):
+ self.auth = auth
+ self.host = host
+
+ def execute_query(self, method, params):
+ full_params = params.copy()
+ full_params.update({'appkey' : self.auth.appkey})
+
+ res = requests.get(self.host + "/" + method, params=full_params)
+ self.assert_status(res)
+
+ return json.loads(res.content)
+
+ def assert_status(self, response):
+ if response.status_code == 200:
+ return True
+ else:
+ # Attempt to decode as a JSON response
+ res = json.loads(response.content)
+ if res.errorCode == "incorrect_appkey":
+ raise InvalidOrMissingAppKeyException()
+ else:
+ # TODO: More decoding!
+ raise EchoException()
+
+
+class EchoException(RuntimeError):
+ """An exception occurred executing an Echo request"""
+
+class InvalidRequestException(EchoException):
+ """A request submitted to Echo was invalid"""
+
+class InvalidOrMissingAppKeyException(InvalidRequestException):
+ """The appkey provided to echo was blank or invalid"""
+
+
9 streamserver/feeds_client.py
@@ -0,0 +1,9 @@
+class FeedsClient:
+ def list(self):
+ pass
+
+ def register(self):
+ pass
+
+ def unregister(self):
+ pass
19 streamserver/items_client.py
@@ -0,0 +1,19 @@
+from echo_client import EchoClient
+
+class ItemsClient(EchoClient):
+ def submit(self, items):
+ pass
+
+ def search(self, query, since=None):
+ params = {'q':str(query)}
+ if since != None:
+ params.since = str(since)
+
+ return self.execute_query('search', params)
+
+ def count(self, query):
+ res = self.execute_query('count', {'q':str(query)})
+ return res['count']
+
+ def mux(self):
+ pass
9 streamserver/kv_client.py
@@ -0,0 +1,9 @@
+class KVClient:
+ def get(self, key):
+ pass
+
+ def put(self, key, value, public=False):
+ pass
+
+ def delete(self, key):
+ pass
62 streamserver/querybuilder.py
@@ -0,0 +1,62 @@
+import string
+
+def query():
+ return MainQueryBuilder()
+
+def subquery():
+ return SubQueryBuilder()
+
+class QueryBuilderBase:
+ def __init__(self):
+ self.terms = []
+
+ def add_term(self, term, value = None):
+ if value == None:
+ self.terms.append(term)
+ else:
+ self.terms.append(term + ":" + str(value))
+
+ def __str__(self):
+ return string.join(self.terms, " ")
+
+class MainQueryBuilder(QueryBuilderBase):
+ def __init__(self):
+ QueryBuilderBase.__init__(self)
+
+ def scope(self, s):
+ self.add_term("scope", s)
+ return self
+
+ def childrenof(self, s):
+ self.add_term("childrenof", s)
+ return self
+
+ def children(self, count=None):
+ self.add_term("children", count)
+ return ChildrenQueryBuilder(str(self))
+
+ def or_terms(self, *terms):
+ self.add_term(" OR ".join(map(lambda t: "(" + str(t) + ")", terms)))
+ return self
+
+class SubQueryBuilder(QueryBuilderBase):
+ def __init__(self):
+ QueryBuilderBase.__init__(self)
+
+ def scope(self, s):
+ self.add_term("scope", s)
+ return self
+
+class ChildrenQueryBuilder(QueryBuilderBase):
+ def __init__(self, prefix):
+ QueryBuilderBase.__init__(self)
+
+ self.prefix = prefix
+
+ def itemsPerPage(self, count):
+ self.add_term("itemsPerPage", count)
+ return self
+
+ def __str__(self):
+ return self.prefix + " " + QueryBuilderBase.__str__(self)
+
9 streamserver/users_client.py
@@ -0,0 +1,9 @@
+class UsersClient:
+ def get(self, identityURL):
+ pass
+
+ def update(self, identityURL, subject, content):
+ pass
+
+ def whoami(self, sessionID):
+ pass
5 tests/feeds_client_tests.py
@@ -0,0 +1,5 @@
+import unittest
+
+class FeedsClientTestSuite(unittest.TestCase):
+ def test_foo(self):
+ pass
16 tests/items_client_tests.py
@@ -0,0 +1,16 @@
+import unittest
+
+from streamserver import (ItemsClient, EchoAuthConfig)
+import streamserver.querybuilder as qb
+
+class ItemsClientTestSuite(unittest.TestCase):
+ def test_search_items(self):
+ c = ItemsClient(auth = EchoAuthConfig(appkey = "test.echoenabled.com"))
+ print(c.search(qb.query().childrenof('http://echosandbox.com/use-cases/commenting')))
+ # TODO: Validate the response
+
+ def test_count_items(self):
+ c = ItemsClient(auth = EchoAuthConfig(appkey = "test.echoenabled.com"))
+ print(c.count(qb.query().childrenof('http://echosandbox.com/use-cases/commenting')))
+ # TODO: Validate the response
+
20 tests/querybuilder_test.py
@@ -0,0 +1,20 @@
+import unittest
+
+import streamserver.querybuilder as qb
+
+class QueryBuilderTestSuite(unittest.TestCase):
+ def test_scope_only(self):
+ q = qb.query().scope('http://example.com/m')
+ self.assertEqual(str(q), "scope:http://example.com/m")
+
+ def test_scope_and_children(self):
+ q = qb.query().scope('http://example.com/m').children(2).itemsPerPage(5)
+ self.assertEqual(str(q), "scope:http://example.com/m children:2 itemsPerPage:5")
+
+ def test_scope_and_unnumbered_children(self):
+ q = qb.query().scope('http://example.com/m').children().itemsPerPage(5)
+ self.assertEqual(str(q), "scope:http://example.com/m children itemsPerPage:5")
+
+ def test_multiple_or_terms(self):
+ q = qb.query().or_terms(qb.subquery().scope('http://example.com/m'), qb.subquery().scope('http://example.com/n'))
+ self.assertEqual(str(q), "(scope:http://example.com/m) OR (scope:http://example.com/n)")

0 comments on commit 3fdc813

Please sign in to comment.