-
Notifications
You must be signed in to change notification settings - Fork 72
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* refactor: add flask-graphql as optional feature * refactor(server): default_format_error to __all__ * chore: rename dir flask-graphql to flask * chore: add extras require all key * chore: update gitignore * fix(sc): move params query check to try-except * refactor(flask): remove unused backend param * tests(flask): graphiqlview and graphqlview * styles: apply black, isort, flake8 formatting * chore: add all requires to test env * chore(flask): remove blueprint module * refactor(flask): remove py27 imports and unused test * styles: apply black, isort and flake8 formatting
- Loading branch information
1 parent
865ee9d
commit 66b8a2b
Showing
11 changed files
with
1,213 additions
and
18 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 |
---|---|---|
@@ -1,19 +1,203 @@ | ||
*.pyc | ||
*.pyo | ||
|
||
# Created by https://www.gitignore.io/api/python,intellij+all,visualstudiocode | ||
# Edit at https://www.gitignore.io/?templates=python,intellij+all,visualstudiocode | ||
|
||
### Intellij+all ### | ||
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and WebStorm | ||
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 | ||
|
||
# User-specific stuff | ||
.idea/**/workspace.xml | ||
.idea/**/tasks.xml | ||
.idea/**/usage.statistics.xml | ||
.idea/**/dictionaries | ||
.idea/**/shelf | ||
|
||
# Generated files | ||
.idea/**/contentModel.xml | ||
|
||
# Sensitive or high-churn files | ||
.idea/**/dataSources/ | ||
.idea/**/dataSources.ids | ||
.idea/**/dataSources.local.xml | ||
.idea/**/sqlDataSources.xml | ||
.idea/**/dynamic.xml | ||
.idea/**/uiDesigner.xml | ||
.idea/**/dbnavigator.xml | ||
|
||
# Gradle | ||
.idea/**/gradle.xml | ||
.idea/**/libraries | ||
|
||
# Gradle and Maven with auto-import | ||
# When using Gradle or Maven with auto-import, you should exclude module files, | ||
# since they will be recreated, and may cause churn. Uncomment if using | ||
# auto-import. | ||
# .idea/modules.xml | ||
# .idea/*.iml | ||
# .idea/modules | ||
# *.iml | ||
# *.ipr | ||
|
||
# CMake | ||
cmake-build-*/ | ||
|
||
# Mongo Explorer plugin | ||
.idea/**/mongoSettings.xml | ||
|
||
# File-based project format | ||
*.iws | ||
|
||
# IntelliJ | ||
out/ | ||
|
||
# mpeltonen/sbt-idea plugin | ||
.idea_modules/ | ||
|
||
# JIRA plugin | ||
atlassian-ide-plugin.xml | ||
|
||
# Cursive Clojure plugin | ||
.idea/replstate.xml | ||
|
||
# Crashlytics plugin (for Android Studio and IntelliJ) | ||
com_crashlytics_export_strings.xml | ||
crashlytics.properties | ||
crashlytics-build.properties | ||
fabric.properties | ||
|
||
# Editor-based Rest Client | ||
.idea/httpRequests | ||
|
||
# Android studio 3.1+ serialized cache file | ||
.idea/caches/build_file_checksums.ser | ||
|
||
### Intellij+all Patch ### | ||
# Ignores the whole .idea folder and all .iml files | ||
# See https://github.com/joeblau/gitignore.io/issues/186 and https://github.com/joeblau/gitignore.io/issues/360 | ||
|
||
.idea/ | ||
|
||
# Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-249601023 | ||
|
||
*.iml | ||
modules.xml | ||
.idea/misc.xml | ||
*.ipr | ||
|
||
# Sonarlint plugin | ||
.idea/sonarlint | ||
|
||
### Python ### | ||
# Byte-compiled / optimized / DLL files | ||
__pycache__/ | ||
*.py[cod] | ||
*$py.class | ||
|
||
# C extensions | ||
*.so | ||
|
||
# Distribution / packaging | ||
.Python | ||
build/ | ||
develop-eggs/ | ||
dist/ | ||
downloads/ | ||
eggs/ | ||
.eggs/ | ||
lib/ | ||
lib64/ | ||
parts/ | ||
sdist/ | ||
var/ | ||
wheels/ | ||
pip-wheel-metadata/ | ||
share/python-wheels/ | ||
*.egg-info/ | ||
.installed.cfg | ||
*.egg | ||
*.egg-info | ||
MANIFEST | ||
|
||
.cache | ||
# PyInstaller | ||
# Usually these files are written by a python script from a template | ||
# before PyInstaller builds the exe, so as to inject date/other infos into it. | ||
*.manifest | ||
*.spec | ||
|
||
# Installer logs | ||
pip-log.txt | ||
pip-delete-this-directory.txt | ||
|
||
# Unit test / coverage reports | ||
htmlcov/ | ||
.tox/ | ||
.nox/ | ||
.coverage | ||
.idea | ||
.mypy_cache | ||
.pytest_cache | ||
.tox | ||
.venv | ||
.coverage.* | ||
.cache | ||
nosetests.xml | ||
coverage.xml | ||
*.cover | ||
.hypothesis/ | ||
.pytest_cache/ | ||
|
||
# Translations | ||
*.mo | ||
*.pot | ||
|
||
# Scrapy stuff: | ||
.scrapy | ||
|
||
# Sphinx documentation | ||
docs/_build/ | ||
|
||
# PyBuilder | ||
target/ | ||
|
||
# pyenv | ||
.python-version | ||
|
||
# pipenv | ||
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. | ||
# However, in case of collaboration, if having platform-specific dependencies or dependencies | ||
# having no cross-platform support, pipenv may install dependencies that don't work, or not | ||
# install all needed dependencies. | ||
#Pipfile.lock | ||
|
||
# celery beat schedule file | ||
celerybeat-schedule | ||
|
||
# SageMath parsed files | ||
*.sage.py | ||
|
||
# Spyder project settings | ||
.spyderproject | ||
.spyproject | ||
|
||
# Rope project settings | ||
.ropeproject | ||
|
||
# Mr Developer | ||
.mr.developer.cfg | ||
.project | ||
.pydevproject | ||
|
||
# mkdocs documentation | ||
/site | ||
|
||
# mypy | ||
.mypy_cache/ | ||
.dmypy.json | ||
dmypy.json | ||
|
||
# Pyre type checker | ||
.pyre/ | ||
|
||
### VisualStudioCode ### | ||
.vscode | ||
|
||
/build/ | ||
/dist/ | ||
### VisualStudioCode Patch ### | ||
# Ignore all local history of files | ||
.history | ||
|
||
docs | ||
# End of https://www.gitignore.io/api/python,intellij+all,visualstudiocode |
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
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,3 @@ | ||
from .graphqlview import GraphQLView | ||
|
||
__all__ = ["GraphQLView"] |
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,151 @@ | ||
from functools import partial | ||
|
||
from flask import Response, request | ||
from flask.views import View | ||
from graphql.error import GraphQLError | ||
from graphql.type.schema import GraphQLSchema | ||
|
||
from graphql_server import ( | ||
HttpQueryError, | ||
encode_execution_results, | ||
format_error_default, | ||
json_encode, | ||
load_json_body, | ||
run_http_query, | ||
) | ||
|
||
from .render_graphiql import render_graphiql | ||
|
||
|
||
class GraphQLView(View): | ||
schema = None | ||
executor = None | ||
root_value = None | ||
pretty = False | ||
graphiql = False | ||
graphiql_version = None | ||
graphiql_template = None | ||
graphiql_html_title = None | ||
middleware = None | ||
batch = False | ||
|
||
methods = ["GET", "POST", "PUT", "DELETE"] | ||
|
||
def __init__(self, **kwargs): | ||
super(GraphQLView, self).__init__() | ||
for key, value in kwargs.items(): | ||
if hasattr(self, key): | ||
setattr(self, key, value) | ||
|
||
assert isinstance( | ||
self.schema, GraphQLSchema | ||
), "A Schema is required to be provided to GraphQLView." | ||
|
||
# noinspection PyUnusedLocal | ||
def get_root_value(self): | ||
return self.root_value | ||
|
||
def get_context_value(self): | ||
return request | ||
|
||
def get_middleware(self): | ||
return self.middleware | ||
|
||
def get_executor(self): | ||
return self.executor | ||
|
||
def render_graphiql(self, params, result): | ||
return render_graphiql( | ||
params=params, | ||
result=result, | ||
graphiql_version=self.graphiql_version, | ||
graphiql_template=self.graphiql_template, | ||
graphiql_html_title=self.graphiql_html_title, | ||
) | ||
|
||
format_error = staticmethod(format_error_default) | ||
encode = staticmethod(json_encode) | ||
|
||
def dispatch_request(self): | ||
try: | ||
request_method = request.method.lower() | ||
data = self.parse_body() | ||
|
||
show_graphiql = request_method == "get" and self.should_display_graphiql() | ||
catch = show_graphiql | ||
|
||
pretty = self.pretty or show_graphiql or request.args.get("pretty") | ||
|
||
extra_options = {} | ||
executor = self.get_executor() | ||
if executor: | ||
# We only include it optionally since | ||
# executor is not a valid argument in all backends | ||
extra_options["executor"] = executor | ||
|
||
execution_results, all_params = run_http_query( | ||
self.schema, | ||
request_method, | ||
data, | ||
query_data=request.args, | ||
batch_enabled=self.batch, | ||
catch=catch, | ||
# Execute options | ||
root_value=self.get_root_value(), | ||
context_value=self.get_context_value(), | ||
middleware=self.get_middleware(), | ||
**extra_options | ||
) | ||
result, status_code = encode_execution_results( | ||
execution_results, | ||
is_batch=isinstance(data, list), | ||
format_error=self.format_error, | ||
encode=partial(self.encode, pretty=pretty), | ||
) | ||
|
||
if show_graphiql: | ||
return self.render_graphiql(params=all_params[0], result=result) | ||
|
||
return Response(result, status=status_code, content_type="application/json") | ||
|
||
except HttpQueryError as e: | ||
parsed_error = GraphQLError(e.message) | ||
return Response( | ||
self.encode(dict(errors=[self.format_error(parsed_error)])), | ||
status=e.status_code, | ||
headers=e.headers, | ||
content_type="application/json", | ||
) | ||
|
||
# Flask | ||
def parse_body(self): | ||
# We use mimetype here since we don't need the other | ||
# information provided by content_type | ||
content_type = request.mimetype | ||
if content_type == "application/graphql": | ||
return {"query": request.data.decode("utf8")} | ||
|
||
elif content_type == "application/json": | ||
return load_json_body(request.data.decode("utf8")) | ||
|
||
elif content_type in ( | ||
"application/x-www-form-urlencoded", | ||
"multipart/form-data", | ||
): | ||
return request.form | ||
|
||
return {} | ||
|
||
def should_display_graphiql(self): | ||
if not self.graphiql or "raw" in request.args: | ||
return False | ||
|
||
return self.request_wants_html() | ||
|
||
def request_wants_html(self): | ||
best = request.accept_mimetypes.best_match(["application/json", "text/html"]) | ||
return ( | ||
best == "text/html" | ||
and request.accept_mimetypes[best] | ||
> request.accept_mimetypes["application/json"] | ||
) |
Oops, something went wrong.