diff --git a/.gitignore b/.gitignore index 57bee153..474bb31d 100644 --- a/.gitignore +++ b/.gitignore @@ -61,3 +61,7 @@ target/ /tests/django.sqlite + +/graphene/index.json + +/graphene/meta.json diff --git a/.travis.yml b/.travis.yml index afc82b45..705ef0d3 100644 --- a/.travis.yml +++ b/.travis.yml @@ -6,22 +6,25 @@ python: - 3.4 - 3.5 - pypy +node_js: +- 4.1 cache: - - pip - - directories: + directories: - .cache/pip/ - - docs/node_modules + - $HOME/.cache/pip + - docs/node_modules/ + - $HOME/docs/node_modules install: -- pip install --download-cache .cache/pip/ pytest pytest-cov coveralls flake8 six +- pip install --download-cache $HOME/.cache/pip/ pytest pytest-cov coveralls flake8 six blinker pytest-django -- pip install --download-cache .cache/pip/ -e .[django] +- pip install --download-cache $HOME/.cache/pip/ -e .[django] - python setup.py develop script: - py.test --cov=graphene - flake8 after_success: - coveralls -- cd docs && npm run deploy +- nvm install 4.1 && cd docs && npm run deploy env: matrix: diff --git a/docs/assets/edit.png b/docs/assets/edit.png new file mode 100644 index 00000000..80646b2f Binary files /dev/null and b/docs/assets/edit.png differ diff --git a/docs/assets/edit@2x.png b/docs/assets/edit@2x.png new file mode 100644 index 00000000..faa87483 Binary files /dev/null and b/docs/assets/edit@2x.png differ diff --git a/docs/config.toml b/docs/config.toml index 08df109a..f53d3f17 100644 --- a/docs/config.toml +++ b/docs/config.toml @@ -1 +1,17 @@ -siteTitle="Graphene" +siteTitle = "Graphene" + +[docs.quickstart] + name = "Quickstart" + pages = [ + "/docs/quickstart/", + "/docs/quickstart-django/", + ] + +[docs.walkthrough] + name = "Walkthrough" + pages = [ + "/docs/interfaces/", + "/docs/objecttypes/", + "/docs/mutations/", + "/docs/basic-types/", + ] diff --git a/docs/css/hljs.css b/docs/css/hljs.css new file mode 100644 index 00000000..10019598 --- /dev/null +++ b/docs/css/hljs.css @@ -0,0 +1,70 @@ +/** + * GitHub Gist Theme + * Author : Louis Barranqueiro - https://github.com/LouisBarranqueiro + */ + +.hljs { + display: block; + background: white; + padding: 0.5em; + color: #333333; + overflow-x: auto; +} + +.hljs-comment, +.hljs-meta { + color: #969896; +} + +.hljs-string, +.hljs-variable, +.hljs-template-variable, +.hljs-strong, +.hljs-emphasis, +.hljs-quote { + color: #df5000; +} + +.hljs-keyword, +.hljs-selector-tag, +.hljs-type { + color: #a71d5d; +} + +.hljs-literal, +.hljs-symbol, +.hljs-bullet, +.hljs-attribute { + color: #0086b3; +} + +.hljs-section, +.hljs-name { + color: #63a35c; +} + +.hljs-tag { + color: #333333; +} + +.hljs-title, +.hljs-attr, +.hljs-selector-id, +.hljs-selector-class, +.hljs-selector-attr { + color: #795da3; +} + +.hljs-addition { + color: #55a532; + background-color: #eaffea; +} + +.hljs-deletion { + color: #bd2c00; + background-color: #ffecec; +} + +.hljs-link { + text-decoration: underline; +} diff --git a/docs/css/main.styl b/docs/css/main.styl index 71c00739..31049be9 100644 --- a/docs/css/main.styl +++ b/docs/css/main.styl @@ -1,6 +1,7 @@ @import 'nib' @import 'jeet' +@import 'hljs.css' @import 'https://fonts.googleapis.com/css?family=Raleway:400,500,600,200,100&.css' normalize-css() @@ -15,6 +16,9 @@ $wrapper a, a:hover text-decoration none +a + color rgb(42,93,173) + html, body font-family "Helvetica Neue", Helvetica, Arial, sans-serif font-weight 300 @@ -85,7 +89,19 @@ html, body text-transform uppercase font-weight 600 line-height 15px - + position relative + &.active:before + content: '' + width 5px + height 5px + border-radius 100% + display block + position absolute + background white + left 50% + margin-left -3px + bottom -24px + .header-logo font-family 'Raleway' font-size 22px @@ -168,45 +184,183 @@ html, body border-radius: 100px font-size 13px padding 17px 17px 17px 71px - width 226px + width 236px box-sizing border-box color white font-family 'Raleway' font-weight 500 + transition all .2s ease-in-out &:before content: '' display block position absolute - left 0 - top 0 + left 20px + top 20px height 32px width 32px - image '../assets/starwars-icon.png' + image '~!file-loader!../assets/starwars-icon.png' 32px 32px + + &:hover + transform translateY(-3px) + box-shadow 0px 4px 8px 0px rgba(0,0,0,0.32) .improve-document-link - @extend $wrapper + position fixed + right 0 + bottom 70px + transform-origin 100% 100% + background: #999; + border: 1px solid #919191; + border-radius: 3px 3px 0 0; + border-bottom 0 + padding 9px 12px 12px 34px + transform: rotate(270deg) translateX(100%) translateY(3px); + font-size: 11px; + font-weight: 500; + text-transform uppercase + color: #FFFFFF; + letter-spacing: 0.3px; + line-height: 11px; + transition all .2s ease-in-out + &:before + content: '' + display block + position absolute + left 10px + top 8px + height 16px + width 16px + image '~!file-loader!../assets/edit.png' 16px 16px + &:hover + transform: rotate(270deg) translateX(100%) + background #666 + border-color #555 + +below(600px) + display none + +$title + display block + font-family: 'Raleway'; + font-weight 500 + line-height 1.2em + padding-top .3em + margin-bottom .5em + padding-bottom .5em + color #4A4A4A .markdown .wrapper margin-top 60px - h1, h2, h3, h4, h5, h6 - font-family: 'Raleway'; - font-weight 600 - color #4A4A4A - strong - font-weight 500 - + h1, h2, h3, h4, h5, h6 + @extend $title + + h1 + font-size 32px + h2 + font-size 26px + h3 + font-size 24px + h4 + font-size 21px + h5 + font-size 18px + h6 + font-size 16px + strong + font-weight 500 + pre + line-height 20px + background #FAFAFA + padding 20px + white-space: pre + display: block; + color: #333333; + overflow-x: auto; + p code, ul code + background #FAFAFA + padding 2px 4px + border-radius 2px + border 1px solid #CCC + color #000 + code + font-size 14px + line-height 20px + overflow-x: auto; margin-bottom 40px +.markdown h1:first-child + margin-top 0 + padding-top 0 + .title background: #F9F9F9; - padding 30px 0 + padding 54px 0 h1 + margin 0 auto @extend $wrapper font-family: 'Raleway'; font-size: 42px; font-weight 200 color: #585858; line-height: 49px; + +.docs + @extend $wrapper + +.docs-aside + col(1/4) + margin-top 60px + +below(600px) + padding 20px + width 100% + box-sizing content-box + margin 0 -20px + margin-bottom 30px + background #F9F9F9 + +.docs-aside-group + display block + margin-bottom 40px + h3 + font-family: 'Raleway'; + font-weight 500 + font-size 12px + text-transform uppercase + line-height 1.2em + margin-bottom 1em + color #AAA + a + display block + font-size 15px + font-weight 400 + line-height 22px + height 28px + padding 3px 0 + color #4A4A4A + &.active + font-weight 500 + line-height 21px + color #E05B49 + + +below(600px) + display none + +.docs-aside-navselect + display none + width 100% + +below(600px) + display block + +.docs-content + col(3/4) + margin-top 60px + +below(600px) + margin-top 10px + col(1) + + >h1 + margin 0 + @extend $title + font-size 32px diff --git a/docs/gatsby.config.js b/docs/gatsby.config.js index 80be7b41..8aa39cdf 100644 --- a/docs/gatsby.config.js +++ b/docs/gatsby.config.js @@ -1,12 +1,13 @@ var nib = require("nib"); var jeet = require("jeet"); +var rupture = require("rupture"); var ExtractTextPlugin = require("extract-text-webpack-plugin"); module.exports = function(config, env) { var IS_STATIC = env === 'static'; config.merge({ stylus: { - use: [nib(), jeet()] + use: [nib(), jeet(), rupture()] } }); if (IS_STATIC) { diff --git a/docs/package.json b/docs/package.json index 7ef2b3b9..2eeea26e 100644 --- a/docs/package.json +++ b/docs/package.json @@ -5,7 +5,7 @@ "main": "n/a", "scripts": { "test": "echo \"Error: no test specified\" && exit 1", - "build": "npm install -g gatsby && npm install", + "build": "npm install -g gatsby && npm install && gatsby build", "deploy": "npm run build" }, "keywords": [ diff --git a/docs/pages/_header.js b/docs/pages/_header.js index 9d70ef03..845a081d 100644 --- a/docs/pages/_header.js +++ b/docs/pages/_header.js @@ -1,6 +1,13 @@ var IN_BROWSER = typeof window != 'undefined'; import React from 'react'; +var browser_supported; if (IN_BROWSER) { + var isSafari = navigator.vendor && navigator.vendor.indexOf('Apple') > -1 && + navigator.userAgent && !navigator.userAgent.match('CriOS'); + var browser_supported = !isSafari; +} +console.log(browser_supported); +if (IN_BROWSER && browser_supported) { var glfx = require('../vendor/glfx.optim') var particlesJS = require('../vendor/particles.js') } @@ -23,12 +30,12 @@ class Header extends React.Component { this.mounted = false; } componentDidMount() { - if (!IN_BROWSER) return; + if (!(IN_BROWSER && browser_supported)) return; this.mounted = true; new particlesJS('header-background', { "particles": { "number": { - "value": 36, + "value": 40, "density": { "enable": true, "value_area": 800 diff --git a/docs/pages/_template.js b/docs/pages/_template.js index e077d005..4d40b788 100644 --- a/docs/pages/_template.js +++ b/docs/pages/_template.js @@ -19,10 +19,10 @@ class Template extends React.Component { Graphene {isIndex? diff --git a/docs/pages/community.md b/docs/pages/community.md index 5ad38de1..c42f6b2b 100644 --- a/docs/pages/community.md +++ b/docs/pages/community.md @@ -17,9 +17,17 @@ If you think working with Graphene is fun, there are many ways you can contribut - **GraphQL Relay**: [Source Code][1] - [PyPI package][2] - **Graphene**: [Source Code][3] - [PyPI package][4] +Django integration: +- **graphql-django-view**: [Source Code][5] - [PyPI package][6] +- **django-graphiql**: [Source Code][7] - [PyPI package][8] + [Source Code]: https://github.com/graphql-python/graphql-core [PyPI package]: https://pypi.python.org/pypi/graphql-core [1]: https://github.com/graphql-python/graphql-relay [2]: https://pypi.python.org/pypi/graphql-relay [3]: https://github.com/graphql-python/graphene [4]: https://pypi.python.org/pypi/graphene + [5]: https://github.com/graphql-python/graphql-django-view + [6]: https://pypi.python.org/pypi/graphql-django-view + [7]: https://github.com/graphql-python/django-graphiql + [8]: https://pypi.python.org/pypi/django-graphiql diff --git a/docs/pages/docs/_template.js b/docs/pages/docs/_template.js new file mode 100644 index 00000000..c678e494 --- /dev/null +++ b/docs/pages/docs/_template.js @@ -0,0 +1,48 @@ +import React from 'react'; +import { RouteHandler, Link, State } from 'react-router'; +import _ from 'lodash'; + +class Template extends React.Component { + goToPage(event) { + var page = event.target.value; + this.context.router.transitionTo(page); + } + render() { + var docs = this.props.config.docs; + var docs_index = _.indexBy(this.props.pages, 'path'); + return ( +
+ +
+ +
+
+ ); + } +} + +Template.contextTypes = { + router: React.PropTypes.func +}; + +module.exports = Template; diff --git a/docs/pages/docs/basic-types.md b/docs/pages/docs/basic-types.md new file mode 100644 index 00000000..7a067942 --- /dev/null +++ b/docs/pages/docs/basic-types.md @@ -0,0 +1,52 @@ +--- +title: Basic Types +description: Walkthrough Basic Types +--- + +# Basic Types + +Graphene define the following base Types: +- `graphene.String` +- `graphene.Int` +- `graphene.Float` +- `graphene.Boolean` +- `graphene.ID` + +Also, define: +- `graphene.List` +- `graphene.NonNull` + +## Mounting in ClassTypes + +This types if are mounted in a `ObjectType`, `Interface` or `Mutation`, + would act as `Field`s. +So, the following examples will behave exactly the same: + +```python +class Person(graphene.ObjectType): + name = graphene.String() +``` +and + +```python +class Person(graphene.ObjectType): + name = graphene.Field(graphene.String()) +``` + +## Mounting in Fields + +If this types are mounted in a `Field`, would act as `Argument`s. +So, the following examples will behave exactly the same: + +```python +class Person(graphene.ObjectType): + say_hello = graphene.Field(graphene.String(), + to=graphene.String()) +``` +and + +```python +class Person(graphene.ObjectType): + say_hello = graphene.Field(graphene.String(), + to=graphene.Argument(graphene.String())) +``` diff --git a/docs/pages/guide/interface.md b/docs/pages/docs/interfaces.md similarity index 76% rename from docs/pages/guide/interface.md rename to docs/pages/docs/interfaces.md index 9f5ff2b9..43c2daf8 100644 --- a/docs/pages/guide/interface.md +++ b/docs/pages/docs/interfaces.md @@ -1,9 +1,13 @@ +--- +title: Interfaces +description: Walkthrough Interfaces +--- + # Interfaces An Interface contains the essential fields that will be shared among multiple ObjectTypes. The basics: - - Each Interface is a Python class that inherits from graphene.Interface. - Each attribute of the Interface represents a GraphQL field. @@ -16,19 +20,19 @@ import graphene # Character is an Interface class Character(graphene.Interface): - name = graphene.String() + name = graphene.String() # Human is an ObjectType, as inherits an interface class Human(Character): - born_in = graphene.String() + born_in = graphene.String() # Droid is an ObjectType, as inherits an interface class Droid(Character): - function = graphene.String() + function = graphene.String() ``` -**name** is a field in the `Character` interface that will be in both `Human` and `Droid` ObjectTypes (as those inherit from Character). Each ObjectType also define extra fields at the same time. +**name** is a field in the `Character` interface that will be in both `Human` and `Droid` ObjectTypes (as those inherit from `Character`). Each ObjectType also define extra fields at the same time. The above types would have the following representation in a schema: diff --git a/docs/pages/docs/mutations.md b/docs/pages/docs/mutations.md new file mode 100644 index 00000000..7967f62c --- /dev/null +++ b/docs/pages/docs/mutations.md @@ -0,0 +1,75 @@ +--- +title: Mutations +description: Walkthrough Mutations +--- + +# Mutations + +A Mutation is a special ObjectType that define also an Input. + +## Quick example + +This example defines a Mutation: + +```python +import graphene + +class CreatePerson(graphene.Mutation): + class Input: + name = graphene.String() + + ok = graphene.String() + person = graphene.Field('Person') + + @classmethod + def mutate(cls, args, info): + person = Person(name=args.get('name')) + ok = True + return CreatePerson(person=person, ok=ok) +``` + +**person** and **ok** are the output fields of the Mutation when is resolved. + +**Input** attributes are the arguments that the Mutation `CreatePerson` needs for resolving, in this case **name** will be the only argument for the mutation. + +**mutate** is the function that will be applied once the mutation is called. + +So, we can finish our schema like this: + +```python +# ... the Mutation Class + +class Person(graphene.ObjectType): + name = graphene.String() + +class MyMutations(graphene.ObjectType): + create_person = graphene.Field(CreatePerson) + +schema = graphene.Schema(mutation=MyMutations) +``` + +## Executing the Mutation + +Then, if we query (`schema.execute(query_str)`) the following: +```graphql +mutation myFirstMutation { + createPerson(name:"Peter") { + person { + name + } + ok + } +} +``` + +We should receive: + +```json +{ + "createPerson": { + "person" : { + name: "Peter" + }, + "ok": true + } +} diff --git a/docs/pages/guide/objecttype.md b/docs/pages/docs/objecttypes.md similarity index 85% rename from docs/pages/guide/objecttype.md rename to docs/pages/docs/objecttypes.md index 191094ae..dbcab0f3 100644 --- a/docs/pages/guide/objecttype.md +++ b/docs/pages/docs/objecttypes.md @@ -1,10 +1,14 @@ +--- +title: ObjectTypes +description: Walkthrough ObjectTypes +--- + # ObjectTypes An ObjectType is the single, definitive source of information about your data. It contains the essential fields and behaviors of the data you’re querying. The basics: - -- Each ObjectType is a Python class that inherits graphene.ObjectType or inherits an implemented [interface](/docs/interface). +- Each ObjectType is a Python class that inherits graphene.ObjectType or inherits an implemented [interfaces](/docs/interfaces/). - Each attribute of the ObjectType represents a GraphQL field. ## Quick example @@ -20,7 +24,7 @@ class Person(graphene.ObjectType): full_name = graphene.String() def resolve_full_name(self, args, info): - return '{} {}'.format(self.first_name, self.last_name) + return '{} {}'.format(self.first_name, self.last_name) ``` **first_name** and **last_name** are fields of the ObjectType. Each field is specified as a class attribute, and each attribute maps to a GraphQL field. diff --git a/docs/pages/docs/django_quickstart.md b/docs/pages/docs/quickstart-django.md similarity index 95% rename from docs/pages/docs/django_quickstart.md rename to docs/pages/docs/quickstart-django.md index e88fbcdf..542854e7 100644 --- a/docs/pages/docs/django_quickstart.md +++ b/docs/pages/docs/quickstart-django.md @@ -1,13 +1,10 @@ --- -title: Django Quickstart Guide -active_tab: quickstart +title: Guide to Django description: A Quick guide to Graphene in Django --- -## Django Quickstart - In our previous quickstart page we created a very simple schema. -Now we will adapt the schema for our Django models. +Now we will adapt the schema to map automatically some Django models. ## Project setup diff --git a/docs/pages/docs/quickstart.md b/docs/pages/docs/quickstart.md index f408a851..f2c56f03 100644 --- a/docs/pages/docs/quickstart.md +++ b/docs/pages/docs/quickstart.md @@ -1,10 +1,10 @@ --- -title: Quickstart Guide -active_tab: quickstart +title: Getting started description: A Quick guide to Graphene --- -Graphene is a powerful framework for creating GraphQL schemas easily in Python. +Let's build a basic GraphQL schema from scratch. + ## Requirements @@ -23,6 +23,7 @@ cd tutorial virtualenv env source env/bin/activate # On Windows use `env\Scripts\activate` +# Install Graphene pip install graphene ``` @@ -30,7 +31,7 @@ pip install graphene A GraphQL schema describes your data model, and provides a GraphQL server with an associated set of resolve methods that know how to fetch data. -We are going to create a very simple schema, with a `Query` with only one field: `hello`. And when we query it should return 'World'. +We are going to create a very simple schema, with a `Query` with only one field: `hello`. And when we query it should return `"World"`. ```python diff --git a/docs/pages/guide/mutation.md b/docs/pages/guide/mutation.md deleted file mode 100644 index 788cadfd..00000000 --- a/docs/pages/guide/mutation.md +++ /dev/null @@ -1,67 +0,0 @@ -# Mutations - -A Mutation is a special ObjectType that define also an Input. - -## Quick example - -This example model defines a Mutation: - -```python -import graphene - -class CreatePerson(graphene.Mutation): - class Input: - name = graphene.String() - - ok = graphene.String() - person = graphene.Field('Person') - - @classmethod - def mutate(cls, args, info): - person = Person(name=args.get('name')) - ok = True - return CreatePerson(person=person, ok=ok) -``` - -**person** and **ok** are the output fields of the Mutation when is resolved. -**Input** attributes are the arguments that the Mutation needs for resolving. **mutate** is the function that will be applied once the mutation is called. - -So, we can finish our schema like this: - -```python -# ... the Mutation Class - -class Person(graphene.ObjectType): - name = graphene.String() - -class MyMutations(graphene.ObjectType): - create_person = graphene.Field(CreatePerson) - -schema = graphene.Schema(mutation=MyMutations) -``` - -## Executing the Mutation - -Then, if we query (`schema.execute(query_str)`) the following: -```graphql -mutation myFirstMutation { - createPerson(name:"Peter") { - person { - name - } - ok - } -} -``` - -We should receive: - -```json -{ - "createPerson": { - "person" : { - name: "Peter" - }, - "ok": true - } -} diff --git a/docs/pages/index.md b/docs/pages/index.md index 80444d40..359e512b 100644 --- a/docs/pages/index.md +++ b/docs/pages/index.md @@ -1,7 +1,7 @@ --- path: / --- -Check our starwars API example! +
Check our Django Starwars API example!
## About Graphene diff --git a/docs/wrappers/md.js b/docs/wrappers/md.js index ddde3df8..b34806c3 100644 --- a/docs/wrappers/md.js +++ b/docs/wrappers/md.js @@ -9,15 +9,15 @@ class Markdown extends React.Component { var post = this.props.page.data; var pagePath = this.props.page.requirePath; var documentUrl = `${DOCS_BASEURL}${pagePath}`; - + var showTitle = post.title && !this.props.docs; return (
- {post.title?
+ {showTitle?

{post.title}

:null} -
- Improve this document! +
+ Edit page
);