From 4a09ea20f6a0f329004a7fa0f16c97fd180d4db1 Mon Sep 17 00:00:00 2001 From: Syrus Akbary Date: Mon, 20 Mar 2017 01:03:06 -0700 Subject: [PATCH 01/21] Improved GraphQL Server code --- graphql_server/__init__.py | 78 +++++++++++++++++++------------------- 1 file changed, 38 insertions(+), 40 deletions(-) diff --git a/graphql_server/__init__.py b/graphql_server/__init__.py index 1e29aba..09c407f 100644 --- a/graphql_server/__init__.py +++ b/graphql_server/__init__.py @@ -10,7 +10,6 @@ from graphql.type.schema import GraphQLSchema from graphql.utils.get_operation_ast import get_operation_ast - from .error import HttpQueryError @@ -29,57 +28,56 @@ def default_format_error(error): return {'message': six.text_type(error)} - def run_http_query(schema, request_method, data, query_data=None, batch_enabled=False, catch=None, **execute_options): - if request_method not in ('get', 'post'): - raise HttpQueryError( - 405, - 'GraphQL only supports GET and POST requests.', - headers={ - 'Allow': 'GET, POST' - } - ) + if request_method not in ('get', 'post'): + raise HttpQueryError( + 405, + 'GraphQL only supports GET and POST requests.', + headers={ + 'Allow': 'GET, POST' + } + ) - is_batch = isinstance(data, list) + is_batch = isinstance(data, list) - is_get_request = request_method == 'get' - allow_only_query = is_get_request + is_get_request = request_method == 'get' + allow_only_query = is_get_request - if not is_batch: - if not isinstance(data, dict): - raise HttpQueryError( - 400, - 'GraphQL params should be a dict. Received {}.'.format(data) - ) - data = [data] - elif not batch_enabled: + if not is_batch: + if not isinstance(data, dict): raise HttpQueryError( 400, - 'Batch GraphQL requests are not enabled.' + 'GraphQL params should be a dict. Received {}.'.format(data) ) + data = [data] + elif not batch_enabled: + raise HttpQueryError( + 400, + 'Batch GraphQL requests are not enabled.' + ) - if not data: - raise HttpQueryError( - 400, - 'Received an empty list in the batch request.' - ) + if not data: + raise HttpQueryError( + 400, + 'Received an empty list in the batch request.' + ) - extra_data = {} - # If is a batch request, we don't consume the data from the query - if not is_batch: - extra_data = query_data or {} + extra_data = {} + # If is a batch request, we don't consume the data from the query + if not is_batch: + extra_data = query_data or {} - all_params = [get_graphql_params(entry, extra_data) for entry in data] + all_params = [get_graphql_params(entry, extra_data) for entry in data] - responses = [get_response( - schema, - params, - catch, - allow_only_query, - **execute_options - ) for params in all_params] + responses = [get_response( + schema, + params, + catch, + allow_only_query, + **execute_options + ) for params in all_params] - return responses, all_params + return responses, all_params def encode_execution_results(execution_results, format_error, is_batch, encode): From d728f808ab9e43f26362eedba6350efa948cb204 Mon Sep 17 00:00:00 2001 From: Syrus Akbary Date: Wed, 22 Mar 2017 20:14:37 -0700 Subject: [PATCH 02/21] Improved catch and removed base context from the GraphQLView --- flask_graphql/graphqlview.py | 7 ++----- graphql_server/__init__.py | 13 +++++++------ tests/test_graphqlview.py | 2 +- 3 files changed, 10 insertions(+), 12 deletions(-) diff --git a/flask_graphql/graphqlview.py b/flask_graphql/graphqlview.py index 038938f..bcb4a11 100644 --- a/flask_graphql/graphqlview.py +++ b/flask_graphql/graphqlview.py @@ -15,7 +15,6 @@ class GraphQLView(View): schema = None executor = None root_value = None - context = None pretty = False graphiql = False graphiql_version = None @@ -38,8 +37,6 @@ def get_root_value(self): return self.root_value def get_context(self): - if self.context is not None: - return self.context return request def get_middleware(self): @@ -65,7 +62,7 @@ def dispatch_request(self): data = self.parse_body() show_graphiql = request_method == 'get' and self.should_display_graphiql() - catch = HttpQueryError if show_graphiql else None + catch = show_graphiql pretty = self.pretty or show_graphiql or request.args.get('pretty') @@ -97,8 +94,8 @@ def dispatch_request(self): ) return Response( + result, status=status_code, - response=result, content_type='application/json' ) diff --git a/graphql_server/__init__.py b/graphql_server/__init__.py index 09c407f..47fa8c3 100644 --- a/graphql_server/__init__.py +++ b/graphql_server/__init__.py @@ -1,5 +1,5 @@ import json -from collections import namedtuple +from collections import namedtuple, MutableMapping import six from promise import Promise @@ -28,7 +28,7 @@ def default_format_error(error): return {'message': six.text_type(error)} -def run_http_query(schema, request_method, data, query_data=None, batch_enabled=False, catch=None, **execute_options): +def run_http_query(schema, request_method, data, query_data=None, batch_enabled=False, catch=False, **execute_options): if request_method not in ('get', 'post'): raise HttpQueryError( 405, @@ -37,14 +37,17 @@ def run_http_query(schema, request_method, data, query_data=None, batch_enabled= 'Allow': 'GET, POST' } ) - + if catch: + catch = HttpQueryError + else: + catch = SkipException is_batch = isinstance(data, list) is_get_request = request_method == 'get' allow_only_query = is_get_request if not is_batch: - if not isinstance(data, dict): + if not isinstance(data, (dict, MutableMapping)): raise HttpQueryError( 400, 'GraphQL params should be a dict. Received {}.'.format(data) @@ -124,8 +127,6 @@ def get_graphql_params(data, query_data): def get_response(schema, params, catch=None, allow_only_query=False, **kwargs): - if catch is None: - catch = SkipException try: execution_result = execute_graphql_request( schema, diff --git a/tests/test_graphqlview.py b/tests/test_graphqlview.py index 8461d3c..5f53dfe 100644 --- a/tests/test_graphqlview.py +++ b/tests/test_graphqlview.py @@ -438,7 +438,7 @@ def test_passes_request_into_request_context(client): } -@pytest.mark.parametrize('app', [create_app(context="CUSTOM CONTEXT")]) +@pytest.mark.parametrize('app', [create_app(get_context=lambda:"CUSTOM CONTEXT")]) def test_supports_pretty_printing(client): response = client.get(url_string(query='{context}')) From 973d10f1823cd690a63995707ce669d5793e79e0 Mon Sep 17 00:00:00 2001 From: Syrus Akbary Date: Wed, 22 Mar 2017 21:30:27 -0700 Subject: [PATCH 03/21] Removed graphql-server to use the core package --- graphql_server/__init__.py | 211 ------------------------------------- graphql_server/error.py | 7 -- setup.py | 6 +- tox.ini | 1 + 4 files changed, 6 insertions(+), 219 deletions(-) delete mode 100644 graphql_server/__init__.py delete mode 100644 graphql_server/error.py diff --git a/graphql_server/__init__.py b/graphql_server/__init__.py deleted file mode 100644 index 47fa8c3..0000000 --- a/graphql_server/__init__.py +++ /dev/null @@ -1,211 +0,0 @@ -import json -from collections import namedtuple, MutableMapping - -import six -from promise import Promise -from graphql import Source, execute, parse, validate -from graphql.error import format_error as format_graphql_error -from graphql.error import GraphQLError -from graphql.execution import ExecutionResult -from graphql.type.schema import GraphQLSchema -from graphql.utils.get_operation_ast import get_operation_ast - -from .error import HttpQueryError - - -class SkipException(Exception): - pass - - -GraphQLParams = namedtuple('GraphQLParams', 'query,variables,operation_name,id') -GraphQLResponse = namedtuple('GraphQLResponse', 'result,status_code') - - -def default_format_error(error): - if isinstance(error, GraphQLError): - return format_graphql_error(error) - - return {'message': six.text_type(error)} - - -def run_http_query(schema, request_method, data, query_data=None, batch_enabled=False, catch=False, **execute_options): - if request_method not in ('get', 'post'): - raise HttpQueryError( - 405, - 'GraphQL only supports GET and POST requests.', - headers={ - 'Allow': 'GET, POST' - } - ) - if catch: - catch = HttpQueryError - else: - catch = SkipException - is_batch = isinstance(data, list) - - is_get_request = request_method == 'get' - allow_only_query = is_get_request - - if not is_batch: - if not isinstance(data, (dict, MutableMapping)): - raise HttpQueryError( - 400, - 'GraphQL params should be a dict. Received {}.'.format(data) - ) - data = [data] - elif not batch_enabled: - raise HttpQueryError( - 400, - 'Batch GraphQL requests are not enabled.' - ) - - if not data: - raise HttpQueryError( - 400, - 'Received an empty list in the batch request.' - ) - - extra_data = {} - # If is a batch request, we don't consume the data from the query - if not is_batch: - extra_data = query_data or {} - - all_params = [get_graphql_params(entry, extra_data) for entry in data] - - responses = [get_response( - schema, - params, - catch, - allow_only_query, - **execute_options - ) for params in all_params] - - return responses, all_params - - -def encode_execution_results(execution_results, format_error, is_batch, encode): - responses = [ - format_execution_result(execution_result, format_error) - for execution_result in execution_results - ] - result, status_codes = zip(*responses) - status_code = max(status_codes) - - if not is_batch: - result = result[0] - - return encode(result), status_code - - -def json_encode(data, pretty=False): - if not pretty: - return json.dumps(data, separators=(',', ':')) - - return json.dumps( - data, - indent=2, - separators=(',', ': ') - ) - - -def load_json_variables(variables): - if variables and isinstance(variables, six.text_type): - try: - return json.loads(variables) - except: - raise HttpQueryError(400, 'Variables are invalid JSON.') - return variables - - -def get_graphql_params(data, query_data): - query = data.get('query') or query_data.get('query') - variables = data.get('variables') or query_data.get('variables') - id = data.get('id') - operation_name = data.get('operationName') or query_data.get('operationName') - - return GraphQLParams(query, load_json_variables(variables), operation_name, id) - - -def get_response(schema, params, catch=None, allow_only_query=False, **kwargs): - try: - execution_result = execute_graphql_request( - schema, - params, - allow_only_query, - **kwargs - ) - except catch: - return None - - return execution_result - - -def format_execution_result(execution_result, format_error): - status_code = 200 - - if execution_result: - response = {} - - if execution_result.errors: - response['errors'] = [format_error(e) for e in execution_result.errors] - - if execution_result.invalid: - status_code = 400 - else: - status_code = 200 - response['data'] = execution_result.data - - else: - response = None - - return GraphQLResponse(response, status_code) - - -def execute_graphql_request(schema, params, allow_only_query=False, **kwargs): - if not params.query: - raise HttpQueryError(400, 'Must provide query string.') - - try: - source = Source(params.query, name='GraphQL request') - ast = parse(source) - validation_errors = validate(schema, ast) - if validation_errors: - return ExecutionResult( - errors=validation_errors, - invalid=True, - ) - except Exception as e: - return ExecutionResult(errors=[e], invalid=True) - - if allow_only_query: - operation_ast = get_operation_ast(ast, params.operation_name) - if operation_ast and operation_ast.operation != 'query': - raise HttpQueryError( - 405, - 'Can only perform a {} operation from a POST request.'.format(operation_ast.operation), - headers={ - 'Allow': ['POST'], - } - ) - - try: - return execute( - schema, - ast, - operation_name=params.operation_name, - variable_values=params.variables, - **kwargs - ) - - except Exception as e: - return ExecutionResult(errors=[e], invalid=True) - - -def load_json_body(data): - try: - return json.loads(data) - except: - raise HttpQueryError( - 400, - 'POST body sent invalid JSON.' - ) diff --git a/graphql_server/error.py b/graphql_server/error.py deleted file mode 100644 index f9459b7..0000000 --- a/graphql_server/error.py +++ /dev/null @@ -1,7 +0,0 @@ -class HttpQueryError(Exception): - def __init__(self, status_code, message=None, is_graphql_error=False, headers=None): - self.status_code = status_code - self.message = message - self.is_graphql_error = is_graphql_error - self.headers = headers - super(HttpQueryError, self).__init__(message) diff --git a/setup.py b/setup.py index 25e8379..538317b 100644 --- a/setup.py +++ b/setup.py @@ -1,6 +1,10 @@ from setuptools import setup, find_packages -required_packages = ['graphql-core>=1.0', 'flask>=0.7.0'] +required_packages = [ + 'graphql-core>=1.0', + 'flask>=0.7.0', + 'graphql-server-core>=1.0.dev' +] setup( name='Flask-GraphQL', diff --git a/tox.ini b/tox.ini index 9d79799..407f02c 100644 --- a/tox.ini +++ b/tox.ini @@ -9,6 +9,7 @@ deps = pytest>=2.7.2 pytest-flask>=0.10.0 graphql-core>=1.0 + graphql-server-core>=1.0.dev Flask>=0.10.0 pytest-cov commands = From 6e2d7c902e6260e3364082a5650068e848fd8bab Mon Sep 17 00:00:00 2001 From: David Baumgold Date: Mon, 14 Aug 2017 15:57:33 -0700 Subject: [PATCH 04/21] Customizable HTML title in GraphiQL --- flask_graphql/graphqlview.py | 2 ++ flask_graphql/render_graphiql.py | 5 ++++- tests/test_graphiqlview.py | 11 +++++++++++ 3 files changed, 17 insertions(+), 1 deletion(-) diff --git a/flask_graphql/graphqlview.py b/flask_graphql/graphqlview.py index bcb4a11..ceca3eb 100644 --- a/flask_graphql/graphqlview.py +++ b/flask_graphql/graphqlview.py @@ -19,6 +19,7 @@ class GraphQLView(View): graphiql = False graphiql_version = None graphiql_template = None + graphiql_html_title = None middleware = None batch = False @@ -51,6 +52,7 @@ def render_graphiql(self, params, result): result=result, graphiql_version=self.graphiql_version, graphiql_template=self.graphiql_template, + graphiql_html_title=self.graphiql_html_title, ) format_error = staticmethod(default_format_error) diff --git a/flask_graphql/render_graphiql.py b/flask_graphql/render_graphiql.py index 1ecfe8a..6541f82 100644 --- a/flask_graphql/render_graphiql.py +++ b/flask_graphql/render_graphiql.py @@ -12,6 +12,7 @@ + {{graphiql_html_title|default("GraphiQL", true)}} - + - + - - - - - - - -''' - - -def render_graphiql(params, result, graphiql_version=None, - graphiql_template=None, graphiql_html_title=None): - graphiql_version = graphiql_version or GRAPHIQL_VERSION - template = graphiql_template or TEMPLATE - - return render_template_string( - template, - graphiql_version=graphiql_version, - graphiql_html_title=graphiql_html_title, - result=result, - params=params - ) diff --git a/setup.cfg b/setup.cfg index 34e51b5..b6ff204 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,9 +1,9 @@ [flake8] exclude = tests,scripts,setup.py,docs -max-line-length = 160 +max-line-length = 88 [isort] known_first_party=graphql [tool:pytest] -norecursedirs = venv .tox .cache +norecursedirs = venv .venv .tox .git .cache .mypy_cache .pytest_cache diff --git a/setup.py b/setup.py index a851be7..9a7c1cc 100644 --- a/setup.py +++ b/setup.py @@ -1,28 +1,28 @@ from setuptools import setup, find_packages install_requires = [ - "flask>=0.7.0", - "graphql-core>=2.3,<3", - "graphql-server-core>=1.1,<2", + "graphql-server>=3.0.0b1", ] tests_requires = [ - 'pytest>=2.7.2', - 'pytest-cov==2.8.1', - 'pytest-flask>=0.10.0', + "pytest>=5.4,<5.5", + "pytest-cov>=2.8,<3", ] dev_requires = [ - 'flake8==3.7.9', - 'isort<4.0.0', - 'check-manifest>=0.40,<1', + "flake8>=3.7,<4", + "isort>=4,<5", + "check-manifest>=0.40,<1", ] + tests_requires +with open("README.md", encoding="utf-8") as readme_file: + readme = readme_file.read() + setup( name="Flask-GraphQL", version="2.0.1", description="Adds GraphQL support to your Flask application", - long_description=open("README.md").read(), + long_description=readme, long_description_content_type="text/markdown", url="https://github.com/graphql-python/flask-graphql", download_url="https://github.com/graphql-python/flask-graphql/releases", @@ -33,13 +33,9 @@ "Development Status :: 5 - Production/Stable", "Intended Audience :: Developers", "Topic :: Software Development :: Libraries", - "Programming Language :: Python :: 2", - "Programming Language :: Python :: 2.7", - "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.5", "Programming Language :: Python :: 3.6", "Programming Language :: Python :: 3.7", - "Programming Language :: Python :: Implementation :: PyPy", + "Programming Language :: Python :: 3.8", "License :: OSI Approved :: MIT License", ], keywords="api graphql protocol rest flask", diff --git a/tests/app.py b/tests/app.py index 0d38502..84f1d23 100644 --- a/tests/app.py +++ b/tests/app.py @@ -1,19 +1,18 @@ from flask import Flask + from flask_graphql import GraphQLView -from .schema import Schema -from graphql import GraphQLCachedBackend -# from quiver.backend import GraphQLQuiverBackend +from tests.schema import Schema -def create_app(path='/graphql', **kwargs): - # backend = GraphQLCachedBackend(GraphQLQuiverBackend({"async_framework": "PROMISE"})) - backend = None +def create_app(path="/graphql", **kwargs): app = Flask(__name__) app.debug = True - app.add_url_rule(path, view_func=GraphQLView.as_view('graphql', schema=Schema, backend=backend, **kwargs)) + app.add_url_rule( + path, view_func=GraphQLView.as_view("graphql", schema=Schema, **kwargs) + ) return app -if __name__ == '__main__': +if __name__ == "__main__": app = create_app(graphiql=True) app.run() diff --git a/tests/schema.py b/tests/schema.py index f841672..fdb5c9a 100644 --- a/tests/schema.py +++ b/tests/schema.py @@ -1,4 +1,5 @@ -from graphql.type.definition import GraphQLArgument, GraphQLField, GraphQLNonNull, GraphQLObjectType +from graphql.type.definition import (GraphQLArgument, GraphQLField, + GraphQLNonNull, GraphQLObjectType) from graphql.type.scalars import GraphQLString from graphql.type.schema import GraphQLSchema @@ -8,31 +9,39 @@ def resolve_raises(*_): QueryRootType = GraphQLObjectType( - name='QueryRoot', + name="QueryRoot", fields={ - 'thrower': GraphQLField(GraphQLNonNull(GraphQLString), resolver=resolve_raises), - 'request': GraphQLField(GraphQLNonNull(GraphQLString), - resolver=lambda obj, info: info.context.args.get('q')), - 'context': GraphQLField(GraphQLNonNull(GraphQLString), - resolver=lambda obj, info: info.context), - 'test': GraphQLField( - type=GraphQLString, - args={ - 'who': GraphQLArgument(GraphQLString) - }, - resolver=lambda obj, info, who='World': 'Hello %s' % who - ) - } + "thrower": GraphQLField(GraphQLNonNull(GraphQLString), resolve=resolve_raises), + "request": GraphQLField( + GraphQLNonNull(GraphQLString), + resolve=lambda obj, info: info.context["request"].args.get("q"), + ), + "context": GraphQLField( + GraphQLObjectType( + name="context", + fields={ + "session": GraphQLField(GraphQLString), + "request": GraphQLField( + GraphQLNonNull(GraphQLString), + resolve=lambda obj, info: info.context["request"], + ), + }, + ), + resolve=lambda obj, info: info.context, + ), + "test": GraphQLField( + type_=GraphQLString, + args={"who": GraphQLArgument(GraphQLString)}, + resolve=lambda obj, info, who="World": "Hello %s" % who, + ), + }, ) MutationRootType = GraphQLObjectType( - name='MutationRoot', + name="MutationRoot", fields={ - 'writeTest': GraphQLField( - type=QueryRootType, - resolver=lambda *_: QueryRootType - ) - } + "writeTest": GraphQLField(type_=QueryRootType, resolve=lambda *_: QueryRootType) + }, ) Schema = GraphQLSchema(QueryRootType, MutationRootType) diff --git a/tests/test_graphiqlview.py b/tests/test_graphiqlview.py index aa92202..4a55710 100644 --- a/tests/test_graphiqlview.py +++ b/tests/test_graphiqlview.py @@ -1,7 +1,7 @@ import pytest +from flask import url_for from .app import create_app -from flask import url_for @pytest.fixture @@ -22,33 +22,39 @@ def client(app): def test_graphiql_is_enabled(app, client): with app.test_request_context(): - response = client.get(url_for('graphql', externals=False), headers={'Accept': 'text/html'}) + response = client.get( + url_for("graphql", externals=False), headers={"Accept": "text/html"} + ) assert response.status_code == 200 def test_graphiql_renders_pretty(app, client): with app.test_request_context(): - response = client.get(url_for('graphql', query='{test}'), headers={'Accept': 'text/html'}) + response = client.get( + url_for("graphql", query="{test}"), headers={"Accept": "text/html"} + ) assert response.status_code == 200 pretty_response = ( - '{\n' + "{\n" ' "data": {\n' ' "test": "Hello World"\n' - ' }\n' - '}' - ).replace("\"", "\\\"").replace("\n", "\\n") + " }\n" + "}".replace('"', '\\"').replace("\n", "\\n") + ) - assert pretty_response in response.data.decode('utf-8') + assert pretty_response in response.data.decode("utf-8") def test_graphiql_default_title(app, client): with app.test_request_context(): - response = client.get(url_for('graphql'), headers={'Accept': 'text/html'}) - assert 'GraphiQL' in response.data.decode('utf-8') + response = client.get(url_for("graphql"), headers={"Accept": "text/html"}) + assert "GraphiQL" in response.data.decode("utf-8") -@pytest.mark.parametrize('app', [create_app(graphiql=True, graphiql_html_title="Awesome")]) +@pytest.mark.parametrize( + "app", [create_app(graphiql=True, graphiql_html_title="Awesome")] +) def test_graphiql_custom_title(app, client): with app.test_request_context(): - response = client.get(url_for('graphql'), headers={'Accept': 'text/html'}) - assert 'Awesome' in response.data.decode('utf-8') + response = client.get(url_for("graphql"), headers={"Accept": "text/html"}) + assert "Awesome" in response.data.decode("utf-8") diff --git a/tests/test_graphqlview.py b/tests/test_graphqlview.py index 3e49e55..961a8e0 100644 --- a/tests/test_graphqlview.py +++ b/tests/test_graphqlview.py @@ -1,18 +1,11 @@ -import pytest import json +from io import StringIO +from urllib.parse import urlencode -try: - from StringIO import StringIO -except ImportError: - from io import StringIO - -try: - from urllib import urlencode -except ImportError: - from urllib.parse import urlencode +import pytest +from flask import url_for from .app import create_app -from flask import url_for @pytest.fixture @@ -33,10 +26,10 @@ def client(app): def url_string(app, **url_params): with app.test_request_context(): - string = url_for('graphql') + string = url_for("graphql") if url_params: - string += '?' + urlencode(url_params) + string += "?" + urlencode(url_params) return string @@ -54,357 +47,354 @@ def json_dump_kwarg_list(**kwargs): def test_allows_get_with_query_param(app, client): - response = client.get(url_string(app, query='{test}')) + response = client.get(url_string(app, query="{test}")) assert response.status_code == 200 - assert response_json(response) == { - 'data': {'test': "Hello World"} - } + assert response_json(response) == {"data": {"test": "Hello World"}} def test_allows_get_with_variable_values(app, client): - response = client.get(url_string( - app, - query='query helloWho($who: String){ test(who: $who) }', - variables=json.dumps({'who': "Dolly"}) - )) + response = client.get( + url_string( + app, + query="query helloWho($who: String){ test(who: $who) }", + variables=json.dumps({"who": "Dolly"}), + ) + ) assert response.status_code == 200 - assert response_json(response) == { - 'data': {'test': "Hello Dolly"} - } + assert response_json(response) == {"data": {"test": "Hello Dolly"}} def test_allows_get_with_operation_name(app, client): - response = client.get(url_string( - app, - query=''' + response = client.get( + url_string( + app, + query=""" query helloYou { test(who: "You"), ...shared } query helloWorld { test(who: "World"), ...shared } query helloDolly { test(who: "Dolly"), ...shared } fragment shared on QueryRoot { shared: test(who: "Everyone") } - ''', - operationName='helloWorld' - )) + """, + operationName="helloWorld", + ) + ) assert response.status_code == 200 assert response_json(response) == { - 'data': { - 'test': 'Hello World', - 'shared': 'Hello Everyone' - } + "data": {"test": "Hello World", "shared": "Hello Everyone"} } def test_reports_validation_errors(app, client): - response = client.get(url_string( - app, - query='{ test, unknownOne, unknownTwo }' - )) + response = client.get(url_string(app, query="{ test, unknownOne, unknownTwo }")) assert response.status_code == 400 assert response_json(response) == { - 'errors': [ + "errors": [ { - 'message': 'Cannot query field "unknownOne" on type "QueryRoot".', - 'locations': [{'line': 1, 'column': 9}] + "message": "Cannot query field 'unknownOne' on type 'QueryRoot'.", + "locations": [{"line": 1, "column": 9}], + "path": None, }, { - 'message': 'Cannot query field "unknownTwo" on type "QueryRoot".', - 'locations': [{'line': 1, 'column': 21}] - } + "message": "Cannot query field 'unknownTwo' on type 'QueryRoot'.", + "locations": [{"line": 1, "column": 21}], + "path": None, + }, ] } def test_errors_when_missing_operation_name(app, client): - response = client.get(url_string( - app, - query=''' + response = client.get( + url_string( + app, + query=""" query TestQuery { test } mutation TestMutation { writeTest { test } } - ''' - )) + """, + ) + ) assert response.status_code == 400 assert response_json(response) == { - 'errors': [ + "errors": [ { - 'message': 'Must provide operation name if query contains multiple operations.' + "message": "Must provide operation name if query contains multiple operations.", # noqa: E501 + "locations": None, + "path": None, } ] } def test_errors_when_sending_a_mutation_via_get(app, client): - response = client.get(url_string( - app, - query=''' + response = client.get( + url_string( + app, + query=""" mutation TestMutation { writeTest { test } } - ''' - )) + """, + ) + ) assert response.status_code == 405 assert response_json(response) == { - 'errors': [ + "errors": [ { - 'message': 'Can only perform a mutation operation from a POST request.' + "message": "Can only perform a mutation operation from a POST request.", + "locations": None, + "path": None, } ] } def test_errors_when_selecting_a_mutation_within_a_get(app, client): - response = client.get(url_string( - app, - query=''' + response = client.get( + url_string( + app, + query=""" query TestQuery { test } mutation TestMutation { writeTest { test } } - ''', - operationName='TestMutation' - )) + """, + operationName="TestMutation", + ) + ) assert response.status_code == 405 assert response_json(response) == { - 'errors': [ + "errors": [ { - 'message': 'Can only perform a mutation operation from a POST request.' + "message": "Can only perform a mutation operation from a POST request.", + "locations": None, + "path": None, } ] } def test_allows_mutation_to_exist_within_a_get(app, client): - response = client.get(url_string( - app, - query=''' + response = client.get( + url_string( + app, + query=""" query TestQuery { test } mutation TestMutation { writeTest { test } } - ''', - operationName='TestQuery' - )) + """, + operationName="TestQuery", + ) + ) assert response.status_code == 200 - assert response_json(response) == { - 'data': {'test': "Hello World"} - } + assert response_json(response) == {"data": {"test": "Hello World"}} def test_allows_post_with_json_encoding(app, client): - response = client.post(url_string(app), data=json_dump_kwarg(query='{test}'), content_type='application/json') + response = client.post( + url_string(app), + data=json_dump_kwarg(query="{test}"), + content_type="application/json", + ) assert response.status_code == 200 - assert response_json(response) == { - 'data': {'test': "Hello World"} - } + assert response_json(response) == {"data": {"test": "Hello World"}} def test_allows_sending_a_mutation_via_post(app, client): - response = client.post(url_string(app), data=json_dump_kwarg(query='mutation TestMutation { writeTest { test } }'), content_type='application/json') + response = client.post( + url_string(app), + data=json_dump_kwarg(query="mutation TestMutation { writeTest { test } }"), + content_type="application/json", + ) assert response.status_code == 200 - assert response_json(response) == { - 'data': {'writeTest': {'test': 'Hello World'}} - } + assert response_json(response) == {"data": {"writeTest": {"test": "Hello World"}}} def test_allows_post_with_url_encoding(app, client): - response = client.post(url_string(app), data=urlencode(dict(query='{test}')), content_type='application/x-www-form-urlencoded') + response = client.post( + url_string(app), + data=urlencode(dict(query="{test}")), + content_type="application/x-www-form-urlencoded", + ) assert response.status_code == 200 - assert response_json(response) == { - 'data': {'test': "Hello World"} - } - - -# def test_benchmark(client, benchmark): -# url = url_string() -# data = urlencode(dict(query='{test}')) -# def fun(): -# return client.post(url_string(), data=data, content_type='application/x-www-form-urlencoded') - -# response = benchmark(fun) -# assert response.status_code == 200 -# assert response_json(response) == { -# 'data': {'test': "Hello World"} -# } + assert response_json(response) == {"data": {"test": "Hello World"}} def test_supports_post_json_query_with_string_variables(app, client): - response = client.post(url_string(app), data=json_dump_kwarg( - query='query helloWho($who: String){ test(who: $who) }', - variables=json.dumps({'who': "Dolly"}) - ), content_type='application/json') + response = client.post( + url_string(app), + data=json_dump_kwarg( + query="query helloWho($who: String){ test(who: $who) }", + variables=json.dumps({"who": "Dolly"}), + ), + content_type="application/json", + ) assert response.status_code == 200 - assert response_json(response) == { - 'data': {'test': "Hello Dolly"} - } + assert response_json(response) == {"data": {"test": "Hello Dolly"}} def test_supports_post_json_query_with_json_variables(app, client): - response = client.post(url_string(app), data=json_dump_kwarg( - query='query helloWho($who: String){ test(who: $who) }', - variables={'who': "Dolly"} - ), content_type='application/json') + response = client.post( + url_string(app), + data=json_dump_kwarg( + query="query helloWho($who: String){ test(who: $who) }", + variables={"who": "Dolly"}, + ), + content_type="application/json", + ) assert response.status_code == 200 - assert response_json(response) == { - 'data': {'test': "Hello Dolly"} - } + assert response_json(response) == {"data": {"test": "Hello Dolly"}} def test_supports_post_url_encoded_query_with_string_variables(app, client): - response = client.post(url_string(app), data=urlencode(dict( - query='query helloWho($who: String){ test(who: $who) }', - variables=json.dumps({'who': "Dolly"}) - )), content_type='application/x-www-form-urlencoded') + response = client.post( + url_string(app), + data=urlencode( + dict( + query="query helloWho($who: String){ test(who: $who) }", + variables=json.dumps({"who": "Dolly"}), + ) + ), + content_type="application/x-www-form-urlencoded", + ) assert response.status_code == 200 - assert response_json(response) == { - 'data': {'test': "Hello Dolly"} - } + assert response_json(response) == {"data": {"test": "Hello Dolly"}} def test_supports_post_json_quey_with_get_variable_values(app, client): - response = client.post(url_string( - app, - variables=json.dumps({'who': "Dolly"}) - ), data=json_dump_kwarg( - query='query helloWho($who: String){ test(who: $who) }', - ), content_type='application/json') + response = client.post( + url_string(app, variables=json.dumps({"who": "Dolly"})), + data=json_dump_kwarg(query="query helloWho($who: String){ test(who: $who) }",), + content_type="application/json", + ) assert response.status_code == 200 - assert response_json(response) == { - 'data': {'test': "Hello Dolly"} - } + assert response_json(response) == {"data": {"test": "Hello Dolly"}} def test_post_url_encoded_query_with_get_variable_values(app, client): - response = client.post(url_string( - app, - variables=json.dumps({'who': "Dolly"}) - ), data=urlencode(dict( - query='query helloWho($who: String){ test(who: $who) }', - )), content_type='application/x-www-form-urlencoded') + response = client.post( + url_string(app, variables=json.dumps({"who": "Dolly"})), + data=urlencode(dict(query="query helloWho($who: String){ test(who: $who) }",)), + content_type="application/x-www-form-urlencoded", + ) assert response.status_code == 200 - assert response_json(response) == { - 'data': {'test': "Hello Dolly"} - } + assert response_json(response) == {"data": {"test": "Hello Dolly"}} def test_supports_post_raw_text_query_with_get_variable_values(app, client): - response = client.post(url_string( - app, - variables=json.dumps({'who': "Dolly"}) - ), - data='query helloWho($who: String){ test(who: $who) }', - content_type='application/graphql' + response = client.post( + url_string(app, variables=json.dumps({"who": "Dolly"})), + data="query helloWho($who: String){ test(who: $who) }", + content_type="application/graphql", ) assert response.status_code == 200 - assert response_json(response) == { - 'data': {'test': "Hello Dolly"} - } + assert response_json(response) == {"data": {"test": "Hello Dolly"}} def test_allows_post_with_operation_name(app, client): - response = client.post(url_string(app), data=json_dump_kwarg( - query=''' + response = client.post( + url_string(app), + data=json_dump_kwarg( + query=""" query helloYou { test(who: "You"), ...shared } query helloWorld { test(who: "World"), ...shared } query helloDolly { test(who: "Dolly"), ...shared } fragment shared on QueryRoot { shared: test(who: "Everyone") } - ''', - operationName='helloWorld' - ), content_type='application/json') + """, + operationName="helloWorld", + ), + content_type="application/json", + ) assert response.status_code == 200 assert response_json(response) == { - 'data': { - 'test': 'Hello World', - 'shared': 'Hello Everyone' - } + "data": {"test": "Hello World", "shared": "Hello Everyone"} } def test_allows_post_with_get_operation_name(app, client): - response = client.post(url_string( - app, - operationName='helloWorld' - ), data=''' + response = client.post( + url_string(app, operationName="helloWorld"), + data=""" query helloYou { test(who: "You"), ...shared } query helloWorld { test(who: "World"), ...shared } query helloDolly { test(who: "Dolly"), ...shared } fragment shared on QueryRoot { shared: test(who: "Everyone") } - ''', - content_type='application/graphql') + """, + content_type="application/graphql", + ) assert response.status_code == 200 assert response_json(response) == { - 'data': { - 'test': 'Hello World', - 'shared': 'Hello Everyone' - } + "data": {"test": "Hello World", "shared": "Hello Everyone"} } -@pytest.mark.parametrize('app', [create_app(pretty=True)]) +@pytest.mark.parametrize("app", [create_app(pretty=True)]) def test_supports_pretty_printing(app, client): - response = client.get(url_string(app, query='{test}')) + response = client.get(url_string(app, query="{test}")) assert response.data.decode() == ( - '{\n' - ' "data": {\n' - ' "test": "Hello World"\n' - ' }\n' - '}' + "{\n" ' "data": {\n' ' "test": "Hello World"\n' " }\n" "}" ) -@pytest.mark.parametrize('app', [create_app(pretty=False)]) +@pytest.mark.parametrize("app", [create_app(pretty=False)]) def test_not_pretty_by_default(app, client): - response = client.get(url_string(app, query='{test}')) + response = client.get(url_string(app, query="{test}")) - assert response.data.decode() == ( - '{"data":{"test":"Hello World"}}' - ) + assert response.data.decode() == '{"data":{"test":"Hello World"}}' def test_supports_pretty_printing_by_request(app, client): - response = client.get(url_string(app, query='{test}', pretty='1')) + response = client.get(url_string(app, query="{test}", pretty="1")) assert response.data.decode() == ( - '{\n' - ' "data": {\n' - ' "test": "Hello World"\n' - ' }\n' - '}' + "{\n" ' "data": {\n' ' "test": "Hello World"\n' " }\n" "}" ) def test_handles_field_errors_caught_by_graphql(app, client): - response = client.get(url_string(app, query='{thrower}')) + response = client.get(url_string(app, query="{thrower}")) assert response.status_code == 200 assert response_json(response) == { - 'data': None, - 'errors': [{'locations': [{'column': 2, 'line': 1}], 'path': ['thrower'], 'message': 'Throws!'}] + "errors": [ + { + "locations": [{"column": 2, "line": 1}], + "path": ["thrower"], + "message": "Throws!", + } + ], + "data": None, } def test_handles_syntax_errors_caught_by_graphql(app, client): - response = client.get(url_string(app, query='syntaxerror')) + response = client.get(url_string(app, query="syntaxerror")) assert response.status_code == 400 assert response_json(response) == { - 'errors': [{'locations': [{'column': 1, 'line': 1}], - 'message': 'Syntax Error GraphQL (1:1) ' - 'Unexpected Name "syntaxerror"\n\n1: syntaxerror\n ^\n'}] + "errors": [ + { + "locations": [{"column": 1, "line": 1}], + "message": "Syntax Error: Unexpected Name 'syntaxerror'.", + "path": None, + } + ] } @@ -413,162 +403,196 @@ def test_handles_errors_caused_by_a_lack_of_query(app, client): assert response.status_code == 400 assert response_json(response) == { - 'errors': [{'message': 'Must provide query string.'}] + "errors": [ + {"message": "Must provide query string.", "locations": None, "path": None} + ] } def test_handles_batch_correctly_if_is_disabled(app, client): - response = client.post(url_string(app), data='[]', content_type='application/json') + response = client.post(url_string(app), data="[]", content_type="application/json") assert response.status_code == 400 assert response_json(response) == { - 'errors': [{'message': 'Batch GraphQL requests are not enabled.'}] + "errors": [ + { + "message": "Batch GraphQL requests are not enabled.", + "locations": None, + "path": None, + } + ] } def test_handles_incomplete_json_bodies(app, client): - response = client.post(url_string(app), data='{"query":', content_type='application/json') + response = client.post( + url_string(app), data='{"query":', content_type="application/json" + ) assert response.status_code == 400 assert response_json(response) == { - 'errors': [{'message': 'POST body sent invalid JSON.'}] + "errors": [ + {"message": "POST body sent invalid JSON.", "locations": None, "path": None} + ] } def test_handles_plain_post_text(app, client): - response = client.post(url_string( - app, - variables=json.dumps({'who': "Dolly"}) - ), - data='query helloWho($who: String){ test(who: $who) }', - content_type='text/plain' + response = client.post( + url_string(app, variables=json.dumps({"who": "Dolly"})), + data="query helloWho($who: String){ test(who: $who) }", + content_type="text/plain", ) assert response.status_code == 400 assert response_json(response) == { - 'errors': [{'message': 'Must provide query string.'}] + "errors": [ + {"message": "Must provide query string.", "locations": None, "path": None} + ] } def test_handles_poorly_formed_variables(app, client): - response = client.get(url_string( - app, - query='query helloWho($who: String){ test(who: $who) }', - variables='who:You' - )) + response = client.get( + url_string( + app, + query="query helloWho($who: String){ test(who: $who) }", + variables="who:You", + ) + ) assert response.status_code == 400 assert response_json(response) == { - 'errors': [{'message': 'Variables are invalid JSON.'}] + "errors": [ + {"message": "Variables are invalid JSON.", "locations": None, "path": None} + ] } def test_handles_unsupported_http_methods(app, client): - response = client.put(url_string(app, query='{test}')) + response = client.put(url_string(app, query="{test}")) assert response.status_code == 405 - assert response.headers['Allow'] in ['GET, POST', 'HEAD, GET, POST, OPTIONS'] + assert response.headers["Allow"] in ["GET, POST", "HEAD, GET, POST, OPTIONS"] assert response_json(response) == { - 'errors': [{'message': 'GraphQL only supports GET and POST requests.'}] + "errors": [ + { + "message": "GraphQL only supports GET and POST requests.", + "locations": None, + "path": None, + } + ] } def test_passes_request_into_request_context(app, client): - response = client.get(url_string(app, query='{request}', q='testing')) + response = client.get(url_string(app, query="{request}", q="testing")) assert response.status_code == 200 - assert response_json(response) == { - 'data': { - 'request': 'testing' - } - } + assert response_json(response) == {"data": {"request": "testing"}} -@pytest.mark.parametrize('app', [create_app(get_context_value=lambda:"CUSTOM CONTEXT")]) +@pytest.mark.parametrize("app", [create_app(context={"session": "CUSTOM CONTEXT"})]) def test_passes_custom_context_into_context(app, client): - response = client.get(url_string(app, query='{context}')) + response = client.get(url_string(app, query="{context { session request }}")) assert response.status_code == 200 - assert response_json(response) == { - 'data': { - 'context': 'CUSTOM CONTEXT' - } - } + res = response_json(response) + assert "data" in res + assert "session" in res["data"]["context"] + assert "request" in res["data"]["context"] + assert "CUSTOM CONTEXT" in res["data"]["context"]["session"] + assert "Request" in res["data"]["context"]["request"] + + +@pytest.mark.parametrize("app", [create_app(context="CUSTOM CONTEXT")]) +def test_context_remapped_if_not_mapping(app, client): + response = client.get(url_string(app, query="{context { session request }}")) + + assert response.status_code == 200 + res = response_json(response) + assert "data" in res + assert "session" in res["data"]["context"] + assert "request" in res["data"]["context"] + assert "CUSTOM CONTEXT" not in res["data"]["context"]["request"] + assert "Request" in res["data"]["context"]["request"] def test_post_multipart_data(app, client): - query = 'mutation TestMutation { writeTest { test } }' + query = "mutation TestMutation { writeTest { test } }" response = client.post( url_string(app), - data={ - 'query': query, - 'file': (StringIO(), 'text1.txt'), - }, - content_type='multipart/form-data' + data={"query": query, "file": (StringIO(), "text1.txt")}, + content_type="multipart/form-data", ) assert response.status_code == 200 - assert response_json(response) == {'data': {u'writeTest': {u'test': u'Hello World'}}} + assert response_json(response) == { + "data": {u"writeTest": {u"test": u"Hello World"}} + } -@pytest.mark.parametrize('app', [create_app(batch=True)]) +@pytest.mark.parametrize("app", [create_app(batch=True)]) def test_batch_allows_post_with_json_encoding(app, client): response = client.post( url_string(app), data=json_dump_kwarg_list( # id=1, - query='{test}' + query="{test}" ), - content_type='application/json' + content_type="application/json", ) assert response.status_code == 200 - assert response_json(response) == [{ - # 'id': 1, - 'data': {'test': "Hello World"} - }] + assert response_json(response) == [ + { + # 'id': 1, + "data": {"test": "Hello World"} + } + ] -@pytest.mark.parametrize('app', [create_app(batch=True)]) +@pytest.mark.parametrize("app", [create_app(batch=True)]) def test_batch_supports_post_json_query_with_json_variables(app, client): response = client.post( url_string(app), data=json_dump_kwarg_list( # id=1, - query='query helloWho($who: String){ test(who: $who) }', - variables={'who': "Dolly"} + query="query helloWho($who: String){ test(who: $who) }", + variables={"who": "Dolly"}, ), - content_type='application/json' + content_type="application/json", ) assert response.status_code == 200 - assert response_json(response) == [{ - # 'id': 1, - 'data': {'test': "Hello Dolly"} - }] + assert response_json(response) == [ + { + # 'id': 1, + "data": {"test": "Hello Dolly"} + } + ] -@pytest.mark.parametrize('app', [create_app(batch=True)]) +@pytest.mark.parametrize("app", [create_app(batch=True)]) def test_batch_allows_post_with_operation_name(app, client): response = client.post( url_string(app), data=json_dump_kwarg_list( # id=1, - query=''' + query=""" query helloYou { test(who: "You"), ...shared } query helloWorld { test(who: "World"), ...shared } query helloDolly { test(who: "Dolly"), ...shared } fragment shared on QueryRoot { shared: test(who: "Everyone") } - ''', - operationName='helloWorld' + """, + operationName="helloWorld", ), - content_type='application/json' + content_type="application/json", ) assert response.status_code == 200 - assert response_json(response) == [{ - # 'id': 1, - 'data': { - 'test': 'Hello World', - 'shared': 'Hello Everyone' + assert response_json(response) == [ + { + # 'id': 1, + "data": {"test": "Hello World", "shared": "Hello Everyone"} } - }] + ] diff --git a/tox.ini b/tox.ini index fb4b51e..72ab365 100644 --- a/tox.ini +++ b/tox.ini @@ -1,6 +1,6 @@ [tox] envlist = - py{27,35,36,37} + py{36,37,38} flake8,import-order,manifest ; requires = tox-conda @@ -8,25 +8,25 @@ envlist = passenv = * setenv = PYTHONPATH = {toxinidir} -install_command = python -m pip install --ignore-installed {opts} {packages} +install_command = python -m pip install --pre --ignore-installed {opts} {packages} deps = -e.[test] commands = - pytest --cov=flask_graphql tests {posargs} + pytest tests --cov-report=term-missing --cov=flask_graphql {posargs} [testenv:flake8] -basepython=python3.6 +basepython=python3.8 deps = -e.[dev] commands = - flake8 flask_graphql + flake8 setup.py flask_graphql tests [testenv:import-order] -basepython=python3.6 +basepython=python3.8 deps = -e.[dev] commands = - isort --check-only flask_graphql/ -rc + isort -rc flask_graphql/ tests/ [testenv:manifest] -basepython = python3.6 +basepython = python3.8 deps = -e.[dev] commands = check-manifest -v \ No newline at end of file From dfd863dd14902d91d9cc4427b60eb337ee64134c Mon Sep 17 00:00:00 2001 From: KingDarBoja Date: Fri, 7 Aug 2020 11:31:41 -0500 Subject: [PATCH 19/21] docs: update readme with v3-beta notes --- README.md | 46 +++++++++++++++++++++++++++++++++++++++------- 1 file changed, 39 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index af4e1ac..7670f48 100644 --- a/README.md +++ b/README.md @@ -21,26 +21,58 @@ Adds GraphQL support to your Flask application. Just use the `GraphQLView` view from `flask_graphql` ```python +from flask import Flask from flask_graphql import GraphQLView -app.add_url_rule('/graphql', view_func=GraphQLView.as_view('graphql', schema=schema, graphiql=True)) +from schema import schema + +app = Flask(__name__) + +app.add_url_rule('/graphql', view_func=GraphQLView.as_view( + 'graphql', + schema=schema, + graphiql=True, +)) # Optional, for adding batch query support (used in Apollo-Client) -app.add_url_rule('/graphql/batch', view_func=GraphQLView.as_view('graphql', schema=schema, batch=True)) +app.add_url_rule('/graphql/batch', view_func=GraphQLView.as_view( + 'graphql', + schema=schema, + batch=True +)) + +if __name__ == '__main__': + app.run() ``` -This will add `/graphql` and `/graphiql` endpoints to your app. +This will add `/graphql` endpoint to your app and enable the GraphiQL IDE. + +### Special Note for Graphene v3 + +If you are using the `Schema` type of [Graphene](https://github.com/graphql-python/graphene) library, be sure to use the `graphql_schema` attribute to pass as schema on the `GraphQLView` view. Otherwise, the `GraphQLSchema` from `graphql-core` is the way to go. + +More info at [Graphene v3 release notes](https://github.com/graphql-python/graphene/wiki/v3-release-notes#graphene-schema-no-longer-subclasses-graphqlschema-type) and [GraphQL-core 3 usage](https://github.com/graphql-python/graphql-core#usage). + + +### Supported options for GraphQLView -### Supported options * `schema`: The `GraphQLSchema` object that you want the view to execute when it gets a valid request. - * `context`: A value to pass as the `context` to the `graphql()` function. - * `root_value`: The `root_value` you want to provide to `executor.execute`. + * `context`: A value to pass as the `context_value` to graphql `execute` function. By default is set to `dict` with request object at key `request`. + * `root_value`: The `root_value` you want to provide to graphql `execute`. * `pretty`: Whether or not you want the response to be pretty printed JSON. - * `executor`: The `Executor` that you want to use to execute queries. * `graphiql`: If `True`, may present [GraphiQL](https://github.com/graphql/graphiql) when loaded directly from a browser (a useful tool for debugging and exploration). + * `graphiql_version`: The graphiql version to load. Defaults to **"1.0.3"**. * `graphiql_template`: Inject a Jinja template string to customize GraphiQL. + * `graphiql_html_title`: The graphiql title to display. Defaults to **"GraphiQL"**. * `batch`: Set the GraphQL view as batch (for using in [Apollo-Client](http://dev.apollodata.com/core/network.html#query-batching) or [ReactRelayNetworkLayer](https://github.com/nodkz/react-relay-network-layer)) * `middleware`: A list of graphql [middlewares](http://docs.graphene-python.org/en/latest/execution/middleware/). + * `encode`: the encoder to use for responses (sensibly defaults to `graphql_server.json_encode`). + * `format_error`: the error formatter to use for responses (sensibly defaults to `graphql_server.default_format_error`. + * `subscriptions`: The GraphiQL socket endpoint for using subscriptions in graphql-ws. + * `headers`: An optional GraphQL string to use as the initial displayed request headers, if not provided, the stored headers will be used. + * `default_query`: An optional GraphQL string to use when no query is provided and no stored query exists from a previous session. If not provided, GraphiQL will use its own default query. +* `header_editor_enabled`: An optional boolean which enables the header editor when true. Defaults to **false**. +* `should_persist_headers`: An optional boolean which enables to persist headers to storage when true. Defaults to **false**. You can also subclass `GraphQLView` and overwrite `get_root_value(self, request)` to have a dynamic root value per request. From d1ac3a56d3fe6b5eb9c78e9786a3ffd4b1b9e633 Mon Sep 17 00:00:00 2001 From: KingDarBoja Date: Fri, 7 Aug 2020 11:40:39 -0500 Subject: [PATCH 20/21] chore: add flask as extra on install requires --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 9a7c1cc..d8a626e 100644 --- a/setup.py +++ b/setup.py @@ -1,7 +1,7 @@ from setuptools import setup, find_packages install_requires = [ - "graphql-server>=3.0.0b1", + "graphql-server[flask]>=3.0.0b1", ] tests_requires = [ From 2f627ed4028f7dbe986cbbe5a89486822a2c0b83 Mon Sep 17 00:00:00 2001 From: KingDarBoja Date: Fri, 7 Aug 2020 12:18:55 -0500 Subject: [PATCH 21/21] docs: update contribute link --- CONTRIBUTING.md | 94 ------------------------------------------------- MANIFEST.in | 1 - README.md | 2 +- 3 files changed, 1 insertion(+), 96 deletions(-) delete mode 100644 CONTRIBUTING.md diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md deleted file mode 100644 index db93994..0000000 --- a/CONTRIBUTING.md +++ /dev/null @@ -1,94 +0,0 @@ -# Contributing - -Thanks for helping to make flask-graphql awesome! - -We welcome all kinds of contributions: - -- Bug fixes -- Documentation improvements -- New features -- Refactoring & tidying - - -## Getting started - -If you have a specific contribution in mind, be sure to check the [issues](https://github.com/graphql-python/flask-graphql/issues) and [pull requests](https://github.com/graphql-python/flask-graphql/pulls) in progress - someone could already be working on something similar and you can help out. - - -## Project setup - -### Development with virtualenv (recommended) - -After cloning this repo, create a virtualenv: - -```console -virtualenv flask-graphql-dev -``` - -Activate the virtualenv and install dependencies by running: - -```console -python pip install -e ".[test]" -``` - -If you are using Linux or MacOS, you can make use of Makefile command -`make dev-setup`, which is a shortcut for the above python command. - -### Development on Conda - -You must create a new env (e.g. `flask-graphql-dev`) with the following command: - -```sh -conda create -n flask-graphql-dev python=3.8 -``` - -Then activate the environment with `conda activate flask-graphql-dev`. - -Proceed to install all dependencies by running: - -```console -python pip install -e ".[test]" -``` - -And you ready to start development! - -## Running tests - -After developing, the full test suite can be evaluated by running: - -```sh -pytest tests --cov=flask_graphql -vv -``` - -If you are using Linux or MacOS, you can make use of Makefile command -`make tests`, which is a shortcut for the above python command. - -You can also test on several python environments by using tox. - -### Running tox on virtualenv - -Install tox: - -```console -pip install tox -``` - -Run `tox` on your virtualenv (do not forget to activate it!) -and that's it! - -### Running tox on Conda - -In order to run `tox` command on conda, install -[tox-conda](https://github.com/tox-dev/tox-conda): - -```sh -conda install -c conda-forge tox-conda -``` - -This install tox underneath so no need to install it before. - -Then uncomment the `requires = tox-conda` line on `tox.ini` file. - -Run `tox` and you will see all the environments being created -and all passing tests. :rocket: - diff --git a/MANIFEST.in b/MANIFEST.in index 5854051..0fa13a1 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,6 +1,5 @@ include LICENSE include README.md -include CONTRIBUTING.md include tox.ini include Makefile diff --git a/README.md b/README.md index 7670f48..3546b1d 100644 --- a/README.md +++ b/README.md @@ -85,4 +85,4 @@ class UserRootValue(GraphQLView): ``` ## Contributing -See [CONTRIBUTING.md](CONTRIBUTING.md) +Since v3, `flask-graphql` code lives at [graphql-server](https://github.com/graphql-python/graphql-server) repository to keep any breaking change on the base package on sync with all other integrations. In order to contribute, please take a look at [CONTRIBUTING.md](https://github.com/graphql-python/graphql-server/blob/master/CONTRIBUTING.md). pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy