Repository: usergrid-python Updated Branches: refs/heads/master [created] f700740a2
Initial commit of Usergrid Python SDK into its separate repo. Project: http://git-wip-us.apache.org/repos/asf/usergrid-python/repo Commit: http://git-wip-us.apache.org/repos/asf/usergrid-python/commit/f700740a Tree: http://git-wip-us.apache.org/repos/asf/usergrid-python/tree/f700740a Diff: http://git-wip-us.apache.org/repos/asf/usergrid-python/diff/f700740a Branch: refs/heads/master Commit: f700740a282cfb4829b6982b72160f1eabffaec1 Parents: Author: Michael Russo <[email protected]> Authored: Fri Sep 2 08:57:57 2016 -0700 Committer: Michael Russo <[email protected]> Committed: Fri Sep 2 08:57:57 2016 -0700 ---------------------------------------------------------------------- .gitignore | 60 +++++ GUIDE.md | 2 + LICENSE | 202 +++++++++++++++++ README.md | 16 ++ README.rst | 20 ++ sample_app.py | 77 +++++++ setup.py | 51 +++++ usergrid/UsergridApplication.py | 65 ++++++ usergrid/UsergridAuth.py | 105 +++++++++ usergrid/UsergridClient.py | 401 +++++++++++++++++++++++++++++++++ usergrid/UsergridCollection.py | 82 +++++++ usergrid/UsergridConnection.py | 30 +++ usergrid/UsergridError.py | 21 ++ usergrid/UsergridOrganization.py | 35 +++ usergrid/UsergridQueryIterator.py | 157 +++++++++++++ usergrid/__init__.py | 37 +++ usergrid/app_templates.py | 38 ++++ usergrid/management_templates.py | 27 +++ 18 files changed, 1426 insertions(+) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/usergrid-python/blob/f700740a/.gitignore ---------------------------------------------------------------------- diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..e0c9cd2 --- /dev/null +++ b/.gitignore @@ -0,0 +1,60 @@ +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] + +# C extensions +*.so + +# Distribution / packaging +.Python +env/ +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +*.egg-info/ +.installed.cfg +*.egg + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*,cover + +# Translations +*.mo +*.pot + +# Django stuff: +*.log + +# Sphinx documentation +docs/_build/ + +# PyBuilder +target/ + +# Intellij +.idea \ No newline at end of file http://git-wip-us.apache.org/repos/asf/usergrid-python/blob/f700740a/GUIDE.md ---------------------------------------------------------------------- diff --git a/GUIDE.md b/GUIDE.md new file mode 100644 index 0000000..0719005 --- /dev/null +++ b/GUIDE.md @@ -0,0 +1,2 @@ + +https://docs.python.org/2/distutils/packageindex.html \ No newline at end of file http://git-wip-us.apache.org/repos/asf/usergrid-python/blob/f700740a/LICENSE ---------------------------------------------------------------------- diff --git a/LICENSE b/LICENSE new file mode 100755 index 0000000..8f71f43 --- /dev/null +++ b/LICENSE @@ -0,0 +1,202 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright {yyyy} {name of copyright owner} + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + http://git-wip-us.apache.org/repos/asf/usergrid-python/blob/f700740a/README.md ---------------------------------------------------------------------- diff --git a/README.md b/README.md new file mode 100755 index 0000000..5dd5bd9 --- /dev/null +++ b/README.md @@ -0,0 +1,16 @@ +# Usergrid Python SDK + +# Overview +This is a starter project for the Usergrid Python SDK. It is a work in progress. + +# Installation + +## PIP (http://pip.readthedocs.org/en/stable/installing/) + +`pip install usergrid` + +## Manual installation + +- `git clone [email protected]:apache/usergrid-python.git` +- `cd usergrid` +- `pip install -e .` \ No newline at end of file http://git-wip-us.apache.org/repos/asf/usergrid-python/blob/f700740a/README.rst ---------------------------------------------------------------------- diff --git a/README.rst b/README.rst new file mode 100755 index 0000000..659384c --- /dev/null +++ b/README.rst @@ -0,0 +1,20 @@ +********** +Overview +********** + +This is a starter project for the Usergrid Python SDK. It is a work in progress. + +************************** +Installation +************************** + +================================================ +Installation From Pypi Using PIP +================================================ + +PIP is a package manager for Python. For more information please view the information here: `PIP Installation Guide <http://pip.readthedocs.org/en/stable/installing/>`_ + +From the command line:: + + pip install usergrid + http://git-wip-us.apache.org/repos/asf/usergrid-python/blob/f700740a/sample_app.py ---------------------------------------------------------------------- diff --git a/sample_app.py b/sample_app.py new file mode 100755 index 0000000..9deefbe --- /dev/null +++ b/sample_app.py @@ -0,0 +1,77 @@ +# */ +# * Licensed to the Apache Software Foundation (ASF) under one +# * or more contributor license agreements. See the NOTICE file +# * distributed with this work for additional information +# * regarding copyright ownership. The ASF licenses this file +# * to you under the Apache License, Version 2.0 (the +# * "License"); you may not use this file except in compliance +# * with the License. You may obtain a copy of the License at +# * +# * http://www.apache.org/licenses/LICENSE-2.0 +# * +# * Unless required by applicable law or agreed to in writing, +# * software distributed under the License is distributed on an +# * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# * KIND, either express or implied. See the License for the +# * specific language governing permissions and limitations +# * under the License. +# */ + +from usergrid import Usergrid + +__author__ = 'Jeff West @ ApigeeCorporation' + + +def main(): + Usergrid.init(org_id='jwest1', + app_id='sandbox') + + response = Usergrid.DELETE('pets', 'max') + + if not response.ok: + print 'Failed to delete max: %s' % response + exit() + + response = Usergrid.DELETE('owners', 'jeff') + + if not response.ok: + print 'Failed to delete Jeff: %s' % response + exit() + + response = Usergrid.POST('pets', {'name': 'max'}) + + if response.ok: + pet = response.first() + + print pet + + response = Usergrid.POST('owners', {'name': 'jeff'}) + + if response.ok: + owner = response.first() + + print owner + + response = pet.connect('ownedBy', owner) + + if response.ok: + print 'Connected!' + + response = pet.disconnect('ownedBy', owner) + + if response.ok: + print 'all done!' + else: + print response + + else: + print 'failed to connect: %s' % response + + else: + print 'Failed to create Jeff: %s' % response + + else: + print response + + +main() http://git-wip-us.apache.org/repos/asf/usergrid-python/blob/f700740a/setup.py ---------------------------------------------------------------------- diff --git a/setup.py b/setup.py new file mode 100755 index 0000000..8a2d332 --- /dev/null +++ b/setup.py @@ -0,0 +1,51 @@ +# */ +# * Licensed to the Apache Software Foundation (ASF) under one +# * or more contributor license agreements. See the NOTICE file +# * distributed with this work for additional information +# * regarding copyright ownership. The ASF licenses this file +# * to you under the Apache License, Version 2.0 (the +# * "License"); you may not use this file except in compliance +# * with the License. You may obtain a copy of the License at +# * +# * http://www.apache.org/licenses/LICENSE-2.0 +# * +# * Unless required by applicable law or agreed to in writing, +# * software distributed under the License is distributed on an +# * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# * KIND, either express or implied. See the License for the +# * specific language governing permissions and limitations +# * under the License. +# */ + +__author__ = 'Jeff West @ ApigeeCorporation' + +from setuptools import setup, find_packages + +VERSION = '0.1.13.1' + +with open('README.rst') as file: + long_description = file.read() + +setup( + name='usergrid', + version=VERSION, + description='Usergrid SDK for Python', + url='http://usergrid.apache.org', + download_url="https://codeload.github.com/jwest-apigee/usergrid-python/zip/v" + VERSION, + author='Jeff West', + author_email='[email protected]', + packages=find_packages(), + long_description=long_description, + install_requires=[ + 'requests', + 'urllib3' + ], + entry_points={ + }, + classifiers=[ + 'Development Status :: 4 - Beta', + 'Intended Audience :: Developers', + 'Operating System :: OS Independent', + 'Topic :: Software Development', + ] +) http://git-wip-us.apache.org/repos/asf/usergrid-python/blob/f700740a/usergrid/UsergridApplication.py ---------------------------------------------------------------------- diff --git a/usergrid/UsergridApplication.py b/usergrid/UsergridApplication.py new file mode 100644 index 0000000..9c86efd --- /dev/null +++ b/usergrid/UsergridApplication.py @@ -0,0 +1,65 @@ +# */ +# * Licensed to the Apache Software Foundation (ASF) under one +# * or more contributor license agreements. See the NOTICE file +# * distributed with this work for additional information +# * regarding copyright ownership. The ASF licenses this file +# * to you under the Apache License, Version 2.0 (the +# * "License"); you may not use this file except in compliance +# * with the License. You may obtain a copy of the License at +# * +# * http://www.apache.org/licenses/LICENSE-2.0 +# * +# * Unless required by applicable law or agreed to in writing, +# * software distributed under the License is distributed on an +# * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# * KIND, either express or implied. See the License for the +# * specific language governing permissions and limitations +# * under the License. +# */ + +import logging +from usergrid import UsergridError, UsergridCollection +from usergrid.app_templates import app_url_template + +__author__ = '[email protected]' + +class UsergridApplication(object): + def __init__(self, app_id, client): + self.app_id = app_id + self.client = client + self.logger = logging.getLogger('usergrid.UsergridClient') + + def list_collections(self): + url = app_url_template.format(app_id=self.app_id, + **self.client.url_data) + r = self.client.get(url) + + if r.status_code == 200: + api_response = r.json() + collection_list = api_response.get('entities')[0].get('metadata', {}).get('collections', {}) + collections = {} + + for collection_name in collection_list: + collections[collection_name] = UsergridCollection(self.client.org_id, + self.app_id, + collection_name, + self.client) + + return collections + + else: + raise UsergridError(message='Unable to post to list collections', + status_code=r.status_code, + api_response=r, + url=url) + + def collection(self, collection_name): + return UsergridCollection(self.client.org_id, + self.app_id, + collection_name, + self.client) + + def authenticate_app_client(self, + **kwargs): + + return self.client.authenticate_app_client(self.app_id, **kwargs) http://git-wip-us.apache.org/repos/asf/usergrid-python/blob/f700740a/usergrid/UsergridAuth.py ---------------------------------------------------------------------- diff --git a/usergrid/UsergridAuth.py b/usergrid/UsergridAuth.py new file mode 100644 index 0000000..f29bcb9 --- /dev/null +++ b/usergrid/UsergridAuth.py @@ -0,0 +1,105 @@ +# */ +# * Licensed to the Apache Software Foundation (ASF) under one +# * or more contributor license agreements. See the NOTICE file +# * distributed with this work for additional information +# * regarding copyright ownership. The ASF licenses this file +# * to you under the Apache License, Version 2.0 (the +# * "License"); you may not use this file except in compliance +# * with the License. You may obtain a copy of the License at +# * +# * http://www.apache.org/licenses/LICENSE-2.0 +# * +# * Unless required by applicable law or agreed to in writing, +# * software distributed under the License is distributed on an +# * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# * KIND, either express or implied. See the License for the +# * specific language governing permissions and limitations +# * under the License. +# */ + +import json +import requests +from usergrid.management_templates import org_token_url_template + + +class UsergridAuth: + def __init__(self, + grant_type, + url_template, + username=None, + password=None, + client_id=None, + client_secret=None, + token_ttl_seconds=86400): + + self.grant_type = grant_type + self.username = username + self.password = password + self.client_id = client_id + self.client_secret = client_secret + self.token_ttl_seconds = token_ttl_seconds + self.url_template = url_template + self.access_token = None + + def get_token_request(self): + if self.grant_type == 'client_credentials': + return { + 'grant_type': 'client_credentials', + 'client_id': self.client_id, + 'client_secret': self.client_secret, + 'ttl': self.token_ttl_seconds * 1000 + } + elif self.grant_type == 'password': + return { + 'grant_type': 'password', + 'username': self.username, + 'password': self.password, + 'ttl': self.token_ttl_seconds * 1000 + } + + else: + raise ValueError('Unspecified/unknown grant type: %s' % self.grant_type) + + def authenticate(self, client): + token_request = self.get_token_request() + + url = self.url_template.format(**client.url_data) + + r = requests.post(url, data=json.dumps(token_request)) + + if r.status_code == 200: + response = r.json() + self.access_token = response.get('access_token') + + else: + raise ValueError('Unable to authenticate: %s' % r.text) + + +class UsergridOrgAuth(UsergridAuth): + def __init__(self, client_id, client_secret, token_ttl_seconds=86400): + UsergridAuth.__init__(self, + grant_type='client_credentials', + url_template=org_token_url_template, + client_id=client_id, + client_secret=client_secret, + token_ttl_seconds=token_ttl_seconds) + + +class UsergridAppAuth(UsergridAuth): + def __init__(self, client_id, client_secret, token_ttl_seconds=86400): + UsergridAuth.__init__(self, + grant_type='client_credentials', + url_template=app_token_url_template, + client_id=client_id, + client_secret=client_secret, + token_ttl_seconds=token_ttl_seconds) + + +class UsergridUserAuth(UsergridAuth): + def __init__(self, username, password, token_ttl_seconds=86400): + UsergridAuth.__init__(self, + grant_type='password', + url_template=app_token_url_template, + username=username, + password=password, + token_ttl_seconds=token_ttl_seconds) http://git-wip-us.apache.org/repos/asf/usergrid-python/blob/f700740a/usergrid/UsergridClient.py ---------------------------------------------------------------------- diff --git a/usergrid/UsergridClient.py b/usergrid/UsergridClient.py new file mode 100644 index 0000000..cedeaab --- /dev/null +++ b/usergrid/UsergridClient.py @@ -0,0 +1,401 @@ +# */ +# * Licensed to the Apache Software Foundation (ASF) under one +# * or more contributor license agreements. See the NOTICE file +# * distributed with this work for additional information +# * regarding copyright ownership. The ASF licenses this file +# * to you under the Apache License, Version 2.0 (the +# * "License"); you may not use this file except in compliance +# * with the License. You may obtain a copy of the License at +# * +# * http://www.apache.org/licenses/LICENSE-2.0 +# * +# * Unless required by applicable law or agreed to in writing, +# * software distributed under the License is distributed on an +# * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# * KIND, either express or implied. See the License for the +# * specific language governing permissions and limitations +# * under the License. +# */ + +import json +import logging +import requests +from usergrid.UsergridAuth import UsergridAppAuth +from usergrid.app_templates import get_entity_url_template, post_collection_url_template, put_entity_url_template, \ + delete_entity_url_template, connect_entities_by_type_template, assign_role_url_template + +__author__ = '[email protected]' + + +def value_error(message): + raise ValueError(message) + + +def usergrid_error(r): + pass + + +class Usergrid(object): + client = None + + @staticmethod + def init(org_id, + app_id, + **kwargs): + Usergrid.client = UsergridClient(org_id, app_id, **kwargs) + + @staticmethod + def GET(collection, uuid_name, **kwargs): + return Usergrid.client.GET(collection, uuid_name, **kwargs) + + @staticmethod + def PUT(collection, uuid_name, data, **kwargs): + return Usergrid.client.PUT(collection, uuid_name, data, **kwargs) + + @staticmethod + def POST(collection, data, **kwargs): + return Usergrid.client.POST(collection, data, **kwargs) + + @staticmethod + def DELETE(collection, uuid_name, **kwargs): + return Usergrid.client.DELETE(collection, uuid_name, **kwargs) + + @staticmethod + def connect_entities(from_entity, relationship, to_entity, **kwargs): + return Usergrid.client.connect_entities(from_entity, relationship, to_entity, **kwargs) + + @staticmethod + def disconnect_entities(from_entity, relationship, to_entity, **kwargs): + return Usergrid.client.disconnect_entities(from_entity, relationship, to_entity, **kwargs) + + @staticmethod + def assign_role(role_uuid_name, user_entity, **kwargs): + return Usergrid.client.assign_role(role_uuid_name, user_entity, **kwargs) + + +class UsergridResponse(object): + def __init__(self, api_response, client): + self.api_response = api_response + self.client = client + + if api_response is None: + self.ok = False + self.body = 'No Response' + + else: + self.headers = api_response.headers + + if api_response.status_code == 200: + self.ok = True + self.body = api_response.json() + self.entities = self.body.get('entities', []) + + else: + self.ok = False + + if api_response.headers.get('Content-type') == 'application/json': + self.body = api_response.json() + else: + self.body = 'HTTP %s: %s' % (api_response.status_code, api_response.text) + + def __str__(self): + return json.dumps(self.body) + + def first(self): + return UsergridEntity(entity_data=self.entities[0]) if self.ok and self.entities and len( + self.entities) > 0 else None + + def entity(self): + return self.first() + + def last(self): + return UsergridEntity(entity_data=self.entities[len(self.entities) - 1]) if self.ok and self.entities and len( + self.entities) > 0 else None + + def has_next_page(self): + return 'cursor' in self.body if self.ok else False + + +class UsergridEntity(object): + def __init__(self, entity_data): + self.entity_data = entity_data + + def __str__(self): + return json.dumps(self.entity_data) + + def get(self, name, default=None): + return self.entity_data.get(name, default) + + def entity_id(self): + + if self.entity_data.get('type', '').lower() in ['users', 'user']: + return self.entity_data.get('uuid', self.entity_data.get('username')) + + return self.entity_data.get('uuid', self.entity_data.get('name')) + + def can_mutate_or_load(self): + entity_id = self.entity_id() + + if entity_id is None or self.entity_data.get('type') is None: + return False + + return True + + def put_property(self, name, value): + self.entity_data[name] = value + + def put_properties(self, properties): + if isinstance(properties, dict): + self.entity_data.update(properties) + + def remove_property(self, name): + + if name is not None and name in self.entity_data: + del self.entity_data[name] + + def remove_properties(self, properties): + if isinstance(properties, (list, dict)): + for property_name in properties: + self.remove_property(property_name) + + def append(self, array_name, value): + if array_name in self.entity_data: + if isinstance(self.entity_data[array_name], list): + self.entity_data[array_name].append(value) + else: + self.entity_data[array_name] = [value] + + def prepend(self, array_name, value): + if array_name in self.entity_data: + if isinstance(self.entity_data[array_name], list): + self.entity_data[array_name].pre(value) + else: + self.entity_data[array_name] = [value] + + def insert(self, array_name, value, index): + if array_name in self.entity_data: + if isinstance(self.entity_data[array_name], list): + self.entity_data[array_name].insert(index, value) + + def shift(self, array_name): + if array_name in self.entity_data: + if isinstance(self.entity_data[array_name], list): + value = self.entity_data[array_name][0] + self.entity_data[array_name] = self.entity_data[array_name][1:] + return value + + return None + + def reload(self): + if not self.can_mutate_or_load(): + raise ValueError('Unable to reload entity: No uuid nor name') + + response = Usergrid.GET(collection=self.entity_data.get('type'), + uuid_name=self.entity_id()) + if response.ok: + self.entity_data.update(response.entity().entity_data) + + else: + raise ValueError('Unable to reload entity: %s' % response) + + def save(self): + if not self.can_mutate_or_load(): + raise ValueError('Unable to save entity: No uuid nor name') + + response = Usergrid.PUT(collection=self.entity_data.get('type'), + uuid_name=self.entity_id(), + data=self.entity_data) + + if response.ok and 'uuid' not in self.entity_data: + self.entity_data['uuid'] = response.entity().get('uuid') + + return response + + def remove(self): + if not self.can_mutate_or_load(): + raise ValueError('Unable to delete entity: No uuid nor name') + + return Usergrid.DELETE(collection=self.entity_data.get('type'), + uuid_name=self.entity_id()) + + def get_connections(self, relationship, direction='connecting'): + pass + + def connect(self, relationship, to_entity): + + if not to_entity.can_mutate_or_load(): + raise ValueError('Unable to connect to entity - no uuid or name') + + if not self.can_mutate_or_load(): + raise ValueError('Unable from connect to entity - no uuid or name') + + return Usergrid.connect_entities(self, relationship, to_entity) + + def disconnect(self, relationship, to_entity): + if not to_entity.can_mutate_or_load(): + raise ValueError('Unable to connect to entity - no uuid or name') + + if not self.can_mutate_or_load(): + raise ValueError('Unable from connect to entity - no uuid or name') + + return Usergrid.disconnect_entities(self, relationship, to_entity) + + def attach_asset(self, filename, data, content_type): + pass + + def download_asset(self, content_type=None): + pass + + +class UsergridClient(object): + def __init__(self, + org_id, + app_id, + base_url='http://api.usergrid.com', + client_id=None, + client_secret=None, + token_ttl_seconds=86400, + auth_fallback="none"): + + self.base_url = base_url + self.org_id = org_id + self.app_id = app_id + self.auth_fallback = auth_fallback + self.logger = logging.getLogger('usergrid.UsergridClient') + self.session = requests.Session() + + self.url_data = { + 'base_url': base_url, + 'org_id': org_id, + 'app_id': app_id + } + + if client_id and not client_secret: + value_error('Client ID Specified but not Secret') + + elif client_secret and not client_id: + value_error('Client ID Specified but not Secret') + + elif client_secret and client_id: + self.auth = UsergridAppAuth(client_id=client_id, + client_secret=client_secret, + token_ttl_seconds=token_ttl_seconds) + + self.auth.authenticate(self) + self.session.headers.update({'Authorization': 'Bearer %s' % self.auth.access_token}) + + def __str__(self): + return json.dumps({ + 'base_url': self.base_url, + 'org_id': self.org_id, + 'app_id': self.app_id, + 'access_token': self.auth.access_token + }) + + def GET(self, collection, uuid_name, connections='none', auth=None, **kwargs): + url = get_entity_url_template.format(collection=collection, + uuid_name=uuid_name, + connections=connections, + **self.url_data) + if auth: + r = requests.get(url, headers={'Authorization': 'Bearer %s' % auth.access_token}) + + else: + r = self.session.get(url) + + return UsergridResponse(r, self) + + def PUT(self, collection, uuid_name, data, auth=None, **kwargs): + url = put_entity_url_template.format(collection=collection, + uuid_name=uuid_name, + **self.url_data) + + if auth: + r = requests.put(url, + data=json.dumps(data), + headers={'Authorization': 'Bearer %s' % auth.access_token}) + else: + r = self.session.put(url, data=json.dumps(data)) + + return UsergridResponse(r, self) + + def POST(self, collection, data, auth=None, **kwargs): + url = post_collection_url_template.format(collection=collection, + **self.url_data) + + if auth: + r = requests.post(url, + data=json.dumps(data), + headers={'Authorization': 'Bearer %s' % auth.access_token}) + else: + r = self.session.post(url, data=json.dumps(data)) + + return UsergridResponse(r, self) + + def DELETE(self, collection, uuid_name, auth=None, **kwargs): + url = delete_entity_url_template.format(collection=collection, + uuid_name=uuid_name, + **self.url_data) + + if auth: + r = requests.delete(url, headers={'Authorization': 'Bearer %s' % auth.access_token}) + else: + r = self.session.delete(url) + + return UsergridResponse(r, self) + + def connect_entities(self, from_entity, relationship, to_entity, auth=None, **kwargs): + + url = connect_entities_by_type_template.format(from_collection=from_entity.get('type'), + from_uuid_name=from_entity.entity_id(), + relationship=relationship, + to_collection=to_entity.get('type'), + to_uuid_name=to_entity.entity_id(), + **self.url_data) + + if auth: + r = requests.post(url, headers={'Authorization': 'Bearer %s' % auth.access_token}) + else: + r = self.session.post(url) + + return UsergridResponse(r, self) + + def assign_role(self, role_uuid_name, entity, auth=None, **kwargs): + url = assign_role_url_template.format(role_uuid_name=role_uuid_name, + entity_type=entity.get('type'), + entity_uuid_name=entity.entity_id(), + **self.url_data) + + if auth: + r = requests.delete(url, headers={'Authorization': 'Bearer %s' % auth.access_token}) + else: + r = self.session.delete(url) + + return UsergridResponse(r, self) + + def disconnect_entities(self, from_entity, relationship, to_entity, auth=None, **kwargs): + url = connect_entities_by_type_template.format(from_collection=from_entity.get('type'), + from_uuid_name=from_entity.entity_id(), + relationship=relationship, + to_collection=to_entity.get('type'), + to_uuid_name=to_entity.entity_id(), + **self.url_data) + + if auth: + r = requests.delete(url, headers={'Authorization': 'Bearer %s' % auth.access_token}) + else: + r = self.session.delete(url) + + return UsergridResponse(r, self) + + +class UsergridUser(object): + def __init__(self): + pass + + +class UsergridAsset(object): + def __init__(self, filename, data, content_type): + self.filename = filename + self.data = data + self.content_type = content_type http://git-wip-us.apache.org/repos/asf/usergrid-python/blob/f700740a/usergrid/UsergridCollection.py ---------------------------------------------------------------------- diff --git a/usergrid/UsergridCollection.py b/usergrid/UsergridCollection.py new file mode 100644 index 0000000..eb8863d --- /dev/null +++ b/usergrid/UsergridCollection.py @@ -0,0 +1,82 @@ +# */ +# * Licensed to the Apache Software Foundation (ASF) under one +# * or more contributor license agreements. See the NOTICE file +# * distributed with this work for additional information +# * regarding copyright ownership. The ASF licenses this file +# * to you under the Apache License, Version 2.0 (the +# * "License"); you may not use this file except in compliance +# * with the License. You may obtain a copy of the License at +# * +# * http://www.apache.org/licenses/LICENSE-2.0 +# * +# * Unless required by applicable law or agreed to in writing, +# * software distributed under the License is distributed on an +# * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# * KIND, either express or implied. See the License for the +# * specific language governing permissions and limitations +# * under the License. +# */ + +__author__ = '[email protected]' + + +class UsergridCollection(object): + def __init__(self, org_id, app_id, collection_name, client): + self.org_id = org_id + self.app_id = app_id + self.collection_name = collection_name + self.client = client + + def __str__(self): + return json.dumps({ + 'org_id': self.org_id, + 'app_id': self.app_id, + 'collection_name': self.collection_name, + }) + + def entity(self, uuid): + pass + + def entity_from_data(self, data): + return UsergridEntity(org_id=self.org_id, + app_id=self.app_id, + collection_name=self.collection_name, + data=data, + client=self.client) + + def query(self, ql='select *', limit=100): + url = collection_query_url_template.format(app_id=self.app_id, + ql=ql, + limit=limit, + collection=self.collection_name, + **self.client.url_data) + + return UsergridQuery(url, headers=self.client.headers) + + def entities(self, **kwargs): + return self.query(**kwargs) + + def post(self, entity, **kwargs): + url = collection_url_template.format(collection=self.collection_name, + app_id=self.app_id, + **self.client.url_data) + + r = self.client.post(url, data=entity, **kwargs) + + if r.status_code == 200: + api_response = r.json() + entity = api_response.get('entities')[0] + e = UsergridEntity(org_id=self.org_id, + app_id=self.app_id, + collection_name=self.collection_name, + data=entity, + client=self.client) + return e + + else: + raise UsergridError(message='Unable to post to collection name=[%s]' % self.collection_name, + status_code=r.status_code, + data=entity, + api_response=r, + url=url) + http://git-wip-us.apache.org/repos/asf/usergrid-python/blob/f700740a/usergrid/UsergridConnection.py ---------------------------------------------------------------------- diff --git a/usergrid/UsergridConnection.py b/usergrid/UsergridConnection.py new file mode 100644 index 0000000..c008b91 --- /dev/null +++ b/usergrid/UsergridConnection.py @@ -0,0 +1,30 @@ +# */ +# * Licensed to the Apache Software Foundation (ASF) under one +# * or more contributor license agreements. See the NOTICE file +# * distributed with this work for additional information +# * regarding copyright ownership. The ASF licenses this file +# * to you under the Apache License, Version 2.0 (the +# * "License"); you may not use this file except in compliance +# * with the License. You may obtain a copy of the License at +# * +# * http://www.apache.org/licenses/LICENSE-2.0 +# * +# * Unless required by applicable law or agreed to in writing, +# * software distributed under the License is distributed on an +# * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# * KIND, either express or implied. See the License for the +# * specific language governing permissions and limitations +# * under the License. +# */ + +import logging + +__author__ = '[email protected]' + + +class UsergridConnection(object): + def __init__(self, source_entity, verb, target_entity): + self.source_entity = source_entity + self.verb = verb + self.target_entity = target_entity + self.logger = logging.getLogger('usergrid.UsergridConnection') http://git-wip-us.apache.org/repos/asf/usergrid-python/blob/f700740a/usergrid/UsergridError.py ---------------------------------------------------------------------- diff --git a/usergrid/UsergridError.py b/usergrid/UsergridError.py new file mode 100644 index 0000000..3b2a4e0 --- /dev/null +++ b/usergrid/UsergridError.py @@ -0,0 +1,21 @@ +# */ +# * Licensed to the Apache Software Foundation (ASF) under one +# * or more contributor license agreements. See the NOTICE file +# * distributed with this work for additional information +# * regarding copyright ownership. The ASF licenses this file +# * to you under the Apache License, Version 2.0 (the +# * "License"); you may not use this file except in compliance +# * with the License. You may obtain a copy of the License at +# * +# * http://www.apache.org/licenses/LICENSE-2.0 +# * +# * Unless required by applicable law or agreed to in writing, +# * software distributed under the License is distributed on an +# * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# * KIND, either express or implied. See the License for the +# * specific language governing permissions and limitations +# * under the License. +# */ + +__author__ = '[email protected]' + http://git-wip-us.apache.org/repos/asf/usergrid-python/blob/f700740a/usergrid/UsergridOrganization.py ---------------------------------------------------------------------- diff --git a/usergrid/UsergridOrganization.py b/usergrid/UsergridOrganization.py new file mode 100644 index 0000000..14ad7a5 --- /dev/null +++ b/usergrid/UsergridOrganization.py @@ -0,0 +1,35 @@ +# */ +# * Licensed to the Apache Software Foundation (ASF) under one +# * or more contributor license agreements. See the NOTICE file +# * distributed with this work for additional information +# * regarding copyright ownership. The ASF licenses this file +# * to you under the Apache License, Version 2.0 (the +# * "License"); you may not use this file except in compliance +# * with the License. You may obtain a copy of the License at +# * +# * http://www.apache.org/licenses/LICENSE-2.0 +# * +# * Unless required by applicable law or agreed to in writing, +# * software distributed under the License is distributed on an +# * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# * KIND, either express or implied. See the License for the +# * specific language governing permissions and limitations +# * under the License. +# */ + +from usergrid import UsergridApplication + +__author__ = '[email protected]' + + +class UsergridOrganization(object): + def __init__(self, org_id, client): + self.org_id = org_id + self.client = client + + def application(self, app_id): + return UsergridApplication(app_id, client=self.client) + + def app(self, app_id): + return self.application(app_id) + http://git-wip-us.apache.org/repos/asf/usergrid-python/blob/f700740a/usergrid/UsergridQueryIterator.py ---------------------------------------------------------------------- diff --git a/usergrid/UsergridQueryIterator.py b/usergrid/UsergridQueryIterator.py new file mode 100755 index 0000000..e487fb3 --- /dev/null +++ b/usergrid/UsergridQueryIterator.py @@ -0,0 +1,157 @@ +# */ +# * Licensed to the Apache Software Foundation (ASF) under one +# * or more contributor license agreements. See the NOTICE file +# * distributed with this work for additional information +# * regarding copyright ownership. The ASF licenses this file +# * to you under the Apache License, Version 2.0 (the +# * "License"); you may not use this file except in compliance +# * with the License. You may obtain a copy of the License at +# * +# * http://www.apache.org/licenses/LICENSE-2.0 +# * +# * Unless required by applicable law or agreed to in writing, +# * software distributed under the License is distributed on an +# * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# * KIND, either express or implied. See the License for the +# * specific language governing permissions and limitations +# * under the License. +# */ + +import json +import logging +import traceback +import requests +import time + +__author__ = '[email protected]' + + +class UsergridQueryIterator(object): + def __init__(self, + url, + operation='GET', + sleep_time=10, + page_delay=0, + headers=None, + data=None): + + if not data: + data = {} + if not headers: + headers = {} + + self.page_counter = 0 + self.total_retrieved = 0 + self.logger = logging.getLogger('usergrid.UsergridQuery') + self.data = data + self.headers = headers + self.url = url + self.operation = operation + self.next_cursor = None + self.entities = [] + self.count_retrieved = 0 + self._pos = 0 + self.last_response = None + self.page_delay = page_delay + self.sleep_time = sleep_time + self.session = None + + def _get_next_response(self, attempts=0): + + if self.session is None: + self.session = requests.Session() + + try: + if self.operation == 'PUT': + op = self.session.put + elif self.operation == 'DELETE': + op = self.session.delete + else: + op = self.session.get + + target_url = self.url + + if self.next_cursor is not None: + delim = '&' if '?' in target_url else '?' + target_url = '%s%scursor=%s' % (self.url, delim, self.next_cursor) + + self.logger.debug('Operation=[%s] URL=[%s]' % (self.operation, target_url)) + + r = op(target_url, data=json.dumps(self.data), headers=self.headers) + + if r.status_code == 200: + r_json = r.json() + count_retrieved = len(r_json.get('entities', [])) + self.total_retrieved += count_retrieved + self.logger.debug('Retrieved [%s] entities in [%s]th page in [%s], total from [%s] is [%s]' % ( + count_retrieved, self.page_counter, r.elapsed, self.url, self.total_retrieved)) + + return r_json + + elif r.status_code in [401, 404] and 'service_resource_not_found' in r.text: + self.logger.error('Query Not Found [%s] on URL=[%s]: %s' % (r.status_code, target_url, r.text)) + raise SystemError('Query Not Found [%s] on URL=[%s]: %s' % (r.status_code, target_url, r.text)) + + else: + if attempts < 10: + self.logger.error('Sleeping %s after HTTP [%s] for retry attempt=[%s] on URL=[%s], response: %s' % ( + self.sleep_time, r.status_code, attempts, target_url, r.text)) + + time.sleep(self.sleep_time) + + return self._get_next_response(attempts=attempts + 1) + + else: + raise SystemError('Unable to get next response after %s attempts' % attempts) + + except: + print traceback.format_exc() + + def next(self): + + if self.last_response is None: + self.logger.debug('getting first page, url=[%s]' % self.url) + + self._process_next_page() + + elif self._pos >= len(self.entities) > 0 and self.next_cursor is not None: + + self.logger.debug('getting next page, count=[%s] url=[%s], cursor=[%s]' % ( + self.count_retrieved, self.url, self.next_cursor)) + + self._process_next_page() + self.logger.debug('Sleeping [%s]s between pages' % self.page_delay) + + time.sleep(self.page_delay) + + if self._pos < len(self.entities): + response = self.entities[self._pos] + self._pos += 1 + return response + + raise StopIteration + + def __iter__(self): + return self + + def _process_next_page(self, attempts=0): + + api_response = self._get_next_response() + + if api_response is None: + message = 'Unable to retrieve query results from url=[%s]' % self.url + self.logger.error(message) + api_response = {} + raise StopIteration + + self.last_response = api_response + + self.entities = api_response.get('entities', []) + self.next_cursor = api_response.get('cursor', None) + self._pos = 0 + self.count_retrieved += len(self.entities) + self.page_counter += 1 + + if self.next_cursor is None: + self.logger.debug('no cursor in response. Total pages=[%s], entities=[%s] url=[%s]' % ( + self.page_counter, self.count_retrieved, self.url)) http://git-wip-us.apache.org/repos/asf/usergrid-python/blob/f700740a/usergrid/__init__.py ---------------------------------------------------------------------- diff --git a/usergrid/__init__.py b/usergrid/__init__.py new file mode 100644 index 0000000..93f8273 --- /dev/null +++ b/usergrid/__init__.py @@ -0,0 +1,37 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. The ASF licenses this file to You +# under the Apache License, Version 2.0 (the "License"); you may not +# use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. For additional information regarding +# copyright in this work, please see the NOTICE file in the top level +# directory of this distribution. + +__all__ = [ + 'UsergridApplication', + 'UsergridClient', + 'UsergridConnection', + 'UsergridConnectionProfile', + 'UsergridEntity', + 'Usergrid', + 'UsergridError', + 'UsergridOrganization', + 'UsergridAuth', + 'UsergridQueryIterator', + 'UsergridResponse' +] + +from .UsergridApplication import UsergridApplication +from .UsergridClient import UsergridClient, Usergrid, UsergridResponse +from .UsergridConnection import UsergridConnection +from .UsergridOrganization import UsergridOrganization +from .UsergridQueryIterator import UsergridQueryIterator +from .UsergridAuth import UsergridAuth http://git-wip-us.apache.org/repos/asf/usergrid-python/blob/f700740a/usergrid/app_templates.py ---------------------------------------------------------------------- diff --git a/usergrid/app_templates.py b/usergrid/app_templates.py new file mode 100644 index 0000000..3598587 --- /dev/null +++ b/usergrid/app_templates.py @@ -0,0 +1,38 @@ +# */ +# * Licensed to the Apache Software Foundation (ASF) under one +# * or more contributor license agreements. See the NOTICE file +# * distributed with this work for additional information +# * regarding copyright ownership. The ASF licenses this file +# * to you under the Apache License, Version 2.0 (the +# * "License"); you may not use this file except in compliance +# * with the License. You may obtain a copy of the License at +# * +# * http://www.apache.org/licenses/LICENSE-2.0 +# * +# * Unless required by applicable law or agreed to in writing, +# * software distributed under the License is distributed on an +# * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# * KIND, either express or implied. See the License for the +# * specific language governing permissions and limitations +# * under the License. +# */ + +__author__ = '[email protected]' + +org_url_template = "{base_url}/{org_id}" +app_url_template = "%s/{app_id}" % org_url_template + +app_token_url_template = "%s/token" % app_url_template + +collection_url_template = "%s/{collection}" % app_url_template +collection_query_url_template = "%s?ql={ql}&limit={limit}" % collection_url_template + +post_collection_url_template = collection_url_template +entity_url_template = "%s/{uuid_name}" % collection_url_template +get_entity_url_template = "%s?connections={connections}" % entity_url_template +put_entity_url_template = entity_url_template +delete_entity_url_template = entity_url_template + +assign_role_url_template = '%s/roles/{role_uuid_name}/{entity_type}/{entity_uuid_name}' % app_url_template + +connect_entities_by_type_template = '%s/{from_collection}/{from_uuid_name}/{relationship}/{to_collection}/{to_uuid_name}' % app_url_template \ No newline at end of file http://git-wip-us.apache.org/repos/asf/usergrid-python/blob/f700740a/usergrid/management_templates.py ---------------------------------------------------------------------- diff --git a/usergrid/management_templates.py b/usergrid/management_templates.py new file mode 100644 index 0000000..c231b49 --- /dev/null +++ b/usergrid/management_templates.py @@ -0,0 +1,27 @@ +# */ +# * Licensed to the Apache Software Foundation (ASF) under one +# * or more contributor license agreements. See the NOTICE file +# * distributed with this work for additional information +# * regarding copyright ownership. The ASF licenses this file +# * to you under the Apache License, Version 2.0 (the +# * "License"); you may not use this file except in compliance +# * with the License. You may obtain a copy of the License at +# * +# * http://www.apache.org/licenses/LICENSE-2.0 +# * +# * Unless required by applicable law or agreed to in writing, +# * software distributed under the License is distributed on an +# * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# * KIND, either express or implied. See the License for the +# * specific language governing permissions and limitations +# * under the License. +# */ + +__author__ = '[email protected]' + +management_base_url = '{base_url}/management' +management_org_url_template = "%s/organizations/{org_id}" % management_base_url +management_org_list_apps_url_template = "%s/applications" % management_org_url_template +management_app_url_template = "%s/applications/{app_id}" % management_org_url_template + +org_token_url_template = "%s/token" % management_base_url
