Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
5736cf1
fix spec viewing in SwaggerUI.
flask-pro Mar 29, 2024
90cdfdb
fix spec viewing in SwaggerUI.
flask-pro Mar 29, 2024
b3b908e
fix spec viewing in SwaggerUI.
flask-pro Mar 29, 2024
8a6893e
fix spec viewing in SwaggerUI.
flask-pro Mar 29, 2024
6514027
fix spec viewing in SwaggerUI.
flask-pro Mar 29, 2024
205a6eb
fix spec viewing in SwaggerUI.
flask-pro Mar 29, 2024
48af14f
fix spec viewing in SwaggerUI.
flask-pro Mar 29, 2024
6b2386a
fix spec viewing in SwaggerUI.
flask-pro Mar 29, 2024
5d6a3d2
fix spec viewing in SwaggerUI.
flask-pro Mar 29, 2024
1de925d
fix spec viewing in SwaggerUI.
flask-pro Mar 29, 2024
f3fb06e
fix spec viewing in SwaggerUI.
flask-pro Mar 29, 2024
88e95e8
fix spec viewing in SwaggerUI.
flask-pro Mar 29, 2024
e3531cd
fix spec viewing in SwaggerUI.
flask-pro Mar 29, 2024
0b21f36
fix spec viewing in SwaggerUI.
flask-pro Mar 29, 2024
9e8dcc5
fix spec viewing in SwaggerUI.
flask-pro Mar 29, 2024
e4bddb8
fix spec viewing in SwaggerUI.
flask-pro Mar 29, 2024
4afca23
fix spec viewing in SwaggerUI.
flask-pro Mar 29, 2024
be5eeb0
fix spec viewing in SwaggerUI.
flask-pro Mar 29, 2024
08285ea
fix spec viewing in SwaggerUI.
flask-pro Mar 30, 2024
e9a066c
fix spec viewing in SwaggerUI.
flask-pro Mar 30, 2024
c8d6619
fix spec viewing in SwaggerUI.
flask-pro Mar 30, 2024
15792c0
fix spec viewing in SwaggerUI.
flask-pro Mar 30, 2024
542f24d
fix spec viewing in SwaggerUI.
flask-pro Mar 30, 2024
d0d4f6b
fix spec viewing in SwaggerUI.
flask-pro Mar 30, 2024
b2f193d
fix spec viewing in SwaggerUI.
flask-pro Mar 30, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 41 additions & 0 deletions .github/workflows/testing.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
name: Flask-First testing
run-name: ${{ github.actor }} is testing Flask-First
on: [push]
jobs:
linters:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: '3.12'
- run: make format
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: '3.12'
- run: make test
tox:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: |
3.8
3.9
3.10
3.11
3.12
- run: make tox
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: '3.12'
- run: make build
4 changes: 4 additions & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
## Version 0.16.1

* Fix the display of the specification described in several files in Swagger UI.

## Version 0.16.0

* Add support for splitting OpenAPI specification into multiple files.
Expand Down
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ venv:
./venv/bin/pip3 -q install -e ".[dev]"
./venv/bin/pip3 -q install -e .

format:
format: venv
# Run checking and formatting sources.
./venv/bin/pre-commit run -a

Expand Down
4 changes: 2 additions & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,12 @@ dependencies = [
'openapi-spec-validator>=0.5.0',
'marshmallow>=3.14.1'
]
description = "Flask extension for using 'specification first' principle via OpenAPI specification"
description = "Flask extension for using 'specification first' or 'API-first' principle via OpenAPI specification."
license = {file = "LICENSE"}
name = "Flask-First"
readme = "README.md"
requires-python = ">=3.9"
version = "0.16.0"
version = "0.16.1"

[project.optional-dependencies]
dev = [
Expand Down
2 changes: 1 addition & 1 deletion src/flask_first/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -255,7 +255,7 @@ def init_app(self, app: Flask) -> None:
)

if self.swagger_ui_path:
add_swagger_ui_blueprint(self.app, self.path_to_spec, self.swagger_ui_path)
add_swagger_ui_blueprint(self.app, self.spec, self.swagger_ui_path)

self._register_request_validation()

