Repository: marshmallow-code/apispec

Issue:
Flask extension does not support application root
The flask extension only outputs the route path, but `flask.url_for()` takes into consideration that the path should be prefixed with the configured `APPLICATION_ROOT` path. [apispec/ext/flask.py#L58](https://github.com/marshmallow-code/apispec/blob/1076180/apispec/ext/flask.py#L58)

 - http://flask.pocoo.org/docs/0.10/config/#builtin-configuration-values
 - https://github.com/pallets/flask/blob/9f1be8e/flask/helpers.py#L309
 - https://github.com/pallets/flask/blob/2bf477c/flask/app.py#L1761

Discussion:

Patch:
diff --git a/apispec/ext/flask.py b/apispec/ext/flask.py
index 19c029f..a6c1e97 100644
--- a/apispec/ext/flask.py
+++ b/apispec/ext/flask.py
@@ -27,6 +27,11 @@ function to `add_path`. Inspects URL rules and view docstrings.
 from __future__ import absolute_import
 import re
 
+try:
+    from urllib.parse import urljoin
+except ImportError:
+    from urlparse import urljoin
+
 from flask import current_app
 
 from apispec.compat import iteritems
@@ -61,6 +66,8 @@ def path_from_view(spec, view, **kwargs):
     """Path helper that allows passing a Flask view function."""
     rule = _rule_for_view(view)
     path = flaskpath2swagger(rule.rule)
+    app_root = current_app.config['APPLICATION_ROOT'] or '/'
+    path = urljoin(app_root.rstrip('/') + '/', path.lstrip('/'))
     operations = utils.load_operations_from_docstring(view.__doc__)
     path = Path(path=path, operations=operations)
     return path


Test patch:
diff --git a/tests/test_ext_flask.py b/tests/test_ext_flask.py
index 0f6f292..eeb4a03 100644
--- a/tests/test_ext_flask.py
+++ b/tests/test_ext_flask.py
@@ -105,3 +105,36 @@ class TestPathHelpers:
 
         spec.add_path(view=get_pet)
         assert '/pet/{pet_id}' in spec._paths
+
+    def test_path_includes_app_root(self, app, spec):
+
+        app.config['APPLICATION_ROOT'] = '/app/root'
+
+        @app.route('/partial/path/pet')
+        def get_pet():
+            return 'pet'
+
+        spec.add_path(view=get_pet)
+        assert '/app/root/partial/path/pet' in spec._paths
+
+    def test_path_with_args_includes_app_root(self, app, spec):
+
+        app.config['APPLICATION_ROOT'] = '/app/root'
+
+        @app.route('/partial/path/pet/{pet_id}')
+        def get_pet(pet_id):
+            return 'representation of pet {pet_id}'.format(pet_id=pet_id)
+
+        spec.add_path(view=get_pet)
+        assert '/app/root/partial/path/pet/{pet_id}' in spec._paths
+
+    def test_path_includes_app_root_with_right_slash(self, app, spec):
+
+        app.config['APPLICATION_ROOT'] = '/app/root/'
+
+        @app.route('/partial/path/pet')
+        def get_pet():
+            return 'pet'
+
+        spec.add_path(view=get_pet)
+        assert '/app/root/partial/path/pet' in spec._paths