Skip to content
This repository has been archived by the owner on Feb 9, 2024. It is now read-only.

Commit

Permalink
Added the endpoints, and test coverage for all functionality but the …
Browse files Browse the repository at this point in the history
…third-party API integration.
  • Loading branch information
bartfeenstra committed Nov 18, 2017
1 parent f03bccc commit 02278fa
Show file tree
Hide file tree
Showing 5 changed files with 165 additions and 39 deletions.
14 changes: 13 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,17 @@

[![Build Status](https://travis-ci.org/bartfeenstra/tk.svg?branch=master)](https://travis-ci.org/bartfeenstra/tk) [![Coverage Status](https://coveralls.io/repos/github/bartfeenstra/tk/badge.svg?branch=master)](https://coveralls.io/github/bartfeenstra/tk?branch=master)

## Usage
Substitute `http://127.0.0.1:5000` for the actual application URL, if
you are not using `./bin/run-dev`.

### Submitting a document
`curl -X POST --header "Content-Type: application/octet-stream" --header "Accept: text/plain" http://127.0.0.1:5000/submit`

### Retrieving a document
`curl -X GET --header "Accept: text/xml" http://127.0.0.1:5000/retrieve/{uuid}`
where `{uuid}` is the process UUID returned by `POST /submit`.

## Development

### Building the code
Expand All @@ -14,7 +25,8 @@ Run `./bin/test`.
Run `./bin/fix` to fix what can be fixed automatically.

### Running the application
Run `./bin/run-dev` to start a development web server.
Run `./bin/run-dev` to start a development web server at
[http://127.0.0.1:5000](http://127.0.0.1:5000).

### Code style
All code follows [PEP 8](https://www.python.org/dev/peps/pep-0008/).
57 changes: 50 additions & 7 deletions tk/flask/app.py
Original file line number Diff line number Diff line change
@@ -1,16 +1,59 @@
from flask import Flask
from flask import Flask, request, Response
from werkzeug.exceptions import NotAcceptable, UnsupportedMediaType, NotFound

from tk.process import Process


def request_content_type(content_type):
"""
Check we can accept the request body.
:param content_type:
:return:
"""
def decorator(route_method):
def checker(*route_method_args, **route_method_kwargs):
if request.mimetype != content_type:
raise UnsupportedMediaType()
return route_method(*route_method_args, **route_method_kwargs)
return checker
return decorator


def response_content_type(content_type):
"""
Check we can deliver the right content type.
:return:
"""
def decorator(route_method):
def checker(*route_method_args, **route_method_kwargs):
negotiated_content_type = request.accept_mimetypes.best_match(
[content_type])
if negotiated_content_type is None:
raise NotAcceptable()
return route_method(*route_method_args, **route_method_kwargs)
return checker
return decorator


class App(Flask):
def __init__(self, *args, **kwargs):
super().__init__('tk', *args, **kwargs)
super().__init__('tk', static_folder=None, *args, **kwargs)
self._register_routes()
self.process = Process()

def _register_routes(self):
@self.route('/submit', methods=['POST'])
@self.route('/submit', methods=['POST'], endpoint='submit')
@request_content_type('application/octet-stream')
@response_content_type('text/plain')
def submit():
return 'SUBMITTED'
process_id = self.process.submit()
return Response(process_id, 200, mimetype='text/plain')

@self.route('/retrieve/<id>')
def retrieve(id):
return id
@self.route('/retrieve/<process_id>', endpoint='retrieve')
@request_content_type('')
@response_content_type('text/xml')
def retrieve(process_id):
result = self.process.retrieve(process_id)
if result is None:
raise NotFound()
return Response(result, 200, mimetype='text/plain')
16 changes: 16 additions & 0 deletions tk/process.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import uuid


class Process:
def __init__(self):
self._processes = {}

def submit(self):
process_id = str(uuid.uuid4())
self._processes[process_id] = 'PROGRESS'
return process_id

def retrieve(self, process_id):
if process_id not in self._processes:
return None
return self._processes[process_id]
86 changes: 86 additions & 0 deletions tk/tests/test_endpoints.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
from tk.tests import IntegrationTestCase, data_provider


def provide_disallowed_submit_methods():
return {
'GET': ('GET',),
'PUT': ('PUT',),
'PATCH': ('PATCH',),
'DELETE': ('DELETE',),
}


def provide_disallowed_retrieve_methods():
return {
'POST': ('POST',),
'PUT': ('PUT',),
'PATCH': ('PATCH',),
'DELETE': ('DELETE',),
}


class SubmitTest(IntegrationTestCase):
@data_provider(provide_disallowed_submit_methods)
def testSubmitWithDisallowedMethodShould405(self, method):
response = self._flask_app_client.open('/submit', method=method)
self.assertEquals(405, response.status_code)

def testSubmitWithUnsupportedMediaTypeShould415(self):
response = self._flask_app_client.post('/submit')
self.assertEquals(415, response.status_code)

def testSubmitWithNotAcceptableShould406(self):
response = self._flask_app_client.post('/submit', headers={
'Content-Type': 'application/octet-stream'
})
self.assertEquals(406, response.status_code)

def testSubmit(self):
response = self._flask_app_client.post('/submit', headers={
'Accept': 'text/plain',
'Content-Type': 'application/octet-stream'
})
self.assertEquals(200, response.status_code)
# Assert the response contains a plain-text process UUID.
self.assertRegex(response.get_data(
as_text=True), '[a-f0-9]{8}-?[a-f0-9]{4}-?4[a-f0-9]{3}-?[89ab][a-f0-9]{3}-?[a-f0-9]{12}')


class RetrieveTest(IntegrationTestCase):
@data_provider(provide_disallowed_retrieve_methods)
def testRetrieveWithDisallowedMethodShould405(self, method):
response = self._flask_app_client.open('/retrieve/foo', method=method)
self.assertEquals(response.status_code, 405)

def testRetrieveWithUnsupportedMediaTypeShould415(self):
response = self._flask_app_client.get('/retrieve/foo', headers={
'Content-Type': 'text/plain',
})
self.assertEquals(response.status_code, 415)

def testRetrieveWithNotAcceptableShould406(self):
response = self._flask_app_client.get('/retrieve/foo')
self.assertEquals(response.status_code, 406)

def testRetrieveWithUnknownProcessIdShould404(self):
response = self._flask_app_client.get('/retrieve/foo', headers={
'Accept': 'text/xml',
})
self.assertEquals(response.status_code, 404)

def testRetrieveWithUnprocessedDocument(self):
process_id = self._flask_app.process.submit()
response = self._flask_app_client.get('/retrieve/%s' % process_id, headers={
'Accept': 'text/xml',
})
self.assertEquals(response.status_code, 200)
self.assertEquals(response.get_data(as_text=True), 'PROGRESS')

def testRetrieveWithProcessedDocument(self):
process_id = self._flask_app.process.submit()
response = self._flask_app_client.get('/retrieve/%s' % process_id, headers={
'Accept': 'text/xml',
})
self.assertEquals(response.status_code, 200)
# @todo Confirm output.
self.skipTest()
31 changes: 0 additions & 31 deletions tk/tests/test_submit.py

This file was deleted.

0 comments on commit 02278fa

Please sign in to comment.