Skip to content

Commit

Permalink
Merge branch 'release/0.0.1'
Browse files Browse the repository at this point in the history
  • Loading branch information
genaromadrid committed Jun 13, 2016
2 parents c1fbd60 + bc30ec3 commit 9b65014
Show file tree
Hide file tree
Showing 23 changed files with 499 additions and 3 deletions.
4 changes: 4 additions & 0 deletions .coveragerc
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
[report]
omit =
*/python?.?/*
*/site-packages/nose/*
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
*.pyc
/dist/
/*.egg-info
*.sublime-workspace
env
.Python
.env
.coverage
htmlcov
14 changes: 14 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
language: python

python:
- 3.5

install:
- pip install -r requirements.txt
- pip install coveralls

script:
coverage run setup.py test

after_success:
coveralls
66 changes: 65 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,15 +1,79 @@
# Mifiel Python Library

## Install it
[![Coverage Status][coveralls-image]][coveralls-url]
[![Build Status][travis-image]][travis-url]

Pyton library for [Mifiel](https://www.mifiel.com) API.
Please read our [documentation](https://www.mifiel.com/api-docs/) for instructions on how to start using the API.

## Installation

```bash
pip install mifiel
```

## Usage

To start using the API you will need an APP_ID and a APP_SECRET which will be provided upon request (contact us at hola@mifiel.com).

You will first need to create an account in [mifiel.com](https://www.mifiel.com) since the APP_ID and APP_SECRET will be linked to your account.

### Document methods:

For now, the only methods available are **find** and **create**. Contributions are greatly appreciated.

- Find:

```python
from mifiel import Document, Client
client = Client(app_id='APP_ID', secret_key='APP_SECRET')

doc = Document.find(client, 'id')
document.original_hash
document.file
document.file_signed
# ...
```

- Create:

```python
from mifiel import Document, Client
client = Client(app_id='APP_ID', secret_key='APP_SECRET')

signatories = [
{ name: 'Signer 1', email: 'signer1@email.com', tax_id: 'AAA010101AAA' },
{ name: 'Signer 2', email: 'signer2@email.com', tax_id: 'AAA010102AAA' }
]
# Providde the SHA256 hash of the file you want to sign.
doc = Document.create(client, signatories, dhash='some-sha256-hash')
# Or just send the file and we'll take care of everything.
# We will store the file for you.
doc = Document.create(client, signatories, file='path/to/my/file.pdf')
```

## Development

### Install dependencies

```bash
pip install -r requirements.txt
```

## Test

Just clone the repo, install dependencies as you would in development and run `nose2` or `python setup.py test`

## Contributing

1. Fork it ( https://github.com/Mifiel/python-api-client/fork )
2. Create your feature branch (`git checkout -b my-new-feature`)
3. Commit your changes (`git commit -am 'Add some feature'`)
4. Push to the branch (`git push origin my-new-feature`)
5. Create a new Pull Request

[coveralls-image]: https://coveralls.io/repos/github/Mifiel/python-api-client/badge.svg?branch=master
[coveralls-url]: https://coveralls.io/github/Mifiel/python-api-client?branch=master

[travis-image]: https://travis-ci.org/Mifiel/python-api-client.svg?branch=master
[travis-url]: https://travis-ci.org/Mifiel/python-api-client
7 changes: 6 additions & 1 deletion mifiel-python-api-client.sublime-project
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,12 @@
"*.sublime-workspace"
],
"folder_exclude_patterns": [
"env"
"env",
"build",
"dist",
"*.egg-info",
"__pycache__",
"htmlcov"
],
"path": ".",
"follow_symlinks": true,
Expand Down
6 changes: 6 additions & 0 deletions mifiel/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
from .api_auth import ApiAuth

from .client import Client
from .response import Response
from .base import Base
from .document import Document
55 changes: 55 additions & 0 deletions mifiel/api_auth.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
"""
[ApiAuth](https://github.com/mgomes/api_auth) for python
Based on https://github.com/pd/httpie-api-auth by Kyle Hargraves
Usage:
import requests
requests.get(url, auth=ApiAuth(app_id, secret_key))
"""
import hmac, base64, hashlib, datetime
from requests.auth import AuthBase
from urllib.parse import urlparse

class ApiAuth(AuthBase):
def __init__(self, access_id, secret_key):
self.access_id = access_id
self.secret_key = secret_key.encode('ascii')

def __call__(self, request):
method = request.method.upper()

content_type = request.headers.get('content-type')
if not content_type:
content_type = ''

content_md5 = request.headers.get('content-md5')
if not content_md5:
m = hashlib.md5()
body = request.body
if not body: body = ''
m.update(body.encode('ascii'))
content_md5 = base64.b64encode(m.digest()).decode()
request.headers['content-md5'] = content_md5

httpdate = request.headers.get('date')
if not httpdate:
now = datetime.datetime.utcnow()
httpdate = now.strftime('%a, %d %b %Y %H:%M:%S GMT')
request.headers['Date'] = httpdate

url = urlparse(request.url)
path = url.path
if url.query:
path = path + '?' + url.query

canonical_string = '%s,%s,%s,%s,%s' % (method, content_type, content_md5, path, httpdate)

digest = hmac.new(
self.secret_key,
canonical_string.encode('ascii'),
hashlib.sha1
).digest()
signature = base64.encodebytes(digest).rstrip().decode()

request.headers['Authorization'] = 'APIAuth %s:%s' % (self.access_id, signature)
return request
49 changes: 49 additions & 0 deletions mifiel/base.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
from mifiel import Response
import requests

class Base(object):
def __init__(self, mifiel, path):
object.__setattr__(self, 'sandbox', False)
object.__setattr__(self, 'path', path)
object.__setattr__(self, 'mifiel', mifiel)
object.__setattr__(self, 'response', Response())
# initialize id
self.id = None

def save(self):
if self.id is None: return False
self.process_request('put', url=self.url(self.id), data=self.get_data())

def url(self, path=None):
p = self.path
if path:
p = '{}/{}'.format(p, path)

return self.mifiel.url().format(path=p)

def process_request(self, method, url=None, data=None):
if not url:
url = self.url()

if method == 'post':
response = requests.post(url, auth=self.mifiel.auth, json=data)
elif method == 'put':
response = requests.put(url, auth=self.mifiel.auth, json=data)
elif method == 'get':
response = requests.get(url, auth=self.mifiel.auth, json=data)
elif method == 'delete':
response = requests.delete(url, auth=self.mifiel.auth, json=data)

self.set_data(response)

def set_data(self, response):
self.response.set_response(response)

def get_data(self):
return self.response.get_response()

def __setattr__(self, name, value):
self.response.set(name, value)

def __getattr__(self, name):
return self.response.get(name)
6 changes: 6 additions & 0 deletions mifiel/certificate.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
from mifiel import Base
import requests

class Document(Base):
def __init__(self, client):
Base.__init__(self, client, 'keys')
17 changes: 17 additions & 0 deletions mifiel/client.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
from mifiel import ApiAuth

class Client:
def __init__(self, app_id, secret_key):
self.sandbox = False
self.auth = ApiAuth(app_id, secret_key)
self.base_url = 'https://www.mifiel.com'

def use_sandbox(self):
self.sandbox = True
self.base_url = 'https://sandbox.mifiel.com'

def set_base_url(self, base_url):
self.base_url = base_url

def url(self):
return self.base_url + '/api/v1/{path}'
30 changes: 30 additions & 0 deletions mifiel/document.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
from mifiel import Base

class Document(Base):
def __init__(self, client):
Base.__init__(self, client, 'documents')

@staticmethod
def find(client, doc_id):
doc = Document(client)
doc.process_request('get', url=doc.url(doc_id))
return doc

@staticmethod
def create(client, signatories, file=None, dhash=None, callback_url=None):
if not file and not dhash:
raise ValueError('Either file or hash must be provided')
if file and dhash:
raise ValueError('Only one of file or hash must be provided')

data = { 'signatories': signatories }
if callback_url:
data['callback_url'] = callback_url
if file:
data['file'] = open(file)
if dhash:
data['original_hash'] = dhash

doc = Document(client)
doc.process_request('post', data=data)
return doc
22 changes: 22 additions & 0 deletions mifiel/response.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
class Response(object):
def __init__(self):
object.__setattr__(self, 'datastore', {})

def set_response(self, response):
response.raise_for_status()
object.__setattr__(self, 'datastore', response.json())

def get_response(self):
return self.datastore

def __setattr__(self, name, value):
self.set(name, value)

def __getattr__(self, name):
return self.get(name)

def set(self, name, value):
self.datastore[name] = value

def get(self, name):
return self.datastore[name]
10 changes: 10 additions & 0 deletions nose2.cfg
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
[unittest]
code-directories = mifiel
start-dir = test

[coverage]
coverage = mifiel
always-on = True
coverage-report =
term-missing
html
8 changes: 7 additions & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
@@ -1 +1,7 @@

cookies==2.2.1
cov-core==1.15.0
coverage==4.1
nose2==0.6.4
requests==2.10.0
responses==0.5.1
six==1.10.0
21 changes: 21 additions & 0 deletions setup.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
from setuptools import setup

def readme():
with open('README.md') as f:
return f.read()

setup(name='mifiel',
version='0.0.1',
description='The funniest joke in the world',
long_description=readme(),
url='http://github.com/mifiel/python-api-client',
author='Genaro Madrid',
author_email='genmadrid@gmail.com',
license='MIT',
test_suite='nose2.collector.collector',
packages=['mifiel'],
install_requires=[
'requests'
],
include_package_data=True,
zip_safe=False)
Empty file added test/api_auth/__init__.py
Empty file.

0 comments on commit 9b65014

Please sign in to comment.