Skip to content

Commit

Permalink
Merge pull request #434 from HebaruSan/feature/flask-2
Browse files Browse the repository at this point in the history
  • Loading branch information
DasSkelett authored Mar 12, 2022
2 parents 4e73161 + d2485b0 commit b632cb8
Show file tree
Hide file tree
Showing 18 changed files with 51 additions and 80 deletions.
13 changes: 7 additions & 6 deletions KerbalStuff/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@
from flask_login import LoginManager, current_user
from flaskext.markdown import Markdown
from sqlalchemy import desc
from werkzeug.exceptions import HTTPException, InternalServerError
from werkzeug.exceptions import HTTPException, InternalServerError, NotFound
from flask.typing import ResponseReturnValue
from jinja2 import ChainableUndefined

from .blueprints.accounts import accounts
Expand Down Expand Up @@ -129,8 +130,8 @@ def load_user(username: str) -> User:
# 1 | 1 | 1 | 4XX in API -> jsonified_exception(e)


@app.errorhandler(404)
def handle_404(e: HTTPException) -> Union[Tuple[str, int], werkzeug.wrappers.Response]:
@app.errorhandler(NotFound)
def handle_404(e: NotFound) -> ResponseReturnValue:
# Switch out the default message
if e.description == werkzeug.exceptions.NotFound.description:
e.description = "Requested page not found. Looks like this was deleted, or maybe was never here."
Expand All @@ -143,7 +144,7 @@ def handle_404(e: HTTPException) -> Union[Tuple[str, int], werkzeug.wrappers.Res

# This one handles the remaining 4XX errors. JSONified for XHR requests, otherwise the user gets a nice error screen.
@app.errorhandler(HTTPException)
def handle_http_exception(e: HTTPException) -> Union[Tuple[str, int], werkzeug.wrappers.Response]:
def handle_http_exception(e: HTTPException) -> ResponseReturnValue:
if e.code and e.code >= 500:
return handle_generic_exception(e)
if request.path.startswith("/api/") \
Expand All @@ -155,7 +156,7 @@ def handle_http_exception(e: HTTPException) -> Union[Tuple[str, int], werkzeug.w
# And this one handles everything leftover, that means, real otherwise unhandled exceptions.
# https://flask.palletsprojects.com/en/1.1.x/errorhandling/#unhandled-exceptions
@app.errorhandler(Exception)
def handle_generic_exception(e: Union[Exception, HTTPException]) -> Union[Tuple[str, int], werkzeug.wrappers.Response]:
def handle_generic_exception(e: Exception) -> ResponseReturnValue:
site_logger.exception(e)
try:
db.rollback()
Expand All @@ -179,7 +180,7 @@ def handle_generic_exception(e: Union[Exception, HTTPException]) -> Union[Tuple[


@app.teardown_request
def teardown_request(exception: Optional[Exception]) -> None:
def teardown_request(exception: Optional[BaseException]) -> None:
db.close()


Expand Down
2 changes: 0 additions & 2 deletions KerbalStuff/blueprints/login_oauth.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,8 +66,6 @@ def get_github_oath() -> Tuple[str, OAuthRemoteApp]:
if resp is None:
raise Exception(
f"Access denied: reason={request.args['error']} error={request.args['error_description']}")
if 'error' in resp:
return jsonify(resp)
session['github_token'] = (resp['access_token'], '')
gh_info = github.get('user').data
return gh_info['login'], github
Expand Down
19 changes: 0 additions & 19 deletions KerbalStuff/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -127,25 +127,6 @@ def wrapper(*args: str, **kwargs: int) -> werkzeug.wrappers.Response:
return wrapper


def cors(f: Callable[..., Any]) -> Callable[..., Any]:
@wraps(f)
def wrapper(*args: str, **kwargs: int) -> Tuple[str, int]:
res = f(*args, **kwargs)
if request.headers.get('x-cors-status', False):
if isinstance(res, tuple):
json_text = res[0].data
code = res[1]
else:
json_text = res.data
code = 200
o = json.loads(json_text)
o['x-status'] = code
return jsonify(o)
return res

return wrapper


def paginate_query(query: Query, page_size: int = 30) -> Tuple[List[Mod], int, int]:
total_pages = math.ceil(query.count() / page_size)
page = get_page()
Expand Down
3 changes: 1 addition & 2 deletions KerbalStuff/database.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
from sqlalchemy import create_engine
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import scoped_session, sessionmaker
from sqlalchemy.orm import scoped_session, sessionmaker, declarative_base

from .config import _cfg

Expand Down
17 changes: 9 additions & 8 deletions KerbalStuff/kerbdown.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import urllib.parse
from urllib.parse import parse_qs, urlparse
from typing import Dict, Any, Match, Tuple
from typing import Dict, Any, Match, Tuple, Optional

from markdown import Markdown
from markdown.extensions import Extension
from markdown.inlinepatterns import InlineProcessor
from markdown.util import etree
from xml.etree import ElementTree


class EmbedInlineProcessor(InlineProcessor):
Expand All @@ -23,18 +23,19 @@ def __init__(self, md: Markdown, configs: Dict[str, Any]) -> None:
super().__init__(self.EMBED_RE, md)
self.config = configs

def handleMatch(self, m: Match[str], data: str) -> Tuple[etree.Element, int, int]: # type: ignore[override]
def handleMatch(self, m: Match[str], data: str) -> Tuple[ElementTree.Element, int, int]: # type: ignore[override]
d = m.groupdict()
url = d.get('url')
el: Optional[ElementTree.Element]
if not url:
el = etree.Element('span')
el = ElementTree.Element('span')
el.text = "[[]]"
return el, m.start(0), m.end(0)
try:
link = urlparse(url)
host = link.hostname
except:
el = etree.Element('span')
el = ElementTree.Element('span')
el.text = "[[" + url + "]]"
return el, m.start(0), m.end(0)
el = None
Expand All @@ -44,16 +45,16 @@ def handleMatch(self, m: Match[str], data: str) -> Tuple[etree.Element, int, int
except:
pass
if el is None:
el = etree.Element('span')
el = ElementTree.Element('span')
el.text = "[[" + url + "]]"
return el, m.start(0), m.end(0)

def _get_youtube_id(self, link: urllib.parse.ParseResult) -> str:
return (link.path if link.netloc == 'youtu.be'
else parse_qs(link.query)['v'][0])

def _embed_youtube(self, vid_id: str) -> etree.Element:
el = etree.Element('iframe')
def _embed_youtube(self, vid_id: str) -> ElementTree.Element:
el = ElementTree.Element('iframe')
el.set('width', '100%')
el.set('height', '600')
el.set('frameborder', '0')
Expand Down
8 changes: 0 additions & 8 deletions KerbalStuff/stubs/jinja2.pyi

This file was deleted.

3 changes: 1 addition & 2 deletions alembic/versions/2020_06_23_17_49_36-85be165bc5dc.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,7 @@

from packaging import version
from sqlalchemy import orm, Column, Integer, Unicode, DateTime, String, ForeignKey
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import relationship, backref
from sqlalchemy.orm import relationship, backref, declarative_base

# revision identifiers, used by Alembic.
revision = '85be165bc5dc'
Expand Down
2 changes: 1 addition & 1 deletion alembic/versions/2020_07_20_19_00_00-73c9d707134b.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
from alembic import op
import sqlalchemy as sa

Base = sa.ext.declarative.declarative_base()
Base = sa.orm.declarative_base()


class ModVersion(Base): # type: ignore
Expand Down
2 changes: 1 addition & 1 deletion alembic/versions/2021_06_28_19_00_00-17fbd4ff8193.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
from alembic import op
import sqlalchemy as sa

Base = sa.ext.declarative.declarative_base()
Base = sa.orm.declarative_base()


class User(Base): # type: ignore
Expand Down
4 changes: 2 additions & 2 deletions requirements-backend.txt
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,14 @@ celery
click
dnspython
flameprof
Flask<2 # Needs testing before upgrading
Flask
Flask-Login
Flask-Markdown
Flask-OAuthlib
future
GitPython
gunicorn
Jinja2<3 # See Flask
Jinja2
Markdown
MarkupSafe
oauthlib
Expand Down
2 changes: 0 additions & 2 deletions requirements-tests.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,6 @@ flask-api
flask-testing
types-Markdown
types-bleach
types-Flask
types-Jinja2
types-Markdown
types-MarkupSafe
types-Werkzeug
Expand Down
4 changes: 2 additions & 2 deletions spacedock
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,8 @@ def wait_database():
from sqlalchemy.engine.url import URL
site_logger.info('Waiting for database to come online...')
u = engine.url
pg_engine = create_engine(URL(u.drivername, u.username, u.password,
u.host, u.port))
pg_engine = create_engine(URL.create(u.drivername, u.username, u.password,
u.host, u.port))
while True:
try:
connection = pg_engine.connect()
Expand Down
2 changes: 2 additions & 0 deletions tests/fixtures/fake_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,7 @@
config[env]['protocol'] = 'https'
config[env]['domain'] = 'tests.spacedock.info'
config[env]['ksp-game-id'] = '1'
if 'profile-dir' in config[env]:
del config[env]['profile-dir']

dummy = ''
10 changes: 5 additions & 5 deletions tests/test_api_browse.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import pytest
from flask.testing import FlaskClient
from flask import Response
from flask_api import status
from http import HTTPStatus

from .fixtures.client import client
from KerbalStuff.objects import Publisher, Game, GameVersion, User, Mod, ModVersion
Expand All @@ -21,14 +21,14 @@ def test_api_browse(client: 'FlaskClient[Response]') -> None:
featured_resp = client.get('/api/browse/featured')

# Assert
assert browse_resp.status_code == status.HTTP_200_OK, 'Request should succeed'
assert browse_resp.status_code == HTTPStatus.OK, 'Request should succeed'
assert browse_resp.data == b'{"total":0,"count":30,"pages":1,"page":1,"result":[]}', 'Should be a simple empty db'

assert new_resp.status_code == status.HTTP_200_OK, 'Request should succeed'
assert new_resp.status_code == HTTPStatus.OK, 'Request should succeed'
assert new_resp.data == b'[]', 'Should return empty list'

assert top_resp.status_code == status.HTTP_200_OK, 'Request should succeed'
assert top_resp.status_code == HTTPStatus.OK, 'Request should succeed'
assert top_resp.data == b'[]', 'Should return empty list'

assert featured_resp.status_code == status.HTTP_200_OK, 'Request should succeed'
assert featured_resp.status_code == HTTPStatus.OK, 'Request should succeed'
assert featured_resp.data == b'[]', 'Should return empty list'
8 changes: 4 additions & 4 deletions tests/test_api_errors.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import pytest
from flask.testing import FlaskClient
from flask import Response
from flask_api import status
from http import HTTPStatus

from .fixtures.client import client

Expand All @@ -14,8 +14,8 @@ def test_api_bad_url(client: 'FlaskClient[Response]') -> None:
bad_url_resp = client.get('/api/something_that_matches_no_routes/69/420')

# Assert
assert bad_url_resp.status_code == status.HTTP_404_NOT_FOUND, 'Request should fail'
assert bad_url_resp.json['code'] == status.HTTP_404_NOT_FOUND, 'Code should match'
assert bad_url_resp.status_code == HTTPStatus.NOT_FOUND, 'Request should fail'
assert bad_url_resp.json['code'] == HTTPStatus.NOT_FOUND, 'Code should match'
assert bad_url_resp.json['error'] == True, 'Should contain "error" property'
assert 'not found' in bad_url_resp.json['reason'], 'Reason should be typical 404 lingo'

Expand All @@ -28,6 +28,6 @@ def test_api_mod_not_found(client: 'FlaskClient[Response]') -> None:
missing_mod_resp = client.get('/api/mod/20000')

# Assert
assert missing_mod_resp.status_code == status.HTTP_404_NOT_FOUND, 'Request should fail'
assert missing_mod_resp.status_code == HTTPStatus.NOT_FOUND, 'Request should fail'
assert missing_mod_resp.json['error'] == True, 'Should contain "error" property'
assert missing_mod_resp.json['reason'] == 'Mod not found.', 'Reason should match'
22 changes: 11 additions & 11 deletions tests/test_api_mod.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
import pytest
from flask.testing import FlaskClient
from flask import Response
from flask_api import status
from http import HTTPStatus

from .fixtures.client import client
from KerbalStuff.objects import Publisher, Game, GameVersion, User, Mod, ModVersion
Expand Down Expand Up @@ -65,36 +65,36 @@ def test_api_mod(client: 'FlaskClient[Response]') -> None:
search_user_resp = client.get('/api/search/user?query=Test&page=0')

# Assert
assert mod_resp.status_code == status.HTTP_200_OK, 'Request should succeed'
assert mod_resp.status_code == HTTPStatus.OK, 'Request should succeed'
check_mod(mod_resp.json)
# Not returned by all APIs
assert mod_resp.json['description'] == 'A mod that we will use to test the API', 'Short description should match'

assert kspversions_resp.status_code == status.HTTP_200_OK, 'Request should succeed'
assert kspversions_resp.status_code == HTTPStatus.OK, 'Request should succeed'
check_game_version(kspversions_resp.json[0])

assert gameversions_resp.status_code == status.HTTP_200_OK, 'Request should succeed'
assert gameversions_resp.status_code == HTTPStatus.OK, 'Request should succeed'
check_game_version(gameversions_resp.json[0])

assert games_resp.status_code == status.HTTP_200_OK, 'Request should succeed'
assert games_resp.status_code == HTTPStatus.OK, 'Request should succeed'
check_game(games_resp.json[0])

assert publishers_resp.status_code == status.HTTP_200_OK, 'Request should succeed'
assert publishers_resp.status_code == HTTPStatus.OK, 'Request should succeed'
check_publisher(publishers_resp.json[0])

assert mod_version_resp.status_code == status.HTTP_200_OK, 'Request should succeed'
assert mod_version_resp.status_code == HTTPStatus.OK, 'Request should succeed'
check_mod_version(mod_version_resp.json)

assert user_resp.status_code == status.HTTP_200_OK, 'Request should succeed'
assert user_resp.status_code == HTTPStatus.OK, 'Request should succeed'
check_user(user_resp.json)

assert typeahead_resp.status_code == status.HTTP_200_OK, 'Request should succeed'
assert typeahead_resp.status_code == HTTPStatus.OK, 'Request should succeed'
check_mod(typeahead_resp.json[0])

assert search_mod_resp.status_code == status.HTTP_200_OK, 'Request should succeed'
assert search_mod_resp.status_code == HTTPStatus.OK, 'Request should succeed'
check_mod(search_mod_resp.json[0])

assert search_user_resp.status_code == status.HTTP_200_OK, 'Request should succeed'
assert search_user_resp.status_code == HTTPStatus.OK, 'Request should succeed'
check_user(search_user_resp.json[0])


Expand Down
6 changes: 3 additions & 3 deletions tests/test_errors.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import pytest
from flask.testing import FlaskClient
from flask import Response
from flask_api import status
from http import HTTPStatus

from .fixtures.client import client

Expand All @@ -14,7 +14,7 @@ def test_bad_url(client: 'FlaskClient[Response]') -> None:
bad_url_resp = client.get('/something_that_matches_no_routes/69/420')

# Assert
assert bad_url_resp.status_code == status.HTTP_404_NOT_FOUND, 'Request should fail'
assert bad_url_resp.status_code == HTTPStatus.NOT_FOUND, 'Request should fail'
assert bad_url_resp.json is None, 'Should not be JSON'
assert bad_url_resp.mimetype == 'text/html', 'Should be HTML'
assert b'Not Found' in bad_url_resp.data, 'Should be a nice web page'
Expand All @@ -29,7 +29,7 @@ def test_mod_not_found(client: 'FlaskClient[Response]') -> None:
missing_mod_resp = client.get('/mod/20000')

# Assert
assert missing_mod_resp.status_code == status.HTTP_404_NOT_FOUND, 'Request should fail'
assert missing_mod_resp.status_code == HTTPStatus.NOT_FOUND, 'Request should fail'
assert missing_mod_resp.json is None, 'Should not be JSON'
assert missing_mod_resp.mimetype == 'text/html', 'Should be HTML'
assert b'Not Found' in missing_mod_resp.data, 'Should be a nice web page'
Expand Down
4 changes: 2 additions & 2 deletions tests/test_version.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import pytest
from flask.testing import FlaskClient
from flask import Response
from flask_api import status
from http import HTTPStatus

from .fixtures.client import client

Expand All @@ -14,7 +14,7 @@ def test_version(client: 'FlaskClient[Response]') -> None:
resp = client.get('/version')

# Assert
assert resp.status_code == status.HTTP_200_OK, 'Request should succeed'
assert resp.status_code == HTTPStatus.OK, 'Request should succeed'
assert resp.data.startswith(b'commit'), 'Response should start with "commit"'
assert b'\nAuthor: ' in resp.data, 'Response should return a Author header'
assert b'\nDate: ' in resp.data, 'Response should return a Date header'

0 comments on commit b632cb8

Please sign in to comment.