Skip to content

Commit fcc3de2

Browse files
garyd203jkimbo
authored andcommitted
Allow graphql schema export to use a canonical representation (#439)
When we use the `graphql_schema` management command, the output can vary from run to run depending on arbitrary factors (because there is no guarantee made about the order used to output JSON dictionary keys). This makes it difficult to compare two schema's at different points in time. We address this by including a new `canonical` flag to the command, which uses standard `json.dump` funcitonality to sort dictionary keys and force pretty-printed output.
1 parent 1ad0347 commit fcc3de2

File tree

4 files changed

+20
-5
lines changed

4 files changed

+20
-5
lines changed

docs/introspection.rst

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ data to ``schema.json`` that is compatible with babel-relay-plugin.
1111
Usage
1212
-----
1313

14-
Include ``graphene_django`` to ``INSTALLED_APPS`` in you project
14+
Include ``graphene_django`` to ``INSTALLED_APPS`` in your project
1515
settings:
1616

1717
.. code:: python
@@ -29,6 +29,8 @@ It dumps your full introspection schema to ``schema.json`` inside your
2929
project root directory. Point ``babel-relay-plugin`` to this file and
3030
you're ready to use Relay with Graphene GraphQL implementation.
3131

32+
The schema file is sorted to create a reproducible canonical representation.
33+
3234
Advanced Usage
3335
--------------
3436

graphene_django/management/commands/graphql_schema.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ class Command(CommandArguments):
3939

4040
def save_file(self, out, schema_dict, indent):
4141
with open(out, "w") as outfile:
42-
json.dump(schema_dict, outfile, indent=indent)
42+
json.dump(schema_dict, outfile, indent=indent, sort_keys=True)
4343

4444
def handle(self, *args, **options):
4545
options_schema = options.get("schema")
@@ -65,7 +65,7 @@ def handle(self, *args, **options):
6565
indent = options.get("indent")
6666
schema_dict = {"data": schema.introspect()}
6767
if out == '-':
68-
self.stdout.write(json.dumps(schema_dict, indent=indent))
68+
self.stdout.write(json.dumps(schema_dict, indent=indent, sort_keys=True))
6969
else:
7070
self.save_file(out, schema_dict, indent)
7171

graphene_django/settings.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@
2828
DEFAULTS = {
2929
"SCHEMA": None,
3030
"SCHEMA_OUTPUT": "schema.json",
31-
"SCHEMA_INDENT": None,
31+
"SCHEMA_INDENT": 2,
3232
"MIDDLEWARE": (),
3333
# Set to True if the connection fields must have
3434
# either the first or last argument

graphene_django/tests/test_command.py

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
from django.core import management
2-
from mock import patch
2+
from mock import patch, mock_open
33
from six import StringIO
44

55

@@ -8,3 +8,16 @@ def test_generate_file_on_call_graphql_schema(savefile_mock, settings):
88
out = StringIO()
99
management.call_command("graphql_schema", schema="", stdout=out)
1010
assert "Successfully dumped GraphQL schema to schema.json" in out.getvalue()
11+
12+
13+
@patch('json.dump')
14+
def test_files_are_canonical(dump_mock):
15+
open_mock = mock_open()
16+
with patch('graphene_django.management.commands.graphql_schema.open', open_mock):
17+
management.call_command('graphql_schema', schema='')
18+
19+
open_mock.assert_called_once()
20+
21+
dump_mock.assert_called_once()
22+
assert dump_mock.call_args[1]["sort_keys"], "json.mock() should be used to sort the output"
23+
assert dump_mock.call_args[1]["indent"] > 0, "output should be pretty-printed by default"

0 commit comments

Comments
 (0)