Permalink
Browse files

license added

directory layout changed
 * example app moved
 * lib renamed to marketplace
 * tests moved inside marketplace

classes modified
 * Marketplace is split into Client and Connection classes

setup tools added to setup.py to provide requirements
  • Loading branch information...
1 parent b6f0bbc commit 3e6d3ce8a4034141b09cc83de0d547ef776b15e5 @zalun committed Oct 5, 2012
View
27 LICENSE
@@ -1,27 +0,0 @@
-Copyright (c) 2012, Mozilla Corporation
-All rights reserved.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions are met:
-
-* Redistributions of source code must retain the above copyright notice,
-this list of conditions and the following disclaimer.
-
-* Redistributions in binary form must reproduce the above copyright notice,
-this list of conditions and the following disclaimer in the documentation
-and/or other materials provided with the distribution.
-
-* Neither the name of the Mozilla Corporation nor the names of its contributors
-may be used to endorse or promote products derived from this software without
-specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
-ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
-WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
-FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
-SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
-CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
-OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
View

Large diffs are not rendered by default.

Oops, something went wrong.
View
@@ -1,45 +1,16 @@
-Examples of the Marketplace clients
-===================================
+Marketplace Client Library
+==========================
-Python
-######
-**Marketplace command line client**
+Mozilla is building a Marketplace to bring personalized discovery,
+worldwide distribution, and easy payments to the largest platform
+for app development: the Web.
-Install requirements::
+This library helps to create Python based sites or apps to communicate
+with Marketplace.
- cd python
- pip install -r requirements.txt
+You may find and collaborate your time and experience at
+https://github.com/mozilla/Marketplace.Python
Test::
nosetests
-
-Usage
------
-
-* Set CONSUMER_KEY and CONSUMER_SECRET environment variables::
-
- export CONSUMER_KEY=yourconsumerkey
- export CONSUMER_SECRET=yourconsumersecret
-
-* Validate manifest. Will return ``manifest_id`` which is needed for the next steps::
-
- python main.py validate_manifest http://mozilla.github.com/MarketplaceClientExample/manifest.webapp
-
-* Check if the manifest is valid::
-
- python main.py is_manifest_valid your_manifest_id
-
-* Add app to marketplace, app_id will be returned::
-
- python main.py create your_manifest_id
-
-* Display status of the app::
-
- python main.py status your_app_id
-
-* Add screenshot (currently only JPEG) to app, some data including id will be returned::
-
- python main.py add_screenshot your_app_id ~/data/some.jpg
-
-
View
@@ -0,0 +1,36 @@
+Command Line Marketplace Client
+===============================
+
+Install requirements::
+
+ pip install -r requirements.txt
@andymckay

andymckay Oct 5, 2012

Is this still relevant?

@zalun

zalun Oct 6, 2012

Owner

IMHO yes - it contains one line - marketplace, but it's a good practice to keep requirements in the file

