From ed40a5d85204e712ec66fbeecc01a38fe4629f5c Mon Sep 17 00:00:00 2001
From: Johannes Valbjorn <johannes.valbjorn@gmail.com>
Date: Fri, 12 Jan 2018 11:21:44 +0100
Subject: [PATCH 1/6] fix casting of path_params

---
 sanicargs/__init__.py | 27 ++++++++++++++-------------
 1 file changed, 14 insertions(+), 13 deletions(-)

diff --git a/sanicargs/__init__.py b/sanicargs/__init__.py
index 20e6520..52a6fd9 100644
--- a/sanicargs/__init__.py
+++ b/sanicargs/__init__.py
@@ -8,10 +8,10 @@
 
 from logging import getLogger
 
-logger = getLogger('sonicargs')
+__logger = getLogger('sonicargs')
 
 
-def parse_datetime(str):
+def __parse_datetime(str):
     # attempt full date time, but tolerate just a date
     try:
         return datetime.datetime.strptime(str, '%Y-%m-%dT%H:%M:%S')
@@ -19,14 +19,14 @@ def parse_datetime(str):
         pass
     return datetime.datetime.strptime(str, '%Y-%m-%d')
 
-def parse_date(str):
+def __parse_date(str):
     return datetime.datetime.strptime(str, '%Y-%m-%d').date()
 
-type_deserializers = {
+__type_deserializers = {
     int: int,
     str: str,
-    datetime.datetime: parse_datetime,
-    datetime.date: parse_date,
+    datetime.datetime: __parse_datetime,
+    datetime.date: __parse_date,
     List[str]: lambda s: s.split(',')
 }
 
@@ -54,12 +54,16 @@ async def inner(request, *old_args, **route_parameters):
         name = None
         try:
             for name, arg_type, default in parameters:
+                raw_value = request.args.get(name, None)
+
                 # provided in route
                 if name in route_parameters or name=="request":
-                    continue
+                    if name=="request":
+                        continue
+                    raw_value = route_parameters[name]
 
                 # no value
-                if name not in request.args:
+                elif name not in request.args:
                     if default != inspect._empty:
                         # TODO clone?
                         kwargs[name] = default
@@ -67,13 +71,10 @@ async def inner(request, *old_args, **route_parameters):
                     else:
                         raise KeyError("Missing required argument %s" % name)
 
-                raw_value = request.args[name][0]
-                parsed_value = type_deserializers[arg_type](raw_value)
+                parsed_value = __type_deserializers[arg_type](raw_value)
                 kwargs[name] = parsed_value
-
-            kwargs.update(route_parameters)
         except Exception as err: 
-            logger.warning({
+            __logger.warning({
                 "message": "Request args not validated",
                 "stacktrace": str(err)
             })

From 1fcf91c93993734941e69f8287cca0bc07fa05cf Mon Sep 17 00:00:00 2001
From: Johannes Valbjorn <johannes.valbjorn@gmail.com>
Date: Fri, 12 Jan 2018 11:22:47 +0100
Subject: [PATCH 2/6] add tests for path-params, multiple params and optional
 params

---
 tests/test_sanicargs.py | 33 ++++++++++++++++++++++++++++++++-
 1 file changed, 32 insertions(+), 1 deletion(-)

diff --git a/tests/test_sanicargs.py b/tests/test_sanicargs.py
index a2814fc..bdb8553 100644
--- a/tests/test_sanicargs.py
+++ b/tests/test_sanicargs.py
@@ -54,6 +54,16 @@ async def test_all(
             'e': e
         })
 
+    @app.route("/optional", methods=['GET'])
+    @parse_query_args
+    async def test_optional(request, test: str = 'helloworld'):
+        return response.json({'test': test})
+
+    @app.route("/with/<path_param>/path_params", methods=['GET'])
+    @parse_query_args
+    async def test_optional(request, path_param: int, test: str, test_2: int=35):
+        return response.json({'path_param': path_param, 'test': test, 'test_2': test_2})
+
     yield app
 
 @pytest.fixture
@@ -65,6 +75,7 @@ def test_cli(loop, app, test_client):
 # Tests #
 #########
 
+
 async def test_parse_int_success(test_cli):
     resp = await test_cli.get('/int?test=10')
     assert resp.status == 200
@@ -132,6 +143,7 @@ async def test_parse_list_also_works_with_singular(test_cli):
       'not a datetime'
     ]}
 
+
 async def test_all_at_once(test_cli):
     resp = await test_cli.get('/all?a=10&b=test&c=2017-10-10T10:10:10&d=2017-10-10&e=a,b,c,d,e')
     assert resp.status == 200
@@ -144,4 +156,23 @@ async def test_all_at_once(test_cli):
       e=[
         'a', 'b', 'c', 'd', 'e'
       ]
-    )
\ No newline at end of file
+    )
+
+
+async def test_optional(test_cli):
+    resp = await test_cli.get('/optional')
+    assert resp.status == 200
+    resp_json = await resp.json()
+    assert resp_json == {'test': 'helloworld'}
+
+
+async def test_mandatory(test_cli):
+    resp = await test_cli.get('/str')
+    assert resp.status == 400
+
+
+async def test_with_path_params(test_cli):
+    resp = await test_cli.get('/with/123/path_params?test=hello')
+    assert resp.status == 200
+    resp_json = await resp.json()
+    assert resp_json == {'path_param': 123, 'test': 'hello', 'test_2': 35}

