forked from fedora-infra/fasjson
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Fixes: fedora-infra#25 Signed-off-by: Aurélien Bompard <aurelien@bompard.org>
- Loading branch information
Showing
19 changed files
with
354 additions
and
239 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
import ldap | ||
from flask import jsonify | ||
from flask_restx import Api, abort | ||
from werkzeug.exceptions import HTTPException | ||
|
||
|
||
def handle_ldap_local_error(error): | ||
return ({"message": "LDAP local error", "details": str(error)}, 500) | ||
|
||
|
||
def handle_ldap_server_error(error): | ||
return {"message": "LDAP server is down"}, 500 | ||
|
||
|
||
def handle_webserver_error(code): | ||
"""Generate JSON on Apache-generated errors (or whichever webserver is used).""" | ||
abort(code) | ||
|
||
|
||
class FasJsonApi(Api): | ||
def init_app(self, app, **kwargs): | ||
super().init_app(app, **kwargs) | ||
self.errorhandler(ldap.LOCAL_ERROR)(handle_ldap_local_error) | ||
self.errorhandler(ldap.SERVER_DOWN)(handle_ldap_server_error) | ||
self.blueprint.record_once(self._on_blueprint_registration) | ||
|
||
def _on_blueprint_registration(self, state): | ||
# Add an URL rule on the top level app | ||
state.app.add_url_rule( | ||
f"/specs/{self.blueprint.name}.json", | ||
endpoint=f"{self.blueprint.name}.spec", | ||
view_func=self._view_spec, | ||
) | ||
|
||
# TODO: make sure the following two instructions are not done multiple times when we have | ||
# multiple API versions. | ||
|
||
# Make the main app's error handler use the API's error handler in order to output JSON | ||
state.app.register_error_handler(HTTPException, self.handle_error) | ||
# Register views for the webserver to use so that it outputs JSON too | ||
state.app.add_url_rule( | ||
"/errors/<int:code>", view_func=handle_webserver_error | ||
) | ||
|
||
def _view_spec(self): | ||
return jsonify(self.__schema__) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
from flask import Blueprint | ||
|
||
from .base import FasJsonApi | ||
from ..resources.me import api_v1 as me | ||
from ..resources.users import api_v1 as users | ||
from ..resources.groups import api_v1 as groups | ||
|
||
blueprint = Blueprint("v1", __name__, url_prefix="/v1") | ||
api = FasJsonApi( | ||
blueprint, | ||
title="FASJSON", | ||
version="1.0", | ||
description="The FASJSON API", | ||
doc="/doc/", | ||
) | ||
|
||
api.add_namespace(me) | ||
api.add_namespace(users) | ||
api.add_namespace(groups) |
This file was deleted.
Oops, something went wrong.
This file was deleted.
Oops, something went wrong.
This file was deleted.
Oops, something went wrong.
This file was deleted.
Oops, something went wrong.
This file was deleted.
Oops, something went wrong.
This file was deleted.
Oops, something went wrong.
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,51 +1,49 @@ | ||
from flask import Flask, request | ||
import re | ||
|
||
from flask import Flask, jsonify, url_for | ||
from werkzeug.routing import BaseConverter | ||
|
||
from . import errors | ||
from .apis import v1 | ||
from .response import ApiResponse | ||
from .apis.v1 import blueprint as blueprint_v1 | ||
|
||
from .extensions.flask_gss import FlaskGSSAPI | ||
from .extensions.flask_ipacfg import IPAConfig | ||
|
||
|
||
app = Flask(__name__) | ||
app.response_class = ApiResponse | ||
|
||
# extensions | ||
|
||
# Extensions | ||
FlaskGSSAPI(app) | ||
IPAConfig(app) | ||
|
||
|
||
# converters | ||
class UserGroupConverter(BaseConverter): | ||
# URL converters | ||
class NameConverter(BaseConverter): | ||
regex = "[a-zA-Z][a-zA-Z0-9_.-]{0,63}" | ||
|
||
|
||
app.url_map.converters["usergroup"] = UserGroupConverter | ||
|
||
# blueprints | ||
app.register_blueprint(v1.app, url_prefix="/v1") | ||
|
||
|
||
@app.errorhandler(errors.WebApiError) | ||
def handle_error(e): | ||
return e.get_response() | ||
app.url_map.converters["name"] = NameConverter | ||
|
||
|
||
@app.errorhandler(404) | ||
def handle_error_404(e): | ||
data = {"path": request.path, "method": request.method} | ||
e = errors.WebApiError("resource not found", 404, data=data) | ||
return e.get_response() | ||
# TODO: consider having only one class per resource and passing the API version from the global g | ||
# variable as described here: | ||
# https://flask.palletsprojects.com/en/1.1.x/patterns/urlprocessors/#internationalized-blueprint-urls | ||
app.register_blueprint(blueprint_v1) | ||
|
||
|
||
@app.errorhandler(500) | ||
def handle_error_500(e): | ||
original = getattr(e, "original_exception", None) | ||
data = { | ||
"path": request.path, | ||
"method": request.method, | ||
"exception": str(original), | ||
} | ||
e = errors.WebApiError("unexpected internal error", 500, data=data) | ||
return e.get_response() | ||
@app.route("/") | ||
def root(): | ||
blueprints = sorted( | ||
[name for name in app.blueprints if re.match("^v[0-9]+$", name)], | ||
key=lambda name: int(name[1:]), | ||
) | ||
apis = [ | ||
{ | ||
"version": int(name[1:]), | ||
"uri": url_for(f"{name}.root", _external=True), | ||
"spec": url_for(f"{name}.spec", _external=True), | ||
"doc": url_for(f"{name}.doc", _external=True), | ||
} | ||
for name in blueprints | ||
] | ||
return jsonify({"message": "Welcome to FASJSON", "apis": apis}) |
This file was deleted.
Oops, something went wrong.
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
from flask_restx import Namespace as RestXNamespace | ||
|
||
|
||
class Namespace(RestXNamespace): | ||
def marshal_with(self, *args, **kwargs): | ||
kwargs.setdefault("envelope", "result") | ||
return super().marshal_with(*args, **kwargs) |
Oops, something went wrong.