+
+Usage
+-----
+
+* Set CONSUMER_KEY and CONSUMER_SECRET environment variables::
+
+ export CONSUMER_KEY=yourconsumerkey
+ export CONSUMER_SECRET=yourconsumersecret
+
+* Validate manifest. Will return ``manifest_id`` which is needed for the next steps::
+
+ python main.py validate_manifest http://mozilla.github.com/MarketplaceClientExample/manifest.webapp
+
+* Check if the manifest is valid::
+
+ python main.py is_manifest_valid your_manifest_id
+
+* Add app to marketplace, app_id will be returned::
+
+ python main.py create your_manifest_id
+
+* Display status of the app::
+
+ python main.py status your_app_id
+
+* Add screenshot (currently only JPEG) to app, some data including id will be returned::
+
+ python main.py add_screenshot your_app_id ~/data/some.jpg
+
+
File renamed without changes.
@@ -7,8 +7,6 @@
import config
-from lib.marketplace import Marketplace
-
def validate_manifest(auth, manifest_url):
response = auth.validate_manifest(manifest_url)
File renamed without changes.
@@ -1,10 +1,10 @@
import argparse
import sys
-import app.commands
-import app.config as config
+import commands
+import config as config
-from lib.marketplace import Marketplace
+import marketplace
commands = {'validate_manifest': app.commands.validate_manifest,
'is_manifest_valid': app.commands.is_manifest_valid,
@@ -23,17 +23,17 @@
help='command arguments')
args = parser.parse_args()
-auth = Marketplace(
+client = marketplace.Client(
domain=config.MARKETPLACE_DOMAIN,
protocol=config.MARKETPLACE_PROTOCOL,
port=config.MARKETPLACE_PORT,
consumer_key=config.CONSUMER_KEY,
consumer_secret=config.CONSUMER_SECRET)
if args.attrs:
- result = commands[args.method](auth, *args.attrs)
+ result = commands[args.method](client, *args.attrs)
else:
- result = commands[args.method](auth)
+ result = commands[args.method](client)
if result['success']:
sys.stdout.write('%s\n' % result['message'])
View
@@ -0,0 +1 @@
+marketplace
View
@@ -0,0 +1,19 @@
+"""Marketplace Client Library
+
+https://wiki.mozilla.org/Marketplace
+
+Mozilla is building a Marketplace to bring personalized discovery,
+worldwide distribution, and easy payments to the largest platform
+for app development: the Web.
+
+This library helps to create Python based sites or apps to communicate
+with Marketplace.
+"""
+
+__version__ = '0.1'
+__all__ = [
+ 'Client',
+]
+__author__ = 'Piotr Zalewa <zalun@mozilla.com>'
+
+from .client import Client
@@ -1,14 +1,11 @@
"""
A class to interact with Marketplace's api, using OAuth.
-Ripped off from Andy's Flightdeck.utils.amo.py
For full spec please read Marketplace API documentation
https://github.com/mozilla/zamboni/blob/master/docs/topics/api.rst
"""
import json
import logging
-import time
-import urllib
import mimetypes
from base64 import b64encode
@@ -19,6 +16,8 @@
from urlparse import urlparse, urlunparse, parse_qsl
+from .connection import Connection
+
log = logging.getLogger('marketplace.%s' % __name__)
MARKETPLACE_PORT = 443
@@ -34,21 +33,11 @@
'categories': '/apps/category/',
}
-def _get_args(consumer):
- """Provide a dict with oauth data
- """
- return dict(
- oauth_consumer_key=consumer.key,
- oauth_nonce=oauth.generate_nonce(),
- oauth_signature_method='HMAC-SHA1',
- oauth_timestamp=int(time.time()),
- oauth_version='1.0')
-
-class Marketplace:
+
+class Client:
"""A base class to authenticate and work with Marketplace OAuth.
"""
signature_method = oauth.SignatureMethod_HMAC_SHA1()
- should_save_storage = False
def __init__(self, domain=MARKETPLACE_DOMAIN,
protocol=MARKETPLACE_PROTOCOL,
@@ -60,9 +49,13 @@ def __init__(self, domain=MARKETPLACE_DOMAIN,
self.port = port
self.prefix = prefix
self.three_legged = three_legged
- self.consumer = None
+ self.conn = None
if consumer_secret and consumer_key:
- self.set_consumer(consumer_key, consumer_secret)
+ self.conn = self.get_connection(consumer_key, consumer_secret)
+
+ @staticmethod
+ def get_connection(consumer_key, consumer_secret):
+ return Connection(consumer_key, consumer_secret)
def url(self, key):
"""Creates a full URL to the API using urls dict
@@ -71,66 +64,15 @@ def url(self, key):
'%s/api%s' % (self.prefix, urls[key]),
'', '', ''))
- def set_consumer(self, consumer_key, consumer_secret):
- """Sets the consumer attribute
- """
- self.consumer = self.get_consumer(consumer_key, consumer_secret)
-
- def get_consumer(self, consumer_key, consumer_secret):
- """Get the :class:`oauth.Consumer` instance with prvided key and secret
- """
- return oauth.Consumer(consumer_key, consumer_secret)
-
- def prepare_request(self, method, url, body='', consumer=None):
- """Adds consumer and signs the request
-
- :returns: headers of the signed request
- """
- if not consumer:
- consumer = self.consumer
- req = oauth.Request(method=method, url=url,
- parameters=_get_args(consumer))
- req.sign_request(self.signature_method, consumer, None)
-
- headers = req.to_header()
- headers['Content-type'] = 'application/json'
- return headers
-
- def get(self, url, data=None, consumer=None):
- """ Prepare data and send a GET to provided url
- """
- body = urllib.urlencode(data) if data else ''
- headers = self.prepare_request('GET', url, body, consumer)
- return requests.get(url, headers=headers, data=body)
-
- def post(self, url, data, consumer=None):
- """ Prepare data and send a POST to provided url
- """
- body = json.dumps(data)
- headers = self.prepare_request('POST', url, body, consumer)
- return requests.post(url, headers=headers, data=body)
-
- def put(self, url, data, consumer=None):
- """ Prepare data and send a PUT to provided url
- """
- body = json.dumps(data)
- headers = self.prepare_request('PUT', url, body, consumer)
- return requests.put(url, headers=headers, data=body)
-
- def remove(self, url, consumer=None):
- """ Prepare data and send a DELETE to provided url
- """
- headers = self.prepare_request('DELETE', url, '', consumer)
- return requests.delete(url, headers=headers, data='')
-
def validate_manifest(self, manifest_url):
"""Order manifest validation
:returns: dict with an ``id`` to check the result
"""
# there is a bug request to make this synchronous on Marketplace side
# this will return the same as :method:`get_manifest_validation_result`
- return self.post(self.url('validate'), {'manifest': manifest_url})
+ return self.conn.fetch('POST',
+ self.url('validate'), {'manifest': manifest_url})
def get_manifest_validation_result(self, manifest_id):
"""Check if the manifest is processed and if it's valid
@@ -143,7 +85,8 @@ def get_manifest_validation_result(self, manifest_id):
* valid (Boolean) is manifest valid?
* validation - empty string if valid else error dict
"""
- return self.get(self.url('validation_result') % manifest_id)
+ return self.conn.fetch('GET',
+ self.url('validation_result') % manifest_id)
def is_manifest_valid(self, manifest_id):
"""Check validation shortcut
@@ -174,7 +117,7 @@ def create(self, manifest_id):
* resource_uri (string) url in marketplace
* slug (string) unique name in marketplace
"""
- return self.post(self.url('create'),
+ return self.conn.fetch('POST', self.url('create'),
{'manifest': '%s' % manifest_id})
def update(self, app_id, data):
@@ -211,7 +154,7 @@ def update(self, app_id, data):
and data['payment_type']
and 'privacy_policy' in data
and data['privacy_policy'])
- return self.put(self.url('app') % app_id, data)
+ return self.conn.fetch('PUT', self.url('app') % app_id, data)
def status(self, app_id):
"""View details of an app identified by its id
@@ -220,13 +163,13 @@ def status(self, app_id):
* status_code (int) 200 if successful
* content (JSON String) with all available app information
"""
- return self.get(self.url('app') % app_id)
+ return self.conn.fetch('GET', self.url('app') % app_id)
def delete(self, app_id):
"""Delete an app from Marketplace
"""
# XXX: This isn't yet implemented on API
@andymckay

andymckay Oct 5, 2012

Should we put a raise NotImplementedError here?

@zalun

zalun Oct 6, 2012

Owner

Yes, you're right

- return self.remove(self.url('app') % app_id)
+ return self.conn.fetch('DELETE', self.url('app') % app_id)
def create_screenshot(self, app_id, filename, mimetype='image/jpg',
position=1):
@@ -250,7 +193,7 @@ def create_screenshot(self, app_id, filename, mimetype='image/jpg',
data = {'position': position,
'file': {'type': mtype,
'data': s_encoded}}
- return self.post(url, data)
+ return self.conn.fetch('POST', url, data)
def get_screenshot(self, screenshot_id):
"""Get information about screenshot or video
@@ -259,17 +202,17 @@ def get_screenshot(self, screenshot_id):
* status_code (int) 200 is successful
* content (JSON string)
"""
- return self.get(self.url('screenshot') % screenshot_id)
+ return self.conn.get(self.url('screenshot') % screenshot_id)
def del_screenshot(self, screenshot_id):
"""Deletes screenshot
:returns: HttpResponse:
* status_code (int) 204 if successful
"""
- return self.delete(self.url('screenshot') % screenshot_id)
+ return self.conn.delete(self.url('screenshot') % screenshot_id)
def get_categories(self):
"""Get all categories from Marketplae
"""
- return self.get(self.url('categories'))
+ return self.conn.fetch('GET', self.url('categories'))
Oops, something went wrong.

2 comments on commit 3e6d3ce

Looking good zalun, it seemed to install just fine using setup.py here. Yay!

Owner

zalun replied Oct 6, 2012

:D

Please sign in to comment.