Expand Down
10 changes: 7 additions & 3 deletions src/flask_first/first/specification.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,8 @@ class Resolver:
Specification from several files are supported.
"""

def __init__(self, abs_path: Path):
self.abs_path = abs_path
def __init__(self, abs_path: Path or str):
self.abs_path = Path(abs_path)
self.root_dir = self.abs_path.resolve().parent

@staticmethod
Expand Down Expand Up @@ -53,7 +53,11 @@ def _get_schema_from_file_ref(self, root_dir: Path, relative_path: Path, keys: d
def _resolving(self, schema: dict, relative_path_to_file_schema: str) -> dict or list[dict]:
if isinstance(schema, dict):
if '$ref' in schema:
relative_file_path_from_ref, local_path = schema['$ref'].split('#/')
try:
relative_file_path_from_ref, local_path = schema['$ref'].split('#/')
except AttributeError:
raise FirstOpenAPIResolverError(f'"$ref" <{schema["$ref"]}> must be string.')

local_path_parts = local_path.split('/')

if relative_file_path_from_ref:
Expand Down
11 changes: 6 additions & 5 deletions src/flask_first/swagger_ui.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,12 @@
from flask import Blueprint
from flask import Flask
from flask import render_template
from flask import send_file
from flask import url_for

from .first.specification import Specification

def add_swagger_ui_blueprint(app: Flask, path_to_spec: str, swagger_ui_path: str or Path) -> None:

def add_swagger_ui_blueprint(app: Flask, spec: Specification, swagger_ui_path: str or Path) -> None:
swagger_ui = Blueprint(
'swagger_ui',
__name__,
Expand All @@ -22,10 +23,10 @@ def swagger_ui_static(filename):

@swagger_ui.route('/')
def swagger_ui_page():
return render_template('swagger_ui/index.html', path_to_spec=path_to_spec)
return render_template('swagger_ui/index.html')

@swagger_ui.route('/openapi.yaml')
@swagger_ui.route('/openapi.json')
def get_file_spec():
return send_file(path_to_spec)
return spec.raw_spec

app.register_blueprint(swagger_ui)
4 changes: 2 additions & 2 deletions tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,10 +45,10 @@ def fx_client(fx_app) -> FlaskClient:
def fx_create_app():
def _create_app(path_to_spec: str, routes_functions: Iterable):
app = Flask('testing_app')
app.debug = 1
app.debug = True
app.config['FIRST_RESPONSE_VALIDATION'] = True

first = First(path_to_spec, app)
first = First(path_to_spec, app, swagger_ui_path='/docs')
for func in routes_functions:
first.add_view_func(func)

Expand Down
61 changes: 38 additions & 23 deletions tests/test_flask_first.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,44 @@
}


def test_specification__factory_app():
def mini_endpoint() -> dict:
return {'message': 'test_factory_app'}

first = First(Path(BASEDIR, 'specs/v3.1.0/mini.openapi.yaml'))

def create_app():
flask_app = Flask('factory_app')
first.init_app(flask_app)
first.add_view_func(mini_endpoint)
return flask_app

app = create_app()

with app.test_client() as test_client:
r = test_client.get('/mini_endpoint')
assert r.status_code == 200
assert r.json['message'] == 'test_factory_app'


def test_flask_first__swagger_ui(fx_create_app):
def mini_endpoint() -> dict:
return {'message': 'test_flask_first__swagger_ui'}

test_client = fx_create_app(Path(BASEDIR, 'specs/v3.1.0/mini.openapi.yaml'), [mini_endpoint])

r = test_client.get('/docs/openapi.json')
assert r.status_code == 200
assert r.text

r = test_client.get('/docs', follow_redirects=True)
assert r.status_code == 200
assert '<title>Swagger UI</title>' in r.text


# Old test


def test_specification__create_item(fx_app, fx_client):
def create_item() -> tuple:
obj = {**request.json, 'uuid': ITEM['uuid']}
Expand Down Expand Up @@ -218,29 +256,6 @@ def endpoint_with_header() -> dict:
assert r.json['message'] == 'test_header'


def test_specification__factory_app():
def mini_endpoint() -> dict:
return {'message': 'test_factory_app'}

first = First(Path(BASEDIR, 'specs/v3.1.0/mini.openapi.yaml'))

def create_app():
app = Flask('factory_app')
app.debug = 1
app.testing = 1
app.config['FIRST_RESPONSE_VALIDATION'] = True
first.init_app(app)
first.add_view_func(mini_endpoint)
return app

app = create_app()

with app.test_client() as test_client:
r = test_client.get('/mini_endpoint')
assert r.status_code == 200
assert r.json['message'] == 'test_factory_app'


def test_specification__registration_function():
def mini_endpoint() -> dict:
return {'message': 'test_factory_app'}
Expand Down
2 changes: 1 addition & 1 deletion tests/test_specification.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ def test_specification__check_v30_specs(spec):

r = app.test_client().get('/docs', follow_redirects=True)
assert r.status_code == 200
assert '/docs/openapi.yaml' in r.data.decode()
assert '/docs/openapi.json' in r.data.decode()


def test_specification__nullable_parameter():
Expand Down