From b04cee7d294f58eeba934dc1623ca9f4f7e91750 Mon Sep 17 00:00:00 2001
From: Johannes Valbjorn <johannes.valbjorn@gmail.com>
Date: Fri, 12 Jan 2018 12:54:42 +0100
Subject: [PATCH 3/6] add simple example

---
 README.md          | 56 ++++++++++++++++++++++++++++++++++++++++------
 examples/simple.py | 15 +++++++++++++
 tests/__init__.py  |  1 +
 3 files changed, 65 insertions(+), 7 deletions(-)
 create mode 100644 examples/simple.py

diff --git a/README.md b/README.md
index 3023486..b6292a2 100644
--- a/README.md
+++ b/README.md
@@ -1,14 +1,56 @@
 [![Build Status](https://travis-ci.org/trustpilot/python-sanicargs.svg?branch=master)](https://travis-ci.org/trustpilot/python-sanicargs) [![Latest Version](https://img.shields.io/pypi/v/sanicargs.svg)](https://pypi.python.org/pypi/sanicargs) [![Python Support](https://img.shields.io/pypi/pyversions/sanicargs.svg)](https://pypi.python.org/pypi/sanicargs)
 
 # Sanicargs
-Parses query args in sanic using type annotations
+Parses query args in [Sanic](https://github.com/channelcat/sanic) using type annotations.
+
+## Install
+Install with pip
+```
+$ pip install sanicargs
+```
 
 ## Usage
 
-Use with [Sanic framework](https://github.com/channelcat/sanic)
+Use the `parse_query_args` decorator with [Sanic](https://github.com/channelcat/sanic)'s routes or blueprints like in the [example](https://github.com/trustpilot/python-sanicargs/tree/master/examples/simple.py) below:
+
+```python
+import datetime
+from sanic import Sanic, response
+from sanicargs import parse_query_args
+
+app = Sanic("test_sanic_app")
+
+@app.route("/me/<id>/birthdate", methods=['GET'])
+@parse_query_args
+async def test_datetime(request, id: str, birthdate: datetime.datetime):
+    return response.json({
+        'id': id, 
+        'birthdate': birthdate.isoformat()
+    })
+
+if __name__ == "__main__":
+  app.run(host="0.0.0.0", port=8080, access_log=False, debug=False)
+```
+
+Test it running with 
+```bash
+$ curl 'http://0.0.0.0:8080/me/123/birthdate?birthdate=2017-10-30'
 ```
-    @app.route("/datetime", methods=['GET'])
-    @parse_query_args
-    async def test_datetime(request, test: datetime.datetime):
-        return response.json({'test': test.isoformat()})
-```
\ No newline at end of file
+
+### Fields
+
+* **str** : `ex: ?message=hello world`
+* **int** : `ex: ?age=100`
+* **datetime.datetime** : `ex: ?currentdate=2017-10-30T10:10:30 or 2017-10-30`
+* **datetime.date** : `ex: ?birthdate=2017-10-30`
+* **List[str]** : `ex: ?words=you,me,them,we`
+
+### Important notice about decorators
+
+The sequence of decorators is, as usual, important in Python.
+
+You need to apply the `parse_query_args` decorator as the first one executed which means closest to the `def`.
+
+### `request` is mandatory!
+
+You should always have request as the first argument in your function in order to use `parse_query_args`
\ No newline at end of file
diff --git a/examples/simple.py b/examples/simple.py
new file mode 100644
index 0000000..711269c
--- /dev/null
+++ b/examples/simple.py
@@ -0,0 +1,15 @@
+import datetime
+from sanic import Sanic, response
+from sanicargs import parse_query_args
+
+app = Sanic("test_sanic_app")
+
+@app.route("/me/<id>/birthdate", methods=['GET'])
+@parse_query_args
+async def test_datetime(request, id: str, birthdate: datetime.datetime):
+    return response.json({
+        'id': id, 
+        'birthdate': birthdate.isoformat()
+    })
+if __name__ == "__main__":
+  app.run(host="0.0.0.0", port=8080, access_log=False, debug=False)
\ No newline at end of file
diff --git a/tests/__init__.py b/tests/__init__.py
index e69de29..8b13789 100644
--- a/tests/__init__.py
+++ b/tests/__init__.py
@@ -0,0 +1 @@
+

From 966ff7a28b98981f60e14b8eb5c07149bd1f69db Mon Sep 17 00:00:00 2001
From: Johannes Valbjorn <johannes.valbjorn@gmail.com>
Date: Fri, 12 Jan 2018 12:58:35 +0100
Subject: [PATCH 4/6] fix linting

---
 .travis.yml             | 3 ++-
 examples/simple.py      | 2 +-
 tests/__init__.py       | 1 -
 tests/test_sanicargs.py | 2 +-
 4 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/.travis.yml b/.travis.yml
index 76d6ba0..592f08a 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -9,7 +9,8 @@ install:
 - pip install pypandoc
 - pip install .[test]
 script:
-- py.test
+- prospector -M
+- pytest
 branches:
   only: master
 after_success:
diff --git a/examples/simple.py b/examples/simple.py
index 711269c..fc43d49 100644
--- a/examples/simple.py
+++ b/examples/simple.py
@@ -12,4 +12,4 @@ async def test_datetime(request, id: str, birthdate: datetime.datetime):
         'birthdate': birthdate.isoformat()
     })
 if __name__ == "__main__":
-  app.run(host="0.0.0.0", port=8080, access_log=False, debug=False)
\ No newline at end of file
+    app.run(host="0.0.0.0", port=8080, access_log=False, debug=False)
\ No newline at end of file
diff --git a/tests/__init__.py b/tests/__init__.py
index 8b13789..e69de29 100644
--- a/tests/__init__.py
+++ b/tests/__init__.py
@@ -1 +0,0 @@
-
diff --git a/tests/test_sanicargs.py b/tests/test_sanicargs.py
index bdb8553..abc176d 100644
--- a/tests/test_sanicargs.py
+++ b/tests/test_sanicargs.py
@@ -61,7 +61,7 @@ async def test_optional(request, test: str = 'helloworld'):
 
     @app.route("/with/<path_param>/path_params", methods=['GET'])
     @parse_query_args
-    async def test_optional(request, path_param: int, test: str, test_2: int=35):
+    async def test_path_params(request, path_param: int, test: str, test_2: int=35):
         return response.json({'path_param': path_param, 'test': test, 'test_2': test_2})
 
     yield app

From 8a5e88cbd3a61081614a806d0e9e984ef5078725 Mon Sep 17 00:00:00 2001
From: Johannes Valbjorn <johannes.valbjorn@gmail.com>
Date: Fri, 12 Jan 2018 13:01:59 +0100
Subject: [PATCH 5/6] bump version, fixups in README

---
 HISTORY.rst              | 8 +++++++-
 README.md                | 2 +-
 sanicargs/__version__.py | 2 +-
 3 files changed, 9 insertions(+), 3 deletions(-)

diff --git a/HISTORY.rst b/HISTORY.rst
index 031483b..7fb62b1 100644
--- a/HISTORY.rst
+++ b/HISTORY.rst
@@ -4,4 +4,10 @@ History
 0.0.1 (2017-08-09)
 ------------------
 
-* git init
\ No newline at end of file
+* git init
+
+1.0.0(2017-08-09)
+------------------
+
+* added test suite
+* added path_param type casting using
\ No newline at end of file
diff --git a/README.md b/README.md
index b6292a2..b4b2657 100644
--- a/README.md
+++ b/README.md
@@ -11,7 +11,7 @@ $ pip install sanicargs
 
 ## Usage
 
-Use the `parse_query_args` decorator with [Sanic](https://github.com/channelcat/sanic)'s routes or blueprints like in the [example](https://github.com/trustpilot/python-sanicargs/tree/master/examples/simple.py) below:
+Use the `parse_query_args` decorator to parse query args and type cast query args and path params with [Sanic](https://github.com/channelcat/sanic)'s routes or blueprints like in the [example](https://github.com/trustpilot/python-sanicargs/tree/master/examples/simple.py) below:
 
 ```python
 import datetime
diff --git a/sanicargs/__version__.py b/sanicargs/__version__.py
index 8dbfdad..75977e6 100644
--- a/sanicargs/__version__.py
+++ b/sanicargs/__version__.py
@@ -1 +1 @@
-__version__ = '0.0.3'
\ No newline at end of file
+__version__ = '1.0.0'
\ No newline at end of file

From 0a6368f9737c20cea25a96a83ff2079a7ccd2894 Mon Sep 17 00:00:00 2001
From: Johannes Valbjorn <johannes.valbjorn@gmail.com>
Date: Fri, 12 Jan 2018 13:13:26 +0100
Subject: [PATCH 6/6] review fixups

---
 HISTORY.rst           | 6 +++---
 sanicargs/__init__.py | 2 +-
 2 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/HISTORY.rst b/HISTORY.rst
index 7fb62b1..e270965 100644
--- a/HISTORY.rst
+++ b/HISTORY.rst
@@ -1,13 +1,13 @@
 History
 =======
 
-0.0.1 (2017-08-09)
+0.0.1 (2017-01-09)
 ------------------
 
 * git init
 
-1.0.0(2017-08-09)
+1.0.0(2017-01-12)
 ------------------
 
 * added test suite
-* added path_param type casting using
\ No newline at end of file
+* added path_param type casting
\ No newline at end of file
diff --git a/sanicargs/__init__.py b/sanicargs/__init__.py
index 52a6fd9..4012a67 100644
--- a/sanicargs/__init__.py
+++ b/sanicargs/__init__.py
@@ -8,7 +8,7 @@
 
 from logging import getLogger
 
-__logger = getLogger('sonicargs')
+__logger = getLogger('sanicargs')
 
 
 def __parse_datetime(str):