The contents of this repository were obsoleted by recent developments in https://github.com/solidusio-contrib/solidus_graphql_api.
The repository is kept available for reference.
This repository provides scripts used to generate (and periodically update) GraphQL files in Solidus extension solidus_graphql_api.
If you are interested in using the Solidus GraphQL API, please use the mentioned extension - solidus_graphql_api.
If you are interested in developing the Solidus GraphQL API, this repository is of relevance to you.
Solidus GraphQL API schema is kept in sync with Shopify for the most part. To avoid the inconvenience of periodically having to manually check and update schema, this repository contains tools to check upstream schema and make any updates automatically.
In solidus_graphql_api, the GraphQL files live in the directory lib/solidus_graphql_api/graphql/
. That directory is further divided into two parts:
- The schema part, i.e. the files kept in the subdirectory
schema/
- The implementation part, i.e. all other files outside of the subdirectory
schema/
The file lib/solidus_graphql_api/graphql/all.rb
lists all those files and require
s them in the correct order.
Additionally, at the top level there is also directory spec/
with all the tests.
Files in the subdirectory schema/
are intended to be maintained by the generator script from this repository and contain no user implementation code. They are rewritten on every invocation of the generator.
In GraphQL, almost everything is a Type. This is excellent for the simplification of all the tooling and logic needed around it, but is not very elegant when hundreds of files live all in the same directory, especially when whole groups of Types are used in different contexts and are not directly related to each other.
Therefore, Solidus GraphQL API splits all types into the main distinctive groups they belong to:
- Inputs - Input Object types mainly used when invoking mutations. They are kept in the subdirectory
inputs/
and prefixed withInputs::
in class names. - Payloads - Response object types mainly used for receiving responses from mutations. They are kept in the subdirectory
payloads/
and prefixed withPayloads::
in class names. - Interfaces - Non-instantiable types which other types may implement. They are kept in the subdirectory
interfaces/
and prefixed withInterfaces::
in module names. - Types - All other types. They are kept in the subdirectory
types/
and prefixed withTypes::
in class names.
An example would be ::Spree::GraphQL::Schema::Types::Shop
or ::Spree::GraphQL::Schema::Inputs::CreditCardPayment
.
Please note that because the GraphQL schema namespace is flat, and also to preserve compatibility with the upstream schema and type names, in GraphQL queries all these types are known by their original names, such as Shop
and CreditCardPaymentInput
.
Since the schema files are managed by the generator script, actual Solidus GraphQL API method implementations which are inserted and maintained manually cannot be placed there.
Every Solidus schema file expects its corresponding implementation file placed in the same directory structure and using the same class/module name, just with the schema/
and Schema::
prefixes removed.
For example, GraphQL type Schema::Types::Shop
expects to find its implementation methods in the module Types::Shop
and automatically includes that module in its class.
When the schema generator is run, it also generates the stub implementation files if they don't yet exist. These files are generated with ready-made templates for all defined GraphQL fields, including mentioning their description, arguments, and expected return value in the comments above method definitions.
All auto-generated method stubs contain one default line of code which raises error ::Spree::GraphQL::NotImplementedError
.
To implement a particular GraphQL field, one can use all approaches mentioned in graphql-ruby Fields Guides, but most often this will come down to one of two choices:
- Remove the NotImplemented error for a particular call and replace it with the actual implementation. In this case, as per graphql-ruby, the underlying object is automatically available in helper variable
object
, context incontext
, and arguments are accessed directly as arguments passed to the method. - Comment or remove the whole method definition and allow the underlying object to respond to the call directly. For example, in a GraphQL query
{ shop { name } }
, after the query root object resolvesshop
to an instance ofSchema::Types::Shop
(and in turn an instance of::Spree:Store
), thenname
can either be implemented asdef name() object.name end
, or it can be left out so that the methodname
gets called on::Spree::Store
directly.
By default, every Solidus implementation file has a corresponding test file placed in the same directory structure, but under the directory spec/
.
For example, GraphQL type Types::Shop
which is implemented in the file lib/solidus_graphql_api/graphql/types/shop.rb
would have its test file in spec/graphql/types/shop.rb
.
When the schema generator is run, it also generates the stub test files if they don't yet exist. These files are generated with ready-made test templates for all defined GraphQL fields.
Because the auto-generated test templates are simple and only cover basic use cases, the individual field tests are output in commented form and require manual review, modification, and uncommenting to activate them.
To test a particular GraphQL field, we generally use a simple approach where a GraphQL string is passed to GraphQL::Schema::Schema.execute
and its result is tested against the expected value.
To generate GraphQL schema files or to update them from upstream, you would use the following general procedure:
- Clone this repository and chdir into it
- Git clone graphql-ruby from https://github.com/rmosolgo/graphql-ruby into current directory
- Git clone solidus_graphql_api from https://github.com/boomerdigital/solidus_graphql_api into current directory (optional)
- Open GraphiQL at https://help.shopify.com/en/api/graphiql
- Paste the contents of file
introspection_query.txt
into the opened GraphiQL and run the query - Save the resulting JSON content to file
schema.json
- Run
./generate-schema.rb schema.json graphql-ruby
- Check for any differences with
git diff
/git status
- If new files were created, remember to add them to solidus_graphql_api's file
solidus_graphql_api/lib/solidus_graphql_api/graphql/all.rb
Please note that the generator will output all generated files to subdirectory solidus_graphql_api/
, using the same directory structure as used by solidus_graphql_api.
You can compare your existing solidus_graphql_api files with the generated contents in generally one of two ways:
- You can compare the two directories with a command like
cd solidus_graphql_api/lib/solidus_graphql_api/graphql/; diff -ruNP /path/to/.../graphql/ ./graphql/
. This approach is good, but you will need to copy the changes you intend to keep over to solidus_graphql_api. - You can clone solidus_graphql_api into the directory of the same name. This approach would effectively generate the files directly in the solidus_graphql_api source tree and you would be able to check any changes directly, by entering that directory and running
git diff
/git status
. You will also most often want to limit the diffs to directory which is intended to be managed fully automatically, which islib/solidus_graphql_api/graphql/schema/
.
Once the schema files are updated, the implementation files, the test files, and the file all.rb
may need to be adjusted manually to reflect the changes, and then the whole set of changes should be committed to solidus_graphql_api.
After running the generator, you should be able to syntax-check the generated files by simply running ruby solidus_graphql_api/lib/solidus_graphql_api/graphql/all.rb
. There should be no output or error displayed.
In addition, when the generated files are copied over to solidus_graphql_api, you should see its README for instructions how to run the whole spec suite.
For troubleshooting, use the following guidelines:
- Open
generate-schema.rb
and change log level from WARN to DEBUG - To access or print schema from Rails console in a Solidus app, run
rails c
, then seeSpree::GraphQL::Schema::Schema.to_graphql
- To compare schemas, the best way is to run the introspection query against both servers, then compare the returned JSONs
- To filter out insignificant differences in schemas before comparison, and to output each differing type as files on disk, first run the introspection query, then place the upstream result to
schema.json
and Solidus result tosolidus-schema.json
and run./compare-schemas.rb schema.json solidus-schema.json
. The differences will be placed to directorydiffs/
.
The schema is generated automatically and so depends on the specifics of the generator and current version of graphql-ruby
.
Currently, the smaller points of difference between GraphQL API schemas are:
- Solidus uses newer versions of
graphql-ruby
which may include more verbose field and argument descriptions, and more data in thepageInfo
type. - GraphQL field
Shop.productTypes
in upstream API only supports argumentfirst
, while in Solidus it is a regular Connection with four default arguments (first
,last
,before
,after
).
- Check whether implementation methods for connection types need to explicitly allow arguments
first
,last
,before
, andafter
, or this is automatically handled bygraphql-ruby
. - Some types contain lists of accepted values, such as
Schema::Types::CountryCode
. These types and names are currently converted from original schema into Solidus schema as fixed strings. Instead, they should be populated dynamically from Solidus' own country list. - The generator script currently simply discards all deprecated parts of schema and they are not created in Solidus. If/when supporting deprecated elements becomes important, the generator will have to be modified to not skip deprecated elements, but to output them while honoring their
isDeprecated
status. - See whether it is possible to make
Shop.productTypes
connection only support argumentfirst
(like in the upstream API) rather than all four arguments (first
,last
,before
,after
) - Authentication in solidus_graphql_api should be sorted out as mentioned in solidusio-contrib/solidus_graphql_api-deprecated#14
- Some GraphQL types are not marked as deprecated, but they are effectively unused because only deprecated fields used them. Currently, these cases are not detected and these types are output to disk along with all other types. This might want to be detected and those types omitted from output.