├── .babelrc
├── .eslintrc
├── .gitignore
├── .idea
├── .name
├── encodings.xml
├── graphql-blog-schema.iml
├── jsLibraryMappings.xml
├── libraries
│ └── graphql_blog_schema_node_modules.xml
├── misc.xml
├── modules.xml
└── vcs.xml
├── LICENSE
├── README.md
├── index.html
├── package.json
├── server.js
├── src
├── App.js
├── CashayBook.js
├── Comment.js
├── CommentContainer.js
├── CreateComment.js
├── LatestPostComments.js
├── LeastRecentPosts.js
├── SinglePost.js
├── data
│ ├── authors.js
│ ├── comments.js
│ ├── groups.js
│ └── posts.js
├── getCashaySchema.js
├── graphiqlSchema.json
├── index.js
├── recentPosts.js
└── schema.js
├── static
└── graphiql.css
└── webpack.config.js
/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": ["react", "es2015", "stage-0"],
3 | "plugins": [
4 | ["transform-decorators-legacy"],
5 | ["add-module-exports"]
6 | ]
7 | }
8 |
--------------------------------------------------------------------------------
/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 | "ecmaFeatures": {
3 | "jsx": true,
4 | "modules": true
5 | },
6 | "env": {
7 | "browser": true,
8 | "node": true
9 | },
10 | "parser": "babel-eslint",
11 | "rules": {
12 | "quotes": [2, "single"],
13 | "strict": [2, "never"],
14 | "react/jsx-uses-react": 2,
15 | "react/jsx-uses-vars": 2,
16 | "react/react-in-jsx-scope": 2
17 | },
18 | "plugins": [
19 | "react"
20 | ]
21 | }
22 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | npm-debug.log
3 | .DS_Store
4 | dist
5 |
--------------------------------------------------------------------------------
/.idea/.name:
--------------------------------------------------------------------------------
1 | graphql-blog-schema
--------------------------------------------------------------------------------
/.idea/encodings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.idea/graphql-blog-schema.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/.idea/jsLibraryMappings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/.idea/libraries/graphql_blog_schema_node_modules.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/.idea/misc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/.idea/modules.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/.idea/vcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2014 Dan Abramov
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # cashay-playground
2 | really boring & uninteresting & definitely not click worthy
3 |
4 | ## Installation
5 | - `git clone https://github.com/mattkrick/cashay-playground.git`
6 | - `npm i && npm start`
7 | - `npm run updateSchema` (optional, run this whenever you change the schema)
8 |
9 | ## Things to do in the playground
10 | - Server calls are delayed by 1 second to see all the optimistic goodness
11 | - Notice that checking loading status is really, really easy
12 | - Click "get 2 more" and notice that the button goes away after you've got em all without a 2nd request to the server.
13 | - Click "show comments" on the first post to see 2 comments load
14 | - Add a comment & notice that you can add it ***anywhere in the array*** (in this case, it's sorted by karma & starts you out at 0)
15 | - Add a post & notice that the optimistic update is immediate & the response from the server overwrites your optimistic update
16 | - Adding a post updates the array _and_ the post count.
17 | - Crack open the Chrome Redux Devtools and take a look at your sexy, sexy store
18 |
19 | ## Things to notice in the code
20 | - I never wrote a single mutation on the client (no `variableDefinitions`, no response or fat query, nothing).
21 | - For most mutations, you don't even need to include a `components` options, it'll just grab all the queries with the corresponding handlers
22 | - Your mutation handler can do ANYTHING. seriously, ANYTHING
23 |
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | LearnGraphQL Sandbox
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "react-hot-boilerplate",
3 | "version": "1.0.0",
4 | "description": "Boilerplate for ReactJS project with hot code reloading",
5 | "scripts": {
6 | "start": "node server.js",
7 | "lint": "eslint src",
8 | "updateSchema": "cashay-schema src/schema.js src/clientSchema.json"
9 | },
10 | "repository": {
11 | "type": "git",
12 | "url": "https://github.com/gaearon/react-hot-boilerplate.git"
13 | },
14 | "keywords": [
15 | "react",
16 | "reactjs",
17 | "boilerplate",
18 | "hot",
19 | "reload",
20 | "hmr",
21 | "live",
22 | "edit",
23 | "webpack"
24 | ],
25 | "author": "Dan Abramov (http://github.com/gaearon)",
26 | "license": "MIT",
27 | "bugs": {
28 | "url": "https://github.com/gaearon/react-hot-boilerplate/issues"
29 | },
30 | "homepage": "https://github.com/gaearon/react-hot-boilerplate",
31 | "devDependencies": {
32 | "eslint-plugin-react": "^5.1.1",
33 | "json-loader": "^0.5.4",
34 | "webpack-hot-middleware": "^2.10.0",
35 | "react-transform-catch-errors": "1.0.2",
36 | "react-transform-hmr": "^1.0.4",
37 | "redbox-react": "1.2.6",
38 | "babel-cli": "6.9.0",
39 | "babel-core": "6.9.1",
40 | "babel-loader": "6.2.4",
41 | "babel-plugin-add-module-exports": "0.2.1",
42 | "babel-plugin-react-transform": "2.0.2",
43 | "babel-plugin-transform-decorators-legacy": "1.3.4",
44 | "babel-preset-es2015": "6.9.0",
45 | "babel-preset-react": "6.5.0",
46 | "babel-preset-stage-0": "6.5.0",
47 | "babel-register": "6.9.0"
48 | },
49 | "dependencies": {
50 | "babel-polyfill": "^6.9.1",
51 | "body-parser": "^1.15.1",
52 | "cashay": "^0.8.0",
53 | "express": "4.x.x",
54 | "graphiql": "^0.7.1",
55 | "graphql": "^0.6.0",
56 | "react": "^15.1.0",
57 | "react-dom": "^15.1.0",
58 | "react-hot-loader": "^1.3.0",
59 | "react-redux": "^4.4.5",
60 | "redux": "^3.5.2",
61 | "webpack": "^1.13.1",
62 | "webpack-dev-server": "^1.14.1"
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/server.js:
--------------------------------------------------------------------------------
1 | require('babel-polyfill');
2 | require('babel-register');
3 | var webpack = require('webpack')
4 | var config = require('./webpack.config')
5 | var express = require('express');
6 | var app = new express();
7 | var port = 3000
8 | var graphql = require('graphql').graphql
9 | var Schema = require('./src/schema');
10 | var bodyParser = require('body-parser');
11 | const compiler = webpack(config);
12 | const delay = () => new Promise(resolve => setTimeout(resolve, 1000));
13 |
14 | app.use(bodyParser.json());
15 | app.use(require('webpack-dev-middleware')(compiler, {
16 | noInfo: true,
17 | publicPath: config.output.publicPath
18 | }));
19 | app.use(require('webpack-hot-middleware')(compiler));
20 | app.use('/static', express.static('static'));
21 | app.use('/graphql', function(req, res) {
22 | // const {query, variables} = req.body;
23 | const query = req.body.query;
24 | const variables = req.body.variables;
25 | const authToken = req.user || {};
26 | const context = {authToken};
27 | graphql(Schema, query, null, context, variables)
28 | .then(result => {
29 | if (result.errors) {
30 | console.log(query)
31 | console.log('DEBUG GraphQL Error:', result.errors);
32 |
33 | }
34 | return result;
35 | })
36 | .then(result => {
37 | delay().then(() => res.send(result))
38 | })
39 |
40 | });
41 | app.get('*', function(req, res) {
42 | res.sendFile(__dirname + '/index.html')
43 | });
44 |
45 | app.listen(port, function(error) {
46 | if (error) {
47 | console.error(error)
48 | } else {
49 | console.info("==> Listening on port %s. Open up http://localhost:%s/ in your browser.", port, port)
50 | }
51 | })
52 |
--------------------------------------------------------------------------------
/src/App.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import GraphiQL from 'graphiql';
3 | import fetch from 'isomorphic-fetch';
4 | import gqlSchema from './schema.js';
5 | import { graphql } from 'graphql';
6 |
7 |
8 | GraphiQL.Logo = class Logo extends Component {
9 | render() {
10 | let style = {
11 | fontWeight: 800,
12 | fontSize: 16,
13 | color: "#252525"
14 | };
15 |
16 | return (
17 | Learn GraphQL Sandbox
18 | );
19 | }
20 | }
21 |
22 | export default class App extends Component {
23 | fetchData({query, variables}) {
24 | let queryVariables = {};
25 | try {
26 | queryVariables = JSON.parse(variables);
27 | } catch (ex) {
28 | }
29 | return graphql(gqlSchema, query, null, queryVariables);
30 | }
31 |
32 | render() {
33 | return (
34 |
35 | );
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/src/CashayBook.js:
--------------------------------------------------------------------------------
1 | import React, {Component} from 'react';
2 | import RecentPosts from './RecentPosts';
3 | import LeastRecentPosts from './LeastRecentPosts';
4 | import LatestPostComments from './LatestPostComments';
5 | import {cashay} from 'cashay';
6 | import {connect} from 'react-redux';
7 |
8 | const queryPostCount = `
9 | query {
10 | postCount: getPostCount
11 | }`;
12 |
13 | const mutationHandlers = {
14 | createPost(optimisticVariables, docFromServer, currentResponse, getEntities, invalidate) {
15 | if (optimisticVariables) {
16 | return currentResponse.postCount++;
17 | }
18 | return docFromServer.createPost.postCount;
19 | },
20 | removePostById(optimisticVariables, docFromServer, currentResponse) {
21 | if (optimisticVariables) {
22 | return currentResponse.postCount--;
23 | }
24 | currentResponse.postCount = docFromServer.removePostById.postCount;
25 | return currentResponse;
26 | }
27 | };
28 | const cashayOptions = {
29 | component: 'CashayBook',
30 | mutationHandlers
31 | };
32 | const mapStateToProps = () => ({cashay: cashay.query(queryPostCount, cashayOptions)});
33 |
34 | @connect(mapStateToProps)
35 | export default class CashayBook extends Component {
36 | render() {
37 | const {postCount} = this.props.cashay.data;
38 | const {isComplete} = this.props.cashay;
39 | return (
40 |
41 |
42 |
Welcome to the Cashay book!
43 | {isComplete ? `We currently have ${postCount} posts!` : "LOADING AHH I CANT WAIT IM SO EXCITED"}
44 |
45 |
46 |
47 |
48 | );
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/src/Comment.js:
--------------------------------------------------------------------------------
1 | import React, {Component} from 'react';
2 |
3 | export default class Comment extends Component {
4 | render() {
5 | const {comment} = this.props;
6 | return (
7 |
8 |
{comment.karma} - {comment.content}
9 |
10 | )
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/src/CommentContainer.js:
--------------------------------------------------------------------------------
1 | import React, {Component} from 'react';
2 | import {connect} from 'react-redux';
3 | import {cashay} from 'cashay';
4 | import Comment from './Comment';
5 | import CreateComment from './CreateComment'
6 | // import uuid from 'node-uuid';
7 |
8 | // commentCount: getCommentCount(postId: $postId),
9 | const queryCommentsForPostId = `
10 | query($postId: String!) {
11 | comments: getCommentsByPostId(postId: $postId) {
12 | _id,
13 | content,
14 | karma,
15 | createdAt,
16 | postId
17 |
18 | }
19 | }`;
20 |
21 | // mutation: `
22 | // mutation($postId: String!, $content: String!, $_id: String!) {
23 | // newComment: createComment(postId: $postId, content: $content, _id: $_id) {
24 | // _id,
25 | // content,
26 | // karma,
27 | // createdAt,
28 | // postId
29 | // }
30 | // }`
31 |
32 | const mutationHandlers = {
33 | createComment(optimisticVariables, docFromServer, currentResponse, state, invalidate) {
34 | if (optimisticVariables) {
35 | const {content, postId, _id} = optimisticVariables;
36 | const newComment = {
37 | _id,
38 | content: content + ' OPTMISTICAL!',
39 | postId,
40 | createdAt: Date.now(),
41 | karma: 0
42 | };
43 | const placeBefore = currentResponse.comments.findIndex(comment => comment.karma < newComment.karma);
44 | const spliceLocation = placeBefore !== -1 ? placeBefore : currentResponse.comments.length;
45 | currentResponse.comments.splice(spliceLocation, 0, newComment);
46 | return currentResponse;
47 | }
48 | const optimisticDocIdx = currentResponse.comments.findIndex(comment => comment._id === docFromServer.createComment._id);
49 | currentResponse.comments[optimisticDocIdx] = docFromServer.createComment;
50 | return currentResponse;
51 | }
52 | };
53 |
54 | const mapStateToProps = (state, props) => {
55 | const {postId} = props;
56 | return {
57 | cashay: cashay.query(queryCommentsForPostId, {
58 | variables: {postId},
59 | component: `comments`,
60 | key: postId,
61 | mutationHandlers
62 | })
63 | }
64 | };
65 |
66 | @connect(mapStateToProps)
67 | export default class CommentContainer extends Component {
68 | render() {
69 | const {comments} = this.props.cashay.data;
70 | const {postId} = this.props;
71 | return (
72 |
73 |
{comments.map(comment => )}
74 |
75 |
76 | )
77 | }
78 | }
79 |
--------------------------------------------------------------------------------
/src/CreateComment.js:
--------------------------------------------------------------------------------
1 | import React, {Component} from 'react';
2 | import {cashay} from 'cashay';
3 | import uuid from 'node-uuid';
4 |
5 | export default class CreateComment extends Component {
6 | constructor(props) {
7 | super(props);
8 | this.state = {content: ''};
9 | }
10 |
11 | render() {
12 | return (
13 |
14 | New Comment:
15 |
16 | Post!
17 |
18 | )
19 | }
20 |
21 | handleChange = event => {
22 | this.setState({content: event.target.value.substr(0, 140)});
23 | }
24 |
25 | handleSubmit = () => {
26 | const variables = {
27 | content: this.state.content,
28 | postId: this.props.postId,
29 | _id: uuid.v4()
30 | };
31 | this.setState({content: ''});
32 | cashay.mutate('createComment', {variables, components: {comments: variables.postId}})
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/src/LatestPostComments.js:
--------------------------------------------------------------------------------
1 | import React, {Component} from 'react';
2 | import {cashay} from 'cashay';
3 | import {connect} from 'react-redux';
4 | import Comment from './Comment';
5 | const queryPostCount = `
6 | query {
7 | latestPostId: getLatestPostId
8 | latestPostComments: getCommentsByPostId(postId: $postId) {
9 | _id,
10 | content,
11 | karma
12 | }
13 | }`;
14 |
15 | const cashayOptions = {
16 | component: 'LatestPostComments',
17 | variables: {
18 | postId: (response, cashayDataState) => response.latestPostId
19 | }
20 | };
21 | const mapStateToProps = (state, props) => {
22 | return {
23 | cashayResponse: cashay.query(queryPostCount, cashayOptions)
24 |
25 | }
26 | };
27 |
28 | @connect(mapStateToProps)
29 | export default class LatestPostComments extends Component {
30 | render() {
31 | const {isComplete} = this.props.cashayResponse;
32 | const {latestPostId, latestPostComments} = this.props.cashayResponse.data;
33 | return (
34 |
35 |
36 |
37 |
38 |
LATEST POST COMMENTS
39 | (Example of a multi-part query)
40 |
41 |
Latest post ID: {latestPostId}
42 |
Comments for post
43 |
{latestPostComments.map(comment => )}
44 |
45 | )
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/src/LeastRecentPosts.js:
--------------------------------------------------------------------------------
1 | import React, {Component} from 'react';
2 | import {connect} from 'react-redux';
3 | import {cashay} from 'cashay';
4 | import SinglePost from './SinglePost';
5 | import uuid from 'node-uuid';
6 |
7 | const queryRecentPosts = `
8 | query($count: Int) {
9 | leastRecentPosts: getRecentPosts(last: $count) {
10 | _id,
11 | title,
12 | cursor,
13 | spanishTitle: title(language:"spanish"),
14 | reverseSpanishTitle: title(language:"spanish", inReverse:true)
15 | }
16 | }`;
17 |
18 | const customMutations = {
19 | removePostById: `
20 | mutation($postId: String!) {
21 | removePostById(postId: $postId) {
22 | removedPostId
23 | }
24 | }`
25 | };
26 |
27 | const mutationHandlers = {
28 | createPost(optimisticVariables, docFromServer, currentResponse, state, invalidate) {
29 | if (optimisticVariables) {
30 | const {newPost} = optimisticVariables;
31 | const newOptimisticPost = Object.assign({}, newPost, {
32 | title: `${newPost.title} OPTIMISTICAL!`
33 | });
34 | currentResponse.leastRecentPosts.push(newOptimisticPost);
35 | return currentResponse;
36 | }
37 | const optimisticDocIdx = currentResponse.leastRecentPosts.findIndex(post => post._id === docFromServer.createPost.post._id);
38 | currentResponse.leastRecentPosts[optimisticDocIdx] = docFromServer.createPost.post;
39 | return currentResponse;
40 | },
41 | removePostById(optimisticVariables, docFromServer, currentResponse) {
42 | // example of not using optimistic updates
43 | const idRemoved = docFromServer && docFromServer.removePostById.removedPostId;
44 | if (idRemoved) {
45 | const idx = currentResponse.leastRecentPosts.findIndex(post => post._id === idRemoved);
46 | currentResponse.leastRecentPosts.splice(idx, 1);
47 | return currentResponse;
48 | }
49 | }
50 | };
51 |
52 | const mapStateToProps = (state, props) => {
53 | return {
54 | cashay: cashay.query(queryRecentPosts, {
55 | variables: {count: 2},
56 | mutationHandlers,
57 | customMutations,
58 | component: 'LeastRecentPosts'
59 | })
60 | }
61 | };
62 |
63 | @connect(mapStateToProps)
64 | export default class LeastRecentPosts extends Component {
65 | render() {
66 | const {leastRecentPosts} = this.props.cashay.data;
67 | return (
68 |
69 |
LEAST RECENT POSTS
70 |
Create your very own post!
71 |
{leastRecentPosts.map(post => )}
72 | {leastRecentPosts.BOF ? null :
73 |
Get 2 More
74 | }
75 |
76 | );
77 | }
78 |
79 | createPost = () => {
80 | const variables = {
81 | newPost: {
82 | _id: uuid.v4(),
83 | title: 'Woo another post!'
84 | }
85 | };
86 | cashay.mutate('createPost', {variables})
87 | };
88 |
89 | get2More = () => {
90 | const {setVariables} = this.props.cashay;
91 | setVariables(currentVariables => {
92 | return {
93 | count: currentVariables.count + 2
94 | }
95 | })
96 | }
97 | }
98 |
--------------------------------------------------------------------------------
/src/SinglePost.js:
--------------------------------------------------------------------------------
1 | import React, {Component} from 'react';
2 | import CommentContainer from './CommentContainer';
3 | import {cashay} from 'cashay';
4 |
5 | export default class SinglePost extends Component {
6 | constructor(props) {
7 | super(props);
8 | this.state = {showComments: false}
9 | }
10 | render() {
11 | const {post} = this.props;
12 | const {showComments} = this.state;
13 | return (
14 |
15 |
16 | {post.title}
17 | Try to Delete Me
18 |
19 | {showComments ?
: null}
20 |
21 | {showComments ? 'Hide Comments' : 'Show Comments'}
22 |
23 |
24 |
25 | )
26 | }
27 | deletePost = () => {
28 | const variables = {
29 | postId: this.props.post._id
30 | };
31 | cashay.mutate('removePostById', {variables});
32 | };
33 |
34 | toggleComments = () => {
35 | this.setState({showComments: !this.state.showComments})
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/src/data/authors.js:
--------------------------------------------------------------------------------
1 | export default [
2 | {
3 | _id: 'a123',
4 | name: 'Matt K',
5 | twitterHandle: 's@__mattk'
6 | },
7 | {
8 | _id: 'a124',
9 | name: 'Joe J',
10 | twitterHandle: '@__joej'
11 | },
12 | {
13 | _id: 'a125',
14 | name: 'Rob R',
15 | twitterHandle: '@__robr'
16 | }
17 | ]
18 |
--------------------------------------------------------------------------------
/src/data/comments.js:
--------------------------------------------------------------------------------
1 | export default [
2 | {
3 | _id: 'c123',
4 | cursor: 'c123',
5 | content: 'This is great!',
6 | author: 'a125',
7 | postId: 'p126',
8 | karma: 5,
9 | createdAt: 1463074881798
10 | },
11 | {
12 | _id: 'c124',
13 | cursor: 'c124',
14 | content: 'Do you want to make $67,000/hr working from home? This single mom can do it!',
15 | author: 'a124',
16 | postId: 'p126',
17 | karma: -100,
18 | createdAt: 1463078881798
19 | }
20 | ]
21 |
--------------------------------------------------------------------------------
/src/data/groups.js:
--------------------------------------------------------------------------------
1 | export default [
2 | {
3 | name: 'executive team',
4 | _id: 'g123',
5 | ownerId: 'a123',
6 | members: ['a123', 'a124']
7 | },
8 | {
9 | name: 'all employees',
10 | _id: 'g124',
11 | ownerId: 'a123',
12 | members: ['a125', 'g123']
13 | }
14 | ]
15 |
--------------------------------------------------------------------------------
/src/data/posts.js:
--------------------------------------------------------------------------------
1 | export default [
2 | {
3 | _id: 'p123',
4 | author: 'a123',
5 | content: 'Cashay is neat',
6 | title: 'What is cashay?',
7 | title_ES: 'What is cashay? EN ESPANOL!',
8 | category: 'hot stuff',
9 | karma: 2,
10 | createdAt: 1411111111111,
11 | cursor: '1411111111111' + 'chikachikow'
12 | },
13 | {
14 | _id: 'p124',
15 | author: 'a123',
16 | content: 'Cashay uses the redux store to hold all of its normalized data and variables',
17 | title: 'Does cashay work with redux?',
18 | title_ES: 'Does cashay work with redux? EN ESPANOL!',
19 | category: 'hot stuff',
20 | karma: 2,
21 | createdAt: 1422222222222,
22 | cursor: '1422222222222' + 'chikachikow'
23 | },
24 | {
25 | _id: 'p125',
26 | author: 'a124',
27 | content: 'Benchmarks are usually written by people who have a bias. You should write your own (but yes)',
28 | title: 'Is cashay smaller or faster than relay?',
29 | title_ES: 'Is cashay smaller or faster than relay? EN ESPANOL!',
30 | category: 'ice cold',
31 | karma: 2,
32 | createdAt: 1433333333333,
33 | cursor: '1433333333333' + 'chikachikow'
34 | },
35 | {
36 | _id: 'p126',
37 | author: 'a124',
38 | content: 'Denormalized data is cached in the cashay singleton',
39 | title: 'How does cashay store denormalized data?',
40 | title_ES: 'How does cashay store denormalized data? EN ESPANOL!',
41 | category: 'hot stuff',
42 | karma: 2,
43 | createdAt: 1444444444444,
44 | cursor: '1444444444444' + 'chikachikow'
45 | }
46 | ]
47 |
--------------------------------------------------------------------------------
/src/getCashaySchema.js:
--------------------------------------------------------------------------------
1 | require('babel-register');
2 | require('babel-polyfill');
3 | const {transformSchema} = require('cashay');
4 | const graphql = require('graphql').graphql;
5 | const rootSchema = require('./schema');
6 | module.exports = transformSchema(rootSchema, graphql);
7 |
--------------------------------------------------------------------------------
/src/graphiqlSchema.json:
--------------------------------------------------------------------------------
1 | {
2 | "queryType": {
3 | "name": "BlogSchema"
4 | },
5 | "mutationType": {
6 | "name": "BlogMutations"
7 | },
8 | "subscriptionType": null,
9 | "types": [
10 | {
11 | "kind": "OBJECT",
12 | "name": "BlogSchema",
13 | "fields": [
14 | {
15 | "name": "getPostCount",
16 | "args": [],
17 | "type": {
18 | "kind": "NON_NULL",
19 | "name": null,
20 | "ofType": {
21 | "kind": "SCALAR",
22 | "name": "Int",
23 | "ofType": null
24 | }
25 | }
26 | },
27 | {
28 | "name": "getLatestPost",
29 | "args": [],
30 | "type": {
31 | "kind": "OBJECT",
32 | "name": "PostType",
33 | "ofType": null
34 | }
35 | },
36 | {
37 | "name": "getRecentPosts",
38 | "args": [
39 | {
40 | "name": "beforeCursor",
41 | "type": {
42 | "kind": "SCALAR",
43 | "name": "String",
44 | "ofType": null
45 | },
46 | "defaultValue": null
47 | },
48 | {
49 | "name": "afterCursor",
50 | "type": {
51 | "kind": "SCALAR",
52 | "name": "String",
53 | "ofType": null
54 | },
55 | "defaultValue": null
56 | },
57 | {
58 | "name": "first",
59 | "type": {
60 | "kind": "SCALAR",
61 | "name": "Int",
62 | "ofType": null
63 | },
64 | "defaultValue": null
65 | },
66 | {
67 | "name": "last",
68 | "type": {
69 | "kind": "SCALAR",
70 | "name": "Int",
71 | "ofType": null
72 | },
73 | "defaultValue": null
74 | }
75 | ],
76 | "type": {
77 | "kind": "LIST",
78 | "name": null,
79 | "ofType": {
80 | "kind": "OBJECT",
81 | "name": "PostType",
82 | "ofType": null
83 | }
84 | }
85 | },
86 | {
87 | "name": "getPostById",
88 | "args": [
89 | {
90 | "name": "_id",
91 | "type": {
92 | "kind": "NON_NULL",
93 | "name": null,
94 | "ofType": {
95 | "kind": "SCALAR",
96 | "name": "String",
97 | "ofType": null
98 | }
99 | },
100 | "defaultValue": null
101 | }
102 | ],
103 | "type": {
104 | "kind": "OBJECT",
105 | "name": "PostType",
106 | "ofType": null
107 | }
108 | },
109 | {
110 | "name": "getGroup",
111 | "args": [
112 | {
113 | "name": "_id",
114 | "type": {
115 | "kind": "NON_NULL",
116 | "name": null,
117 | "ofType": {
118 | "kind": "SCALAR",
119 | "name": "String",
120 | "ofType": null
121 | }
122 | },
123 | "defaultValue": null
124 | }
125 | ],
126 | "type": {
127 | "kind": "OBJECT",
128 | "name": "Group",
129 | "ofType": null
130 | }
131 | },
132 | {
133 | "name": "getCommentsByPostId",
134 | "args": [
135 | {
136 | "name": "postId",
137 | "type": {
138 | "kind": "NON_NULL",
139 | "name": null,
140 | "ofType": {
141 | "kind": "SCALAR",
142 | "name": "String",
143 | "ofType": null
144 | }
145 | },
146 | "defaultValue": null
147 | }
148 | ],
149 | "type": {
150 | "kind": "LIST",
151 | "name": null,
152 | "ofType": {
153 | "kind": "OBJECT",
154 | "name": "CommentType",
155 | "ofType": null
156 | }
157 | }
158 | }
159 | ],
160 | "inputFields": null,
161 | "interfaces": [],
162 | "enumValues": null,
163 | "possibleTypes": null
164 | },
165 | {
166 | "kind": "SCALAR",
167 | "name": "Int",
168 | "fields": null,
169 | "inputFields": null,
170 | "interfaces": null,
171 | "enumValues": null,
172 | "possibleTypes": null
173 | },
174 | {
175 | "kind": "OBJECT",
176 | "name": "PostType",
177 | "fields": [
178 | {
179 | "name": "_id",
180 | "args": [],
181 | "type": {
182 | "kind": "SCALAR",
183 | "name": "String",
184 | "ofType": null
185 | }
186 | },
187 | {
188 | "name": "title",
189 | "args": [
190 | {
191 | "name": "language",
192 | "type": {
193 | "kind": "SCALAR",
194 | "name": "String",
195 | "ofType": null
196 | },
197 | "defaultValue": null
198 | },
199 | {
200 | "name": "inReverse",
201 | "type": {
202 | "kind": "SCALAR",
203 | "name": "Boolean",
204 | "ofType": null
205 | },
206 | "defaultValue": null
207 | }
208 | ],
209 | "type": {
210 | "kind": "SCALAR",
211 | "name": "String",
212 | "ofType": null
213 | }
214 | },
215 | {
216 | "name": "category",
217 | "args": [],
218 | "type": {
219 | "kind": "ENUM",
220 | "name": "CategoryType",
221 | "ofType": null
222 | }
223 | },
224 | {
225 | "name": "content",
226 | "args": [],
227 | "type": {
228 | "kind": "SCALAR",
229 | "name": "String",
230 | "ofType": null
231 | }
232 | },
233 | {
234 | "name": "createdAt",
235 | "args": [
236 | {
237 | "name": "dateOptions",
238 | "type": {
239 | "kind": "INPUT_OBJECT",
240 | "name": "DateOptions",
241 | "ofType": null
242 | },
243 | "defaultValue": null
244 | }
245 | ],
246 | "type": {
247 | "kind": "SCALAR",
248 | "name": "Int",
249 | "ofType": null
250 | }
251 | },
252 | {
253 | "name": "comments",
254 | "args": [
255 | {
256 | "name": "beforeCursor",
257 | "type": {
258 | "kind": "SCALAR",
259 | "name": "String",
260 | "ofType": null
261 | },
262 | "defaultValue": null
263 | },
264 | {
265 | "name": "afterCursor",
266 | "type": {
267 | "kind": "SCALAR",
268 | "name": "String",
269 | "ofType": null
270 | },
271 | "defaultValue": null
272 | },
273 | {
274 | "name": "first",
275 | "type": {
276 | "kind": "SCALAR",
277 | "name": "Int",
278 | "ofType": null
279 | },
280 | "defaultValue": null
281 | },
282 | {
283 | "name": "last",
284 | "type": {
285 | "kind": "SCALAR",
286 | "name": "Int",
287 | "ofType": null
288 | },
289 | "defaultValue": null
290 | }
291 | ],
292 | "type": {
293 | "kind": "LIST",
294 | "name": null,
295 | "ofType": {
296 | "kind": "OBJECT",
297 | "name": "CommentType",
298 | "ofType": null
299 | }
300 | }
301 | },
302 | {
303 | "name": "author",
304 | "args": [],
305 | "type": {
306 | "kind": "OBJECT",
307 | "name": "AuthorType",
308 | "ofType": null
309 | }
310 | },
311 | {
312 | "name": "cursor",
313 | "args": [],
314 | "type": {
315 | "kind": "SCALAR",
316 | "name": "String",
317 | "ofType": null
318 | }
319 | }
320 | ],
321 | "inputFields": null,
322 | "interfaces": [
323 | {
324 | "kind": "INTERFACE",
325 | "name": "HasAuthorType",
326 | "ofType": null
327 | }
328 | ],
329 | "enumValues": null,
330 | "possibleTypes": null
331 | },
332 | {
333 | "kind": "INTERFACE",
334 | "name": "HasAuthorType",
335 | "fields": [
336 | {
337 | "name": "author",
338 | "args": [],
339 | "type": {
340 | "kind": "OBJECT",
341 | "name": "AuthorType",
342 | "ofType": null
343 | }
344 | }
345 | ],
346 | "inputFields": null,
347 | "interfaces": null,
348 | "enumValues": null,
349 | "possibleTypes": [
350 | {
351 | "kind": "OBJECT",
352 | "name": "PostType",
353 | "ofType": null
354 | },
355 | {
356 | "kind": "OBJECT",
357 | "name": "CommentType",
358 | "ofType": null
359 | }
360 | ]
361 | },
362 | {
363 | "kind": "OBJECT",
364 | "name": "AuthorType",
365 | "fields": [
366 | {
367 | "name": "_id",
368 | "args": [],
369 | "type": {
370 | "kind": "SCALAR",
371 | "name": "String",
372 | "ofType": null
373 | }
374 | },
375 | {
376 | "name": "name",
377 | "args": [],
378 | "type": {
379 | "kind": "SCALAR",
380 | "name": "String",
381 | "ofType": null
382 | }
383 | },
384 | {
385 | "name": "twitterHandle",
386 | "args": [],
387 | "type": {
388 | "kind": "SCALAR",
389 | "name": "String",
390 | "ofType": null
391 | }
392 | }
393 | ],
394 | "inputFields": null,
395 | "interfaces": [],
396 | "enumValues": null,
397 | "possibleTypes": null
398 | },
399 | {
400 | "kind": "SCALAR",
401 | "name": "String",
402 | "fields": null,
403 | "inputFields": null,
404 | "interfaces": null,
405 | "enumValues": null,
406 | "possibleTypes": null
407 | },
408 | {
409 | "kind": "SCALAR",
410 | "name": "Boolean",
411 | "fields": null,
412 | "inputFields": null,
413 | "interfaces": null,
414 | "enumValues": null,
415 | "possibleTypes": null
416 | },
417 | {
418 | "kind": "ENUM",
419 | "name": "CategoryType",
420 | "fields": null,
421 | "inputFields": null,
422 | "interfaces": null,
423 | "enumValues": [
424 | {
425 | "name": "HOT_STUFF"
426 | },
427 | {
428 | "name": "ICE_COLD"
429 | }
430 | ],
431 | "possibleTypes": null
432 | },
433 | {
434 | "kind": "INPUT_OBJECT",
435 | "name": "DateOptions",
436 | "fields": null,
437 | "inputFields": [
438 | {
439 | "name": "day",
440 | "type": {
441 | "kind": "SCALAR",
442 | "name": "Boolean",
443 | "ofType": null
444 | },
445 | "defaultValue": null
446 | },
447 | {
448 | "name": "month",
449 | "type": {
450 | "kind": "SCALAR",
451 | "name": "Boolean",
452 | "ofType": null
453 | },
454 | "defaultValue": null
455 | },
456 | {
457 | "name": "year",
458 | "type": {
459 | "kind": "SCALAR",
460 | "name": "Boolean",
461 | "ofType": null
462 | },
463 | "defaultValue": null
464 | }
465 | ],
466 | "interfaces": null,
467 | "enumValues": null,
468 | "possibleTypes": null
469 | },
470 | {
471 | "kind": "OBJECT",
472 | "name": "CommentType",
473 | "fields": [
474 | {
475 | "name": "_id",
476 | "args": [],
477 | "type": {
478 | "kind": "SCALAR",
479 | "name": "String",
480 | "ofType": null
481 | }
482 | },
483 | {
484 | "name": "content",
485 | "args": [],
486 | "type": {
487 | "kind": "SCALAR",
488 | "name": "String",
489 | "ofType": null
490 | }
491 | },
492 | {
493 | "name": "author",
494 | "args": [],
495 | "type": {
496 | "kind": "OBJECT",
497 | "name": "AuthorType",
498 | "ofType": null
499 | }
500 | },
501 | {
502 | "name": "createdAt",
503 | "args": [],
504 | "type": {
505 | "kind": "SCALAR",
506 | "name": "Int",
507 | "ofType": null
508 | }
509 | },
510 | {
511 | "name": "cursor",
512 | "args": [],
513 | "type": {
514 | "kind": "SCALAR",
515 | "name": "String",
516 | "ofType": null
517 | }
518 | }
519 | ],
520 | "inputFields": null,
521 | "interfaces": [
522 | {
523 | "kind": "INTERFACE",
524 | "name": "HasAuthorType",
525 | "ofType": null
526 | }
527 | ],
528 | "enumValues": null,
529 | "possibleTypes": null
530 | },
531 | {
532 | "kind": "OBJECT",
533 | "name": "Group",
534 | "fields": [
535 | {
536 | "name": "_id",
537 | "args": [],
538 | "type": {
539 | "kind": "SCALAR",
540 | "name": "String",
541 | "ofType": null
542 | }
543 | },
544 | {
545 | "name": "owner",
546 | "args": [],
547 | "type": {
548 | "kind": "UNION",
549 | "name": "Member",
550 | "ofType": null
551 | }
552 | },
553 | {
554 | "name": "members",
555 | "args": [],
556 | "type": {
557 | "kind": "LIST",
558 | "name": null,
559 | "ofType": {
560 | "kind": "UNION",
561 | "name": "Member",
562 | "ofType": null
563 | }
564 | }
565 | }
566 | ],
567 | "inputFields": null,
568 | "interfaces": [],
569 | "enumValues": null,
570 | "possibleTypes": null
571 | },
572 | {
573 | "kind": "UNION",
574 | "name": "Member",
575 | "fields": null,
576 | "inputFields": null,
577 | "interfaces": null,
578 | "enumValues": null,
579 | "possibleTypes": [
580 | {
581 | "kind": "OBJECT",
582 | "name": "Group",
583 | "ofType": null
584 | },
585 | {
586 | "kind": "OBJECT",
587 | "name": "AuthorType",
588 | "ofType": null
589 | }
590 | ]
591 | },
592 | {
593 | "kind": "OBJECT",
594 | "name": "BlogMutations",
595 | "fields": [
596 | {
597 | "name": "createPost",
598 | "args": [
599 | {
600 | "name": "newPost",
601 | "type": {
602 | "kind": "NON_NULL",
603 | "name": null,
604 | "ofType": {
605 | "kind": "INPUT_OBJECT",
606 | "name": "NewPost",
607 | "ofType": null
608 | }
609 | },
610 | "defaultValue": null
611 | },
612 | {
613 | "name": "author",
614 | "type": {
615 | "kind": "SCALAR",
616 | "name": "String",
617 | "ofType": null
618 | },
619 | "defaultValue": null
620 | }
621 | ],
622 | "type": {
623 | "kind": "OBJECT",
624 | "name": "CreatePostMutationPayload",
625 | "ofType": null
626 | }
627 | },
628 | {
629 | "name": "createComment",
630 | "args": [
631 | {
632 | "name": "_id",
633 | "type": {
634 | "kind": "NON_NULL",
635 | "name": null,
636 | "ofType": {
637 | "kind": "SCALAR",
638 | "name": "String",
639 | "ofType": null
640 | }
641 | },
642 | "defaultValue": null
643 | },
644 | {
645 | "name": "postId",
646 | "type": {
647 | "kind": "NON_NULL",
648 | "name": null,
649 | "ofType": {
650 | "kind": "SCALAR",
651 | "name": "String",
652 | "ofType": null
653 | }
654 | },
655 | "defaultValue": null
656 | },
657 | {
658 | "name": "content",
659 | "type": {
660 | "kind": "NON_NULL",
661 | "name": null,
662 | "ofType": {
663 | "kind": "SCALAR",
664 | "name": "String",
665 | "ofType": null
666 | }
667 | },
668 | "defaultValue": null
669 | }
670 | ],
671 | "type": {
672 | "kind": "OBJECT",
673 | "name": "CommentType",
674 | "ofType": null
675 | }
676 | },
677 | {
678 | "name": "createMembers",
679 | "args": [
680 | {
681 | "name": "members",
682 | "type": {
683 | "kind": "NON_NULL",
684 | "name": null,
685 | "ofType": {
686 | "kind": "LIST",
687 | "name": null,
688 | "ofType": {
689 | "kind": "NON_NULL",
690 | "name": null,
691 | "ofType": {
692 | "kind": "INPUT_OBJECT",
693 | "name": "NewMember"
694 | }
695 | }
696 | }
697 | },
698 | "defaultValue": null
699 | }
700 | ],
701 | "type": {
702 | "kind": "LIST",
703 | "name": null,
704 | "ofType": {
705 | "kind": "UNION",
706 | "name": "Member",
707 | "ofType": null
708 | }
709 | }
710 | }
711 | ],
712 | "inputFields": null,
713 | "interfaces": [],
714 | "enumValues": null,
715 | "possibleTypes": null
716 | },
717 | {
718 | "kind": "INPUT_OBJECT",
719 | "name": "NewPost",
720 | "fields": null,
721 | "inputFields": [
722 | {
723 | "name": "_id",
724 | "type": {
725 | "kind": "NON_NULL",
726 | "name": null,
727 | "ofType": {
728 | "kind": "SCALAR",
729 | "name": "String",
730 | "ofType": null
731 | }
732 | },
733 | "defaultValue": null
734 | },
735 | {
736 | "name": "content",
737 | "type": {
738 | "kind": "SCALAR",
739 | "name": "String",
740 | "ofType": null
741 | },
742 | "defaultValue": null
743 | },
744 | {
745 | "name": "title",
746 | "type": {
747 | "kind": "SCALAR",
748 | "name": "String",
749 | "ofType": null
750 | },
751 | "defaultValue": null
752 | },
753 | {
754 | "name": "category",
755 | "type": {
756 | "kind": "SCALAR",
757 | "name": "String",
758 | "ofType": null
759 | },
760 | "defaultValue": null
761 | }
762 | ],
763 | "interfaces": null,
764 | "enumValues": null,
765 | "possibleTypes": null
766 | },
767 | {
768 | "kind": "OBJECT",
769 | "name": "CreatePostMutationPayload",
770 | "fields": [
771 | {
772 | "name": "post",
773 | "args": [],
774 | "type": {
775 | "kind": "OBJECT",
776 | "name": "PostType",
777 | "ofType": null
778 | }
779 | },
780 | {
781 | "name": "postCount",
782 | "args": [],
783 | "type": {
784 | "kind": "SCALAR",
785 | "name": "Int",
786 | "ofType": null
787 | }
788 | }
789 | ],
790 | "inputFields": null,
791 | "interfaces": [],
792 | "enumValues": null,
793 | "possibleTypes": null
794 | },
795 | {
796 | "kind": "INPUT_OBJECT",
797 | "name": "NewMember",
798 | "fields": null,
799 | "inputFields": [
800 | {
801 | "name": "_id",
802 | "type": {
803 | "kind": "NON_NULL",
804 | "name": null,
805 | "ofType": {
806 | "kind": "SCALAR",
807 | "name": "String",
808 | "ofType": null
809 | }
810 | },
811 | "defaultValue": null
812 | },
813 | {
814 | "name": "name",
815 | "type": {
816 | "kind": "SCALAR",
817 | "name": "String",
818 | "ofType": null
819 | },
820 | "defaultValue": null
821 | },
822 | {
823 | "name": "ownerId",
824 | "type": {
825 | "kind": "SCALAR",
826 | "name": "String",
827 | "ofType": null
828 | },
829 | "defaultValue": null
830 | },
831 | {
832 | "name": "members",
833 | "type": {
834 | "kind": "LIST",
835 | "name": null,
836 | "ofType": {
837 | "kind": "SCALAR",
838 | "name": "String",
839 | "ofType": null
840 | }
841 | },
842 | "defaultValue": null
843 | },
844 | {
845 | "name": "twitterHandle",
846 | "type": {
847 | "kind": "SCALAR",
848 | "name": "String",
849 | "ofType": null
850 | },
851 | "defaultValue": null
852 | }
853 | ],
854 | "interfaces": null,
855 | "enumValues": null,
856 | "possibleTypes": null
857 | },
858 | {
859 | "kind": "OBJECT",
860 | "name": "__Schema",
861 | "fields": [
862 | {
863 | "name": "types",
864 | "args": [],
865 | "type": {
866 | "kind": "NON_NULL",
867 | "name": null,
868 | "ofType": {
869 | "kind": "LIST",
870 | "name": null,
871 | "ofType": {
872 | "kind": "NON_NULL",
873 | "name": null,
874 | "ofType": {
875 | "kind": "OBJECT",
876 | "name": "__Type"
877 | }
878 | }
879 | }
880 | }
881 | },
882 | {
883 | "name": "queryType",
884 | "args": [],
885 | "type": {
886 | "kind": "NON_NULL",
887 | "name": null,
888 | "ofType": {
889 | "kind": "OBJECT",
890 | "name": "__Type",
891 | "ofType": null
892 | }
893 | }
894 | },
895 | {
896 | "name": "mutationType",
897 | "args": [],
898 | "type": {
899 | "kind": "OBJECT",
900 | "name": "__Type",
901 | "ofType": null
902 | }
903 | },
904 | {
905 | "name": "subscriptionType",
906 | "args": [],
907 | "type": {
908 | "kind": "OBJECT",
909 | "name": "__Type",
910 | "ofType": null
911 | }
912 | },
913 | {
914 | "name": "directives",
915 | "args": [],
916 | "type": {
917 | "kind": "NON_NULL",
918 | "name": null,
919 | "ofType": {
920 | "kind": "LIST",
921 | "name": null,
922 | "ofType": {
923 | "kind": "NON_NULL",
924 | "name": null,
925 | "ofType": {
926 | "kind": "OBJECT",
927 | "name": "__Directive"
928 | }
929 | }
930 | }
931 | }
932 | }
933 | ],
934 | "inputFields": null,
935 | "interfaces": [],
936 | "enumValues": null,
937 | "possibleTypes": null
938 | },
939 | {
940 | "kind": "OBJECT",
941 | "name": "__Type",
942 | "fields": [
943 | {
944 | "name": "kind",
945 | "args": [],
946 | "type": {
947 | "kind": "NON_NULL",
948 | "name": null,
949 | "ofType": {
950 | "kind": "ENUM",
951 | "name": "__TypeKind",
952 | "ofType": null
953 | }
954 | }
955 | },
956 | {
957 | "name": "name",
958 | "args": [],
959 | "type": {
960 | "kind": "SCALAR",
961 | "name": "String",
962 | "ofType": null
963 | }
964 | },
965 | {
966 | "name": "description",
967 | "args": [],
968 | "type": {
969 | "kind": "SCALAR",
970 | "name": "String",
971 | "ofType": null
972 | }
973 | },
974 | {
975 | "name": "fields",
976 | "args": [
977 | {
978 | "name": "includeDeprecated",
979 | "type": {
980 | "kind": "SCALAR",
981 | "name": "Boolean",
982 | "ofType": null
983 | },
984 | "defaultValue": "false"
985 | }
986 | ],
987 | "type": {
988 | "kind": "LIST",
989 | "name": null,
990 | "ofType": {
991 | "kind": "NON_NULL",
992 | "name": null,
993 | "ofType": {
994 | "kind": "OBJECT",
995 | "name": "__Field",
996 | "ofType": null
997 | }
998 | }
999 | }
1000 | },
1001 | {
1002 | "name": "interfaces",
1003 | "args": [],
1004 | "type": {
1005 | "kind": "LIST",
1006 | "name": null,
1007 | "ofType": {
1008 | "kind": "NON_NULL",
1009 | "name": null,
1010 | "ofType": {
1011 | "kind": "OBJECT",
1012 | "name": "__Type",
1013 | "ofType": null
1014 | }
1015 | }
1016 | }
1017 | },
1018 | {
1019 | "name": "possibleTypes",
1020 | "args": [],
1021 | "type": {
1022 | "kind": "LIST",
1023 | "name": null,
1024 | "ofType": {
1025 | "kind": "NON_NULL",
1026 | "name": null,
1027 | "ofType": {
1028 | "kind": "OBJECT",
1029 | "name": "__Type",
1030 | "ofType": null
1031 | }
1032 | }
1033 | }
1034 | },
1035 | {
1036 | "name": "enumValues",
1037 | "args": [
1038 | {
1039 | "name": "includeDeprecated",
1040 | "type": {
1041 | "kind": "SCALAR",
1042 | "name": "Boolean",
1043 | "ofType": null
1044 | },
1045 | "defaultValue": "false"
1046 | }
1047 | ],
1048 | "type": {
1049 | "kind": "LIST",
1050 | "name": null,
1051 | "ofType": {
1052 | "kind": "NON_NULL",
1053 | "name": null,
1054 | "ofType": {
1055 | "kind": "OBJECT",
1056 | "name": "__EnumValue",
1057 | "ofType": null
1058 | }
1059 | }
1060 | }
1061 | },
1062 | {
1063 | "name": "inputFields",
1064 | "args": [],
1065 | "type": {
1066 | "kind": "LIST",
1067 | "name": null,
1068 | "ofType": {
1069 | "kind": "NON_NULL",
1070 | "name": null,
1071 | "ofType": {
1072 | "kind": "OBJECT",
1073 | "name": "__InputValue",
1074 | "ofType": null
1075 | }
1076 | }
1077 | }
1078 | },
1079 | {
1080 | "name": "ofType",
1081 | "args": [],
1082 | "type": {
1083 | "kind": "OBJECT",
1084 | "name": "__Type",
1085 | "ofType": null
1086 | }
1087 | }
1088 | ],
1089 | "inputFields": null,
1090 | "interfaces": [],
1091 | "enumValues": null,
1092 | "possibleTypes": null
1093 | },
1094 | {
1095 | "kind": "ENUM",
1096 | "name": "__TypeKind",
1097 | "fields": null,
1098 | "inputFields": null,
1099 | "interfaces": null,
1100 | "enumValues": [
1101 | {
1102 | "name": "SCALAR"
1103 | },
1104 | {
1105 | "name": "OBJECT"
1106 | },
1107 | {
1108 | "name": "INTERFACE"
1109 | },
1110 | {
1111 | "name": "UNION"
1112 | },
1113 | {
1114 | "name": "ENUM"
1115 | },
1116 | {
1117 | "name": "INPUT_OBJECT"
1118 | },
1119 | {
1120 | "name": "LIST"
1121 | },
1122 | {
1123 | "name": "NON_NULL"
1124 | }
1125 | ],
1126 | "possibleTypes": null
1127 | },
1128 | {
1129 | "kind": "OBJECT",
1130 | "name": "__Field",
1131 | "fields": [
1132 | {
1133 | "name": "name",
1134 | "args": [],
1135 | "type": {
1136 | "kind": "NON_NULL",
1137 | "name": null,
1138 | "ofType": {
1139 | "kind": "SCALAR",
1140 | "name": "String",
1141 | "ofType": null
1142 | }
1143 | }
1144 | },
1145 | {
1146 | "name": "description",
1147 | "args": [],
1148 | "type": {
1149 | "kind": "SCALAR",
1150 | "name": "String",
1151 | "ofType": null
1152 | }
1153 | },
1154 | {
1155 | "name": "args",
1156 | "args": [],
1157 | "type": {
1158 | "kind": "NON_NULL",
1159 | "name": null,
1160 | "ofType": {
1161 | "kind": "LIST",
1162 | "name": null,
1163 | "ofType": {
1164 | "kind": "NON_NULL",
1165 | "name": null,
1166 | "ofType": {
1167 | "kind": "OBJECT",
1168 | "name": "__InputValue"
1169 | }
1170 | }
1171 | }
1172 | }
1173 | },
1174 | {
1175 | "name": "type",
1176 | "args": [],
1177 | "type": {
1178 | "kind": "NON_NULL",
1179 | "name": null,
1180 | "ofType": {
1181 | "kind": "OBJECT",
1182 | "name": "__Type",
1183 | "ofType": null
1184 | }
1185 | }
1186 | },
1187 | {
1188 | "name": "isDeprecated",
1189 | "args": [],
1190 | "type": {
1191 | "kind": "NON_NULL",
1192 | "name": null,
1193 | "ofType": {
1194 | "kind": "SCALAR",
1195 | "name": "Boolean",
1196 | "ofType": null
1197 | }
1198 | }
1199 | },
1200 | {
1201 | "name": "deprecationReason",
1202 | "args": [],
1203 | "type": {
1204 | "kind": "SCALAR",
1205 | "name": "String",
1206 | "ofType": null
1207 | }
1208 | }
1209 | ],
1210 | "inputFields": null,
1211 | "interfaces": [],
1212 | "enumValues": null,
1213 | "possibleTypes": null
1214 | },
1215 | {
1216 | "kind": "OBJECT",
1217 | "name": "__InputValue",
1218 | "fields": [
1219 | {
1220 | "name": "name",
1221 | "args": [],
1222 | "type": {
1223 | "kind": "NON_NULL",
1224 | "name": null,
1225 | "ofType": {
1226 | "kind": "SCALAR",
1227 | "name": "String",
1228 | "ofType": null
1229 | }
1230 | }
1231 | },
1232 | {
1233 | "name": "description",
1234 | "args": [],
1235 | "type": {
1236 | "kind": "SCALAR",
1237 | "name": "String",
1238 | "ofType": null
1239 | }
1240 | },
1241 | {
1242 | "name": "type",
1243 | "args": [],
1244 | "type": {
1245 | "kind": "NON_NULL",
1246 | "name": null,
1247 | "ofType": {
1248 | "kind": "OBJECT",
1249 | "name": "__Type",
1250 | "ofType": null
1251 | }
1252 | }
1253 | },
1254 | {
1255 | "name": "defaultValue",
1256 | "args": [],
1257 | "type": {
1258 | "kind": "SCALAR",
1259 | "name": "String",
1260 | "ofType": null
1261 | }
1262 | }
1263 | ],
1264 | "inputFields": null,
1265 | "interfaces": [],
1266 | "enumValues": null,
1267 | "possibleTypes": null
1268 | },
1269 | {
1270 | "kind": "OBJECT",
1271 | "name": "__EnumValue",
1272 | "fields": [
1273 | {
1274 | "name": "name",
1275 | "args": [],
1276 | "type": {
1277 | "kind": "NON_NULL",
1278 | "name": null,
1279 | "ofType": {
1280 | "kind": "SCALAR",
1281 | "name": "String",
1282 | "ofType": null
1283 | }
1284 | }
1285 | },
1286 | {
1287 | "name": "description",
1288 | "args": [],
1289 | "type": {
1290 | "kind": "SCALAR",
1291 | "name": "String",
1292 | "ofType": null
1293 | }
1294 | },
1295 | {
1296 | "name": "isDeprecated",
1297 | "args": [],
1298 | "type": {
1299 | "kind": "NON_NULL",
1300 | "name": null,
1301 | "ofType": {
1302 | "kind": "SCALAR",
1303 | "name": "Boolean",
1304 | "ofType": null
1305 | }
1306 | }
1307 | },
1308 | {
1309 | "name": "deprecationReason",
1310 | "args": [],
1311 | "type": {
1312 | "kind": "SCALAR",
1313 | "name": "String",
1314 | "ofType": null
1315 | }
1316 | }
1317 | ],
1318 | "inputFields": null,
1319 | "interfaces": [],
1320 | "enumValues": null,
1321 | "possibleTypes": null
1322 | },
1323 | {
1324 | "kind": "OBJECT",
1325 | "name": "__Directive",
1326 | "fields": [
1327 | {
1328 | "name": "name",
1329 | "args": [],
1330 | "type": {
1331 | "kind": "NON_NULL",
1332 | "name": null,
1333 | "ofType": {
1334 | "kind": "SCALAR",
1335 | "name": "String",
1336 | "ofType": null
1337 | }
1338 | }
1339 | },
1340 | {
1341 | "name": "description",
1342 | "args": [],
1343 | "type": {
1344 | "kind": "SCALAR",
1345 | "name": "String",
1346 | "ofType": null
1347 | }
1348 | },
1349 | {
1350 | "name": "locations",
1351 | "args": [],
1352 | "type": {
1353 | "kind": "NON_NULL",
1354 | "name": null,
1355 | "ofType": {
1356 | "kind": "LIST",
1357 | "name": null,
1358 | "ofType": {
1359 | "kind": "NON_NULL",
1360 | "name": null,
1361 | "ofType": {
1362 | "kind": "ENUM",
1363 | "name": "__DirectiveLocation"
1364 | }
1365 | }
1366 | }
1367 | }
1368 | },
1369 | {
1370 | "name": "args",
1371 | "args": [],
1372 | "type": {
1373 | "kind": "NON_NULL",
1374 | "name": null,
1375 | "ofType": {
1376 | "kind": "LIST",
1377 | "name": null,
1378 | "ofType": {
1379 | "kind": "NON_NULL",
1380 | "name": null,
1381 | "ofType": {
1382 | "kind": "OBJECT",
1383 | "name": "__InputValue"
1384 | }
1385 | }
1386 | }
1387 | }
1388 | }
1389 | ],
1390 | "inputFields": null,
1391 | "interfaces": [],
1392 | "enumValues": null,
1393 | "possibleTypes": null
1394 | },
1395 | {
1396 | "kind": "ENUM",
1397 | "name": "__DirectiveLocation",
1398 | "fields": null,
1399 | "inputFields": null,
1400 | "interfaces": null,
1401 | "enumValues": [
1402 | {
1403 | "name": "QUERY"
1404 | },
1405 | {
1406 | "name": "MUTATION"
1407 | },
1408 | {
1409 | "name": "SUBSCRIPTION"
1410 | },
1411 | {
1412 | "name": "FIELD"
1413 | },
1414 | {
1415 | "name": "FRAGMENT_DEFINITION"
1416 | },
1417 | {
1418 | "name": "FRAGMENT_SPREAD"
1419 | },
1420 | {
1421 | "name": "INLINE_FRAGMENT"
1422 | },
1423 | {
1424 | "name": "SCHEMA"
1425 | },
1426 | {
1427 | "name": "SCALAR"
1428 | },
1429 | {
1430 | "name": "OBJECT"
1431 | },
1432 | {
1433 | "name": "FIELD_DEFINITION"
1434 | },
1435 | {
1436 | "name": "ARGUMENT_DEFINITION"
1437 | },
1438 | {
1439 | "name": "INTERFACE"
1440 | },
1441 | {
1442 | "name": "UNION"
1443 | },
1444 | {
1445 | "name": "ENUM"
1446 | },
1447 | {
1448 | "name": "ENUM_VALUE"
1449 | },
1450 | {
1451 | "name": "INPUT_OBJECT"
1452 | },
1453 | {
1454 | "name": "INPUT_FIELD_DEFINITION"
1455 | }
1456 | ],
1457 | "possibleTypes": null
1458 | }
1459 | ],
1460 | "directives": [
1461 | {
1462 | "name": "include",
1463 | "args": [
1464 | {
1465 | "name": "if",
1466 | "type": {
1467 | "kind": "NON_NULL",
1468 | "name": null,
1469 | "ofType": {
1470 | "kind": "SCALAR",
1471 | "name": "Boolean",
1472 | "ofType": null
1473 | }
1474 | },
1475 | "defaultValue": null
1476 | }
1477 | ],
1478 | "onOperation": false,
1479 | "onFragment": true,
1480 | "onField": true
1481 | },
1482 | {
1483 | "name": "skip",
1484 | "args": [
1485 | {
1486 | "name": "if",
1487 | "type": {
1488 | "kind": "NON_NULL",
1489 | "name": null,
1490 | "ofType": {
1491 | "kind": "SCALAR",
1492 | "name": "Boolean",
1493 | "ofType": null
1494 | }
1495 | },
1496 | "defaultValue": null
1497 | }
1498 | ],
1499 | "onOperation": false,
1500 | "onFragment": true,
1501 | "onField": true
1502 | },
1503 | {
1504 | "name": "deprecated",
1505 | "args": [
1506 | {
1507 | "name": "reason",
1508 | "type": {
1509 | "kind": "SCALAR",
1510 | "name": "String",
1511 | "ofType": null
1512 | },
1513 | "defaultValue": "\"No longer supported\""
1514 | }
1515 | ],
1516 | "onOperation": false,
1517 | "onFragment": false,
1518 | "onField": false
1519 | }
1520 | ]
1521 | }
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import {render} from 'react-dom';
3 | import App from './App';
4 | import CashayBook from './CashayBook';
5 | import {createStore, compose, combineReducers} from 'redux'
6 | import {Provider} from 'react-redux';
7 | import {cashay, cashayReducer, HTTPTransport} from 'cashay';
8 | import gqlSchema from './schema.js';
9 | const clientSchema = require('cashay!./getCashaySchema.js');
10 | import {graphql} from 'graphql';
11 |
12 | const rootReducer = combineReducers({
13 | cashay: cashayReducer
14 | });
15 |
16 | const transport = new HTTPTransport('/graphql');
17 |
18 | const devtoolsExt = global.devToolsExtension && global.devToolsExtension();
19 | const store = createStore(rootReducer, {}, compose(
20 | devtoolsExt || (f => f)
21 | ));
22 |
23 | cashay.create({
24 | store,
25 | schema: clientSchema,
26 | idFieldName: '_id',
27 | paginationWords: {before: 'beforeCursor', after: 'afterCursor'},
28 | transport
29 | });
30 |
31 | render(
32 |
33 |
34 |
35 | , document.getElementById('root')
36 | );
37 |
38 | // use this for graphiql
39 | // render( , document.getElementById('root'));
40 |
--------------------------------------------------------------------------------
/src/recentPosts.js:
--------------------------------------------------------------------------------
1 | import React, {Component} from 'react';
2 | import {connect} from 'react-redux';
3 | import {cashay} from 'cashay';
4 | import SinglePost from './SinglePost';
5 | import uuid from 'node-uuid';
6 |
7 | const queryRecentPosts = `
8 | query($count: Int) {
9 | recentPosts: getRecentPosts(first: $count) {
10 | _id,
11 | title,
12 | cursor,
13 | spanishTitle: title(language:"spanish"),
14 | reverseSpanishTitle: title(language:"spanish", inReverse:true)
15 | }
16 | }`;
17 |
18 | const customMutations = {
19 | removePostById: `
20 | mutation($postId: String!) {
21 | removePostById(postId: $postId) {
22 | removedPostId
23 | }
24 | }`
25 | };
26 |
27 | const mutationHandlers = {
28 | createPost(optimisticVariables, docFromServer, currentResponse, getEntities, invalidate) {
29 | if (optimisticVariables) {
30 | const {newPost} = optimisticVariables;
31 | const newOptimisticPost = Object.assign({}, newPost, {
32 | title: `${newPost.title} OPTIMISTICAL!`
33 | });
34 | currentResponse.recentPosts.unshift(newOptimisticPost);
35 | return currentResponse;
36 | }
37 | // const foo = getEntities('PostType');
38 | const optimisticDocIdx = currentResponse.recentPosts.findIndex(post => post._id === docFromServer.createPost.post._id);
39 | currentResponse.recentPosts[optimisticDocIdx] = docFromServer.createPost.post;
40 | return currentResponse;
41 | },
42 | removePostById(optimisticVariables, docFromServer, currentResponse) {
43 | // example of not using optimistic updates
44 | const idRemoved = docFromServer && docFromServer.removePostById.removedPostId;
45 | if (idRemoved) {
46 | const idx = currentResponse.recentPosts.findIndex(post => post._id === idRemoved);
47 | currentResponse.recentPosts.splice(idx, 1);
48 | return currentResponse;
49 | }
50 | }
51 | };
52 |
53 | const mapStateToProps = (state, props) => {
54 | return {
55 | cashay: cashay.query(queryRecentPosts, {variables: {count: 2}, mutationHandlers, customMutations, component: 'RecentPosts'})
56 | }
57 | };
58 |
59 | @connect(mapStateToProps)
60 | export default class RecentPosts extends Component {
61 | render() {
62 | const {recentPosts} = this.props.cashay.data;
63 | return (
64 |
65 |
RECENT POSTS
66 |
Create your very own post!
67 |
{recentPosts.map(post => )}
68 | {recentPosts.EOF ? null :
69 |
Get 2 More
70 | }
71 |
72 | );
73 | }
74 |
75 | createPost = () => {
76 | const variables = {
77 | newPost: {
78 | _id: uuid.v4(),
79 | title: 'Woo another post!'
80 | }
81 | };
82 | cashay.mutate('createPost', {variables});
83 | };
84 |
85 | get2More = () => {
86 | const {setVariables} = this.props.cashay;
87 | setVariables(currentVariables => {
88 | return {
89 | count: currentVariables.count + 2
90 | }
91 | })
92 | }
93 | }
94 |
--------------------------------------------------------------------------------
/src/schema.js:
--------------------------------------------------------------------------------
1 | import PostDB from './data/posts';
2 | import AuthorDB from './data/authors';
3 | import GroupDB from './data/groups';
4 | import CommentDB from './data/comments';
5 |
6 | import {
7 | GraphQLList,
8 | GraphQLObjectType,
9 | GraphQLSchema,
10 | GraphQLString,
11 | GraphQLInt,
12 | GraphQLEnumType,
13 | GraphQLNonNull,
14 | GraphQLInterfaceType,
15 | GraphQLUnionType,
16 | GraphQLInputObjectType,
17 | GraphQLBoolean,
18 | GraphQLFloat
19 | } from 'graphql';
20 |
21 | const handlePaginationArgs = ({beforeCursor, afterCursor, first, last}, objs) => {
22 | let arrayPartial;
23 | if (first) {
24 | const docsToSend = first + 1;
25 | const startingIdx = objs.findIndex(obj => obj.cursor === afterCursor) + 1;
26 | arrayPartial = objs.slice(startingIdx, startingIdx + docsToSend);
27 | } else if (last) {
28 | const docsToSend = last + 1;
29 | let endingIdx = objs.findIndex(obj => obj.cursor === beforeCursor);
30 | endingIdx = endingIdx === -1 ? objs.length : endingIdx;
31 | const start = Math.max(0, endingIdx - docsToSend);
32 | arrayPartial = objs.slice(start, endingIdx);
33 | } else {
34 | arrayPartial = objs;
35 | }
36 | return arrayPartial;
37 | };
38 |
39 | const CategoryType = new GraphQLEnumType({
40 | name: "CategoryType",
41 | description: "A CategoryType of the blog",
42 | values: {
43 | HOT_STUFF: {value: "hot stuff"},
44 | ICE_COLD: {value: "ice cold"}
45 | }
46 | });
47 |
48 | const AuthorType = new GraphQLObjectType({
49 | name: "AuthorType",
50 | description: "Represent the type of an author of a blog post or a comment",
51 | fields: () => ({
52 | _id: {type: GraphQLString},
53 | name: {type: GraphQLString},
54 | twitterHandle: {type: GraphQLString}
55 | })
56 | });
57 |
58 | const HasAuthorType = new GraphQLInterfaceType({
59 | name: "HasAuthorType",
60 | description: "This type has an author",
61 | fields: () => ({
62 | author: {type: AuthorType}
63 | }),
64 | resolveType: (obj) => {
65 | if (obj.title) {
66 | return PostType;
67 | } else if (obj.replies) {
68 | return CommentType;
69 | }
70 | }
71 | });
72 |
73 | const CommentType = new GraphQLObjectType({
74 | name: "CommentType",
75 | interfaces: [HasAuthorType],
76 | description: "Represent the type of a comment",
77 | fields: () => ({
78 | _id: {type: GraphQLString},
79 | content: {type: GraphQLString},
80 | author: {
81 | type: AuthorType,
82 | resolve: function({author}) {
83 | return AuthorDB.find(doc => doc.author === author);
84 | }
85 | },
86 | createdAt: {type: GraphQLFloat},
87 | cursor: {type: GraphQLString},
88 | karma: {type: GraphQLInt},
89 | postId: {type: GraphQLString}
90 | })
91 | });
92 |
93 | const GroupType = new GraphQLObjectType({
94 | name: "Group",
95 | description: "A group with an owner and members",
96 | args: {
97 | groupId: {type: GraphQLString}
98 | },
99 | fields: () => ({
100 | _id: {type: GraphQLString},
101 | owner: {
102 | type: MemberType,
103 | resolve(source) {
104 | let author;
105 | author = AuthorDB.find(doc => doc._id === source.ownerId);
106 | if (!author) {
107 | author = GroupDB.find(doc => doc._id === source.ownerId);
108 | }
109 | return author;
110 | }
111 | },
112 | members: {
113 | type: new GraphQLList(MemberType),
114 | resolve(source) {
115 | return source.members.map(member => {
116 | let author;
117 | author = AuthorDB.find(doc => doc._id === member);
118 | if (!author) {
119 | author = GroupDB.find(doc => doc._id === member);
120 | }
121 | return author;
122 | });
123 | }
124 | }
125 | })
126 | });
127 |
128 | const MemberType = new GraphQLUnionType({
129 | name: "Member",
130 | resolveType(obj) {
131 | if (obj.hasOwnProperty('ownerId')) {
132 | return GroupType
133 | } else {
134 | return AuthorType;
135 | }
136 | },
137 | types: [GroupType, AuthorType]
138 | });
139 |
140 |
141 | const PostType = new GraphQLObjectType({
142 | name: "PostType",
143 | interfaces: [HasAuthorType],
144 | description: "Represent the type of a blog post",
145 | fields: () => ({
146 | _id: {type: GraphQLString},
147 | title: {
148 | type: GraphQLString,
149 | args: {
150 | language: {type: GraphQLString, description: "Language of the title"},
151 | inReverse: {type: GraphQLBoolean, description: 'give the title in reverse'}
152 | },
153 | resolve(source, args) {
154 | if (args.language === 'spanish') {
155 | if (args.inReverse) {
156 | return source.title_ES.split('').reverse().join('');
157 | }
158 | return source.title_ES;
159 | }
160 | if (args.inReverse) {
161 | return source.title.split('').reverse().join('');
162 | }
163 | return source.title;
164 | }
165 | },
166 | category: {type: CategoryType},
167 | content: {type: GraphQLString},
168 | createdAt: {
169 | type: GraphQLInt,
170 | args: {
171 | dateOptions: {type: DateOptionsType, description: "example of a subfield with an input obj"}
172 | },
173 | resolve(source) {
174 | return source.createdAt
175 | }
176 | },
177 | comments: {
178 | type: new GraphQLList(CommentType),
179 | args: {
180 | beforeCursor: {type: GraphQLString, description: 'the cursor coming from the back'},
181 | afterCursor: {type: GraphQLString, description: 'the cursor coming from the front'},
182 | first: {type: GraphQLInt, description: "Limit the comments from the front"},
183 | last: {type: GraphQLInt, description: "Limit the comments from the back"}
184 | },
185 | resolve: function(post, args) {
186 | return handlePaginationArgs(args, CommentDB)
187 | }
188 | },
189 | author: {
190 | type: AuthorType,
191 | resolve: function({author}) {
192 | return AuthorDB.find(doc => doc._id === author);
193 | }
194 | },
195 | cursor: {type: GraphQLString}
196 | })
197 | });
198 |
199 | const CreatePostMutationPayload = new GraphQLObjectType({
200 | name: "CreatePostMutationPayload",
201 | description: "Payload for creating a post",
202 | fields: () => ({
203 | post: {type: PostType},
204 | postCount: {type: GraphQLInt}
205 | })
206 | });
207 |
208 | const RemovePostMutationPayload = new GraphQLObjectType({
209 | name: "RemovePostMutationPayload",
210 | description: "Payload for removing a post",
211 | fields: () => ({
212 | removedPostId: {type: GraphQLString},
213 | postCount: {type: GraphQLInt}
214 | })
215 | });
216 |
217 | const NewPost = new GraphQLInputObjectType({
218 | name: "NewPost",
219 | description: "input object for a new post",
220 | fields: () => ({
221 | _id: {type: new GraphQLNonNull(GraphQLString)},
222 | content: {type: GraphQLString},
223 | title: {type: GraphQLString},
224 | category: {type: GraphQLString}
225 | })
226 | });
227 |
228 | const DateOptionsType = new GraphQLInputObjectType({
229 | name: "DateOptions",
230 | description: "formatting options for the date",
231 | fields: () => ({
232 | day: {type: GraphQLBoolean},
233 | month: {type: GraphQLBoolean},
234 | year: {type: GraphQLBoolean}
235 | })
236 | });
237 |
238 | const NewMember = new GraphQLInputObjectType({
239 | name: "NewMember",
240 | description: "input object for a new member",
241 | fields: () => ({
242 | _id: {type: new GraphQLNonNull(GraphQLString)},
243 | name: {type: GraphQLString},
244 | ownerId: {type: GraphQLString},
245 | members: {type: new GraphQLList(GraphQLString)},
246 | twitterHandle: {type: GraphQLString}
247 | })
248 | });
249 |
250 | const Query = new GraphQLObjectType({
251 | name: 'BlogSchema',
252 | description: "Root of the Blog Schema",
253 | fields: () => ({
254 | getPostCount: {
255 | type: new GraphQLNonNull(GraphQLInt),
256 | description: "the number of posts currently in the db",
257 | resolve() {
258 | return PostDB.length;
259 | }
260 | },
261 | getLatestPostId: {
262 | type: GraphQLString,
263 | description: "Latest post in the blog",
264 | resolve() {
265 | const sortedPosts = PostDB.sort((a, b) => b.createdAt - a.createdAt);
266 | return sortedPosts[0]._id;
267 | }
268 | },
269 | getRecentPosts: {
270 | type: new GraphQLList(PostType),
271 | description: "Recent posts in the blog",
272 | args: {
273 | beforeCursor: {type: GraphQLString, description: 'the cursor coming from the back'},
274 | afterCursor: {type: GraphQLString, description: 'the cursor coming from the front'},
275 | first: {type: GraphQLInt, description: "Limit the comments from the front"},
276 | last: {type: GraphQLInt, description: "Limit the comments from the back"}
277 | },
278 | resolve(source, args, ref) {
279 | const sortedPosts = PostDB.sort((a, b) => b.createdAt - a.createdAt);
280 |
281 | return handlePaginationArgs(args, sortedPosts);
282 | }
283 | },
284 | getPostById: {
285 | type: PostType,
286 | description: "PostType by _id",
287 | args: {
288 | _id: {type: new GraphQLNonNull(GraphQLString)}
289 | },
290 | resolve: function(source, {_id}) {
291 | return PostDB.find(doc => doc._id === _id);
292 | }
293 | },
294 | getGroup: {
295 | type: GroupType,
296 | args: {
297 | _id: {type: new GraphQLNonNull(GraphQLString)}
298 | },
299 | resolve(source, {_id}) {
300 | return GroupDB.find(doc => doc._id === _id);
301 | }
302 | },
303 | getCommentsByPostId: {
304 | type: new GraphQLList(CommentType),
305 | description: "Comments for a specific post",
306 | args: {
307 | postId: {type: new GraphQLNonNull(GraphQLString)}
308 | },
309 | resolve: function(source, {postId}) {
310 | return CommentDB.filter(doc => doc.postId === postId);
311 | }
312 | },
313 | })
314 | });
315 |
316 | const Mutation = new GraphQLObjectType({
317 | name: "BlogMutations",
318 | fields: () => ({
319 | createPost: {
320 | type: CreatePostMutationPayload,
321 | description: "Create a post",
322 | args: {
323 | newPost: {type: new GraphQLNonNull(NewPost)},
324 | // this is wrong to break out the author, but useful for testing different arg types
325 | author: {type: GraphQLString}
326 | },
327 | resolve(source, {newPost, author}) {
328 | const now = Date.now();
329 | const post = Object.assign({}, newPost, {
330 | karma: 0,
331 | createdAt: now,
332 | title_ES: `${newPost.title} EN ESPANOL!`,
333 | cursor: `${now}chikachikow`,
334 | author
335 | });
336 | PostDB.push(post);
337 | return {
338 | post,
339 | postCount: PostDB.length
340 | }
341 | }
342 | },
343 | removePostById: {
344 | type: RemovePostMutationPayload,
345 | description: 'Remove a post',
346 | args: {
347 | postId: {type: new GraphQLNonNull(GraphQLString)}
348 | },
349 | resolve(source, {postId}) {
350 | const removedPostIdx= PostDB.findIndex(doc => doc.postId === postId);
351 | let didRemove = false;
352 | if (removedPostIdx !== -1) {
353 | PostDB.splice(removedPostIdx,1);
354 | didRemove = true;
355 | }
356 | return {
357 | removedPostId: didRemove ? postId : null,
358 | postCount: Object.keys(PostDB).length
359 | };
360 | }
361 | },
362 | updatePost: {
363 | type: PostType,
364 | description: 'update a post',
365 | args: {
366 | post: {type: NewPost}
367 | },
368 | resolve(source, {post}) {
369 | const storedPost = PostDB.find(doc => doc._id === post._id);
370 | if (storedPost) {
371 | const updatedKeys = Object.keys(post);
372 | updatedKeys.forEach(key => {
373 | const value = post[key];
374 | if (value === null) {
375 | delete storedPost[key];
376 | } else {
377 | storedPost[key] = value
378 | }
379 | })
380 | }
381 | return storedPost;
382 | }
383 | },
384 | createComment: {
385 | type: CommentType,
386 | description: "Comment on a post",
387 | args: {
388 | _id: {type: new GraphQLNonNull(GraphQLString)},
389 | postId: {type: new GraphQLNonNull(GraphQLString)},
390 | content: {type: new GraphQLNonNull(GraphQLString)}
391 | },
392 | resolve(source, {content, postId, _id}) {
393 | const newPost = {
394 | _id,
395 | content,
396 | postId,
397 | karma: 0,
398 | author: 'a125',
399 | createdAt: Date.now()
400 | };
401 | CommentDB.push(newPost);
402 | return newPost;
403 | }
404 | },
405 | createMembers: {
406 | type: new GraphQLList(MemberType),
407 | description: "Create multiple members",
408 | args: {
409 | members: {type: new GraphQLNonNull(new GraphQLList(new GraphQLNonNull(NewMember)))}
410 | },
411 | resolve(source, {members}) {
412 | return members;
413 | }
414 | }
415 | })
416 | });
417 |
418 | export default new GraphQLSchema({
419 | query: Query,
420 | mutation: Mutation
421 | });
422 |
--------------------------------------------------------------------------------
/static/graphiql.css:
--------------------------------------------------------------------------------
1 | .postContainer {
2 | display: flex;
3 | flex-direction: row;
4 | }
5 |
6 | .recentPosts {
7 | margin: 16px;
8 | }
9 |
10 | .latestAndById {
11 | margin: 16px;
12 | }
13 |
14 | .showComments {
15 | font-size: .6em;
16 | text-align: right;
17 | color: blue;
18 | cursor: pointer;
19 | }
20 |
21 | .deleteMe {
22 | font-size: .6em;
23 | text-align: right;
24 | color: darkred;
25 | cursor: pointer;
26 | }
27 |
28 | .commentText {
29 | font-size: .8em;
30 | padding: 16px;
31 | }
32 |
33 | .newComment {
34 | font-size: .8em;
35 | padding-left: 16px;
36 | }
37 |
38 | html, body {
39 | height: 100%;
40 | margin: 0;
41 | overflow: hidden;
42 | width: 100%;
43 | font-family: system, -apple-system, 'San Francisco', '.SFNSDisplay-Regular', 'Segoe UI', Segoe, 'Segoe WP', 'Helvetica Neue', helvetica, 'Lucida Grande', arial, sans-serif;
44 | }
45 |
46 |
47 |
48 | #graphiql-container {
49 | color: #141823;
50 | width: 100%;
51 | display: -webkit-flex;
52 | display: flex;
53 | -webkit-flex-direction: row;
54 | flex-direction: row;
55 | height: 100%;
56 | font-size: 14px;
57 | }
58 |
59 | #graphiql-container .editorWrap {
60 | display: -webkit-flex;
61 | display: flex;
62 | -webkit-flex-direction: column;
63 | flex-direction: column;
64 | -webkit-flex: 1;
65 | flex: 1;
66 | }
67 |
68 | #graphiql-container .title {
69 | font-size: 18px;
70 | }
71 |
72 | #graphiql-container .title em {
73 | font-family: georgia;
74 | font-size: 19px;
75 | }
76 |
77 | #graphiql-container .topBarWrap {
78 | display: -webkit-flex;
79 | display: flex;
80 | -webkit-flex-direction: row;
81 | flex-direction: row;
82 | }
83 |
84 | #graphiql-container .topBar {
85 | background: -webkit-linear-gradient(#f7f7f7, #e2e2e2);
86 | background: linear-gradient(#f7f7f7, #e2e2e2);
87 | border-bottom: solid 1px #d0d0d0;
88 | cursor: default;
89 | height: 34px;
90 | padding: 7px 14px 6px;
91 | -webkit-user-select: none;
92 | user-select: none;
93 | display: -webkit-flex;
94 | display: flex;
95 | -webkit-flex-direction: row;
96 | flex-direction: row;
97 | -webkit-flex: 1;
98 | flex: 1;
99 | -webkit-align-items: center;
100 | align-items: center;
101 | }
102 |
103 | #graphiql-container .docExplorerShow {
104 | background: -webkit-linear-gradient(#f7f7f7, #e2e2e2);
105 | background: linear-gradient(#f7f7f7, #e2e2e2);
106 | border: none;
107 | border-bottom: solid 1px #d0d0d0;
108 | border-left: solid 1px rgba(0, 0, 0, 0.2);
109 | color: #3B5998;
110 | cursor: pointer;
111 | font-size: 14px;
112 | outline: 0;
113 | padding: 2px 20px 0 18px;
114 | }
115 |
116 | #graphiql-container .docExplorerShow:before {
117 | border-left: 2px solid #3B5998;
118 | border-top: 2px solid #3B5998;
119 | content: '';
120 | display: inline-block;
121 | height: 9px;
122 | margin: 0 3px -1px 0;
123 | position: relative;
124 | width: 9px;
125 | -webkit-transform: rotate(-45deg);
126 | transform: rotate(-45deg);
127 | }
128 |
129 | #graphiql-container .editorBar {
130 | display: -webkit-flex;
131 | display: flex;
132 | -webkit-flex-direction: row;
133 | flex-direction: row;
134 | -webkit-flex: 1;
135 | flex: 1;
136 | }
137 |
138 | #graphiql-container .queryWrap {
139 | display: -webkit-flex;
140 | display: flex;
141 | -webkit-flex-direction: column;
142 | flex-direction: column;
143 | -webkit-flex: 1;
144 | flex: 1;
145 | }
146 |
147 | #graphiql-container .resultWrap {
148 | display: -webkit-flex;
149 | display: flex;
150 | -webkit-flex-direction: column;
151 | flex-direction: column;
152 | -webkit-flex: 1;
153 | flex: 1;
154 | border-left: solid 1px #e0e0e0;
155 | }
156 |
157 | #graphiql-container .docExplorerWrap {
158 | box-shadow: 0 0 8px rgba(0, 0, 0, 0.15);
159 | z-index: 3;
160 | position: relative;
161 | background: white;
162 | }
163 |
164 | #graphiql-container .docExplorerResizer {
165 | cursor: col-resize;
166 | height: 100%;
167 | left: -5px;
168 | position: absolute;
169 | top: 0;
170 | width: 10px;
171 | z-index: 10;
172 | }
173 |
174 | #graphiql-container .docExplorerHide {
175 | cursor: pointer;
176 | font-size: 18px;
177 | margin: -7px -8px -6px 0;
178 | padding: 18px 16px 15px 12px;
179 | }
180 |
181 | #graphiql-container .query-editor {
182 | -webkit-flex: 1;
183 | flex: 1;
184 | position: relative;
185 | }
186 |
187 | #graphiql-container .variable-editor {
188 | height: 30px;
189 | display: -webkit-flex;
190 | display: flex;
191 | -webkit-flex-direction: column;
192 | flex-direction: column;
193 | position: relative;
194 | }
195 |
196 | #graphiql-container .variable-editor-title {
197 | background: #eeeeee;
198 | border-bottom: solid 1px #d6d6d6;
199 | border-top: solid 1px #e0e0e0;
200 | color: #777;
201 | font-variant: small-caps;
202 | font-weight: bold;
203 | letter-spacing: 1px;
204 | line-height: 14px;
205 | padding: 6px 0 8px 43px;
206 | text-transform: lowercase;
207 | -webkit-user-select: none;
208 | user-select: none;
209 | }
210 |
211 | #graphiql-container .codemirrorWrap {
212 | -webkit-flex: 1;
213 | flex: 1;
214 | position: relative;
215 | }
216 |
217 | #graphiql-container .result-window {
218 | -webkit-flex: 1;
219 | flex: 1;
220 | position: relative;
221 | }
222 |
223 | #graphiql-container .footer {
224 | background: #f6f7f8;
225 | border-left: solid 1px #e0e0e0;
226 | border-top: solid 1px #e0e0e0;
227 | margin-left: 12px;
228 | position: relative;
229 | }
230 |
231 | #graphiql-container .footer:before {
232 | background: #eeeeee;
233 | bottom: 0;
234 | content: " ";
235 | left: -13px;
236 | position: absolute;
237 | top: -1px;
238 | width: 12px;
239 | }
240 |
241 | #graphiql-container .result-window .CodeMirror {
242 | background: #f6f7f8;
243 | }
244 |
245 | #graphiql-container .result-window .CodeMirror-gutters {
246 | background-color: #eeeeee;
247 | border-color: #e0e0e0;
248 | cursor: col-resize;
249 | }
250 |
251 | #graphiql-container .result-window .CodeMirror-foldgutter,
252 | #graphiql-container .result-window .CodeMirror-foldgutter-open:after,
253 | #graphiql-container .result-window .CodeMirror-foldgutter-folded:after {
254 | padding-left: 3px;
255 | }
256 |
257 | #graphiql-container .execute-button {
258 | background: -webkit-linear-gradient(#fdfdfd, #d2d3d6);
259 | background: linear-gradient(#fdfdfd, #d2d3d6);
260 | border: solid 1px rgba(0,0,0,0.25);
261 | border-radius: 17px;
262 | box-shadow: 0 1px 0 #fff;
263 | cursor: pointer;
264 | fill: #444;
265 | height: 34px;
266 | margin: 0 14px 0 28px;
267 | padding: 0;
268 | width: 34px;
269 | }
270 |
271 | #graphiql-container .execute-button:active {
272 | background: -webkit-linear-gradient(#e6e6e6, #c0c0c0);
273 | background: linear-gradient(#e6e6e6, #c0c0c0);
274 | box-shadow:
275 | 0 1px 0 #fff,
276 | inset 0 0 2px rgba(0, 0, 0, 0.3),
277 | inset 0 0 6px rgba(0, 0, 0, 0.2);
278 | }
279 |
280 | #graphiql-container .execute-button:focus {
281 | outline: 0;
282 | }
283 |
284 | #graphiql-container .CodeMirror-scroll {
285 | -webkit-overflow-scrolling: touch;
286 | }
287 |
288 | #graphiql-container .CodeMirror {
289 | position: absolute;
290 | top: 0;
291 | left: 0;
292 | height: 100%;
293 | width: 100%;
294 | font-size: 13px;
295 | font-family: 'Consolas', 'Inconsolata', 'Droid Sans Mono', 'Monaco', monospace;
296 | color: #141823;
297 | }
298 |
299 | #graphiql-container .CodeMirror-lines {
300 | padding: 20px 0;
301 | }
302 |
303 | .CodeMirror-hint-information .content {
304 | -webkit-box-orient: vertical;
305 | color: #141823;
306 | display: -webkit-box;
307 | font-family: system, -apple-system, 'San Francisco', '.SFNSDisplay-Regular', 'Segoe UI', Segoe, 'Segoe WP', 'Helvetica Neue', helvetica, 'Lucida Grande', arial, sans-serif;
308 | font-size: 13px;
309 | -webkit-line-clamp: 3;
310 | line-height: 16px;
311 | max-height: 48px;
312 | overflow: hidden;
313 | text-overflow: -o-ellipsis-lastline;
314 | }
315 |
316 | .CodeMirror-hint-information .content p:first-child {
317 | margin-top: 0;
318 | }
319 |
320 | .CodeMirror-hint-information .content p:last-child {
321 | margin-bottom: 0;
322 | }
323 |
324 | .CodeMirror-hint-information .infoType {
325 | color: #30a;
326 | margin-right: 0.5em;
327 | display: inline;
328 | cursor: pointer;
329 | }
330 |
331 | .autoInsertedLeaf.cm-property {
332 | padding: 2px 4px 1px;
333 | margin: -2px -4px -1px;
334 | border-radius: 2px;
335 | border-bottom: solid 2px rgba(255, 255, 255, 0);
336 | -webkit-animation-duration: 6s;
337 | -moz-animation-duration: 6s;
338 | animation-duration: 6s;
339 | -webkit-animation-name: insertionFade;
340 | -moz-animation-name: insertionFade;
341 | animation-name: insertionFade;
342 | }
343 |
344 | @-moz-keyframes insertionFade {
345 | from, to {
346 | background: rgba(255, 255, 255, 0);
347 | border-color: rgba(255, 255, 255, 0);
348 | }
349 |
350 | 15%, 85% {
351 | background: #fbffc9;
352 | border-color: #f0f3c0;
353 | }
354 | }
355 |
356 | @-webkit-keyframes insertionFade {
357 | from, to {
358 | background: rgba(255, 255, 255, 0);
359 | border-color: rgba(255, 255, 255, 0);
360 | }
361 |
362 | 15%, 85% {
363 | background: #fbffc9;
364 | border-color: #f0f3c0;
365 | }
366 | }
367 |
368 | @keyframes insertionFade {
369 | from, to {
370 | background: rgba(255, 255, 255, 0);
371 | border-color: rgba(255, 255, 255, 0);
372 | }
373 |
374 | 15%, 85% {
375 | background: #fbffc9;
376 | border-color: #f0f3c0;
377 | }
378 | }
379 |
380 | div.CodeMirror-lint-tooltip {
381 | background-color: white;
382 | color: #141823;
383 | border: 0;
384 | border-radius: 2px;
385 | -webkit-box-shadow: 0 1px 3px rgba(0, 0, 0, 0.45);
386 | -moz-box-shadow: 0 1px 3px rgba(0, 0, 0, 0.45);
387 | box-shadow: 0 1px 3px rgba(0, 0, 0, 0.45);
388 | font-family: system, -apple-system, 'San Francisco', '.SFNSDisplay-Regular', 'Segoe UI', Segoe, 'Segoe WP', 'Helvetica Neue', helvetica, 'Lucida Grande', arial, sans-serif;
389 | font-size: 13px;
390 | line-height: 16px;
391 | padding: 6px 10px;
392 | opacity: 0;
393 | transition: opacity 0.15s;
394 | -moz-transition: opacity 0.15s;
395 | -webkit-transition: opacity 0.15s;
396 | -o-transition: opacity 0.15s;
397 | -ms-transition: opacity 0.15s;
398 | }
399 |
400 | div.CodeMirror-lint-message-error, div.CodeMirror-lint-message-warning {
401 | padding-left: 23px;
402 | }
403 |
404 | /* COLORS */
405 |
406 | #graphiql-container .CodeMirror-foldmarker {
407 | border-radius: 4px;
408 | background: #08f;
409 | background: -webkit-linear-gradient(#43A8FF, #0F83E8);
410 | background: linear-gradient(#43A8FF, #0F83E8);
411 |
412 | color: white;
413 | -webkit-box-shadow: 0 1px 1px rgba(0, 0, 0, 0.2), inset 0 0 0 1px rgba(0, 0, 0, 0.1);
414 | -moz-box-shadow: 0 1px 1px rgba(0, 0, 0, 0.2), inset 0 0 0 1px rgba(0, 0, 0, 0.1);
415 | box-shadow: 0 1px 1px rgba(0, 0, 0, 0.2), inset 0 0 0 1px rgba(0, 0, 0, 0.1);
416 | font-family: arial;
417 | line-height: 0;
418 | padding: 0px 4px 1px;
419 | font-size: 12px;
420 | margin: 0 3px;
421 | text-shadow: 0 -1px rgba(0, 0, 0, 0.1);
422 | }
423 |
424 | #graphiql-container div.CodeMirror span.CodeMirror-matchingbracket {
425 | color: #555;
426 | text-decoration: underline;
427 | }
428 |
429 | #graphiql-container div.CodeMirror span.CodeMirror-nonmatchingbracket {
430 | color: #f00;
431 | }
432 |
433 | /* Comment */
434 | .cm-comment {
435 | color: #999;
436 | }
437 |
438 | /* Punctuation */
439 | .cm-punctuation {
440 | color: #555;
441 | }
442 |
443 | /* Keyword */
444 | .cm-keyword {
445 | color: #B11A04;
446 | }
447 |
448 | /* OperationName, FragmentName */
449 | .cm-def {
450 | color: #D2054E;
451 | }
452 |
453 | /* FieldName */
454 | .cm-property {
455 | color: #1F61A0;
456 | }
457 |
458 | /* FieldAlias */
459 | .cm-qualifier {
460 | color: #1C92A9;
461 | }
462 |
463 | /* ArgumentName and ObjectFieldName */
464 | .cm-attribute {
465 | color: #8B2BB9;
466 | }
467 |
468 | /* Number */
469 | .cm-number {
470 | color: #2882F9;
471 | }
472 |
473 | /* String */
474 | .cm-string {
475 | color: #D64292;
476 | }
477 |
478 | /* Boolean */
479 | .cm-builtin {
480 | color: #D47509;
481 | }
482 |
483 | /* EnumValue */
484 | .cm-string-2 {
485 | color: #0B7FC7;
486 | }
487 |
488 | /* Variable */
489 | .cm-variable {
490 | color: #397D13;
491 | }
492 |
493 | /* Directive */
494 | .cm-meta {
495 | color: #B33086;
496 | }
497 |
498 | /* Type */
499 | .cm-atom {
500 | color: #CA9800;
501 | }
502 | /* BASICS */
503 |
504 | .CodeMirror {
505 | /* Set height, width, borders, and global font properties here */
506 | font-family: monospace;
507 | height: 300px;
508 | color: black;
509 | }
510 |
511 | /* PADDING */
512 |
513 | .CodeMirror-lines {
514 | padding: 4px 0; /* Vertical padding around content */
515 | }
516 | .CodeMirror pre {
517 | padding: 0 4px; /* Horizontal padding of content */
518 | }
519 |
520 | .CodeMirror-scrollbar-filler, .CodeMirror-gutter-filler {
521 | background-color: white; /* The little square between H and V scrollbars */
522 | }
523 |
524 | /* GUTTER */
525 |
526 | .CodeMirror-gutters {
527 | border-right: 1px solid #ddd;
528 | background-color: #f7f7f7;
529 | white-space: nowrap;
530 | }
531 | .CodeMirror-linenumbers {}
532 | .CodeMirror-linenumber {
533 | padding: 0 3px 0 5px;
534 | min-width: 20px;
535 | text-align: right;
536 | color: #999;
537 | white-space: nowrap;
538 | }
539 |
540 | .CodeMirror-guttermarker { color: black; }
541 | .CodeMirror-guttermarker-subtle { color: #999; }
542 |
543 | /* CURSOR */
544 |
545 | .CodeMirror div.CodeMirror-cursor {
546 | border-left: 1px solid black;
547 | }
548 | /* Shown when moving in bi-directional text */
549 | .CodeMirror div.CodeMirror-secondarycursor {
550 | border-left: 1px solid silver;
551 | }
552 | .CodeMirror.cm-fat-cursor div.CodeMirror-cursor {
553 | width: auto;
554 | border: 0;
555 | background: #7e7;
556 | }
557 | .CodeMirror.cm-fat-cursor div.CodeMirror-cursors {
558 | z-index: 1;
559 | }
560 |
561 | .cm-animate-fat-cursor {
562 | width: auto;
563 | border: 0;
564 | -webkit-animation: blink 1.06s steps(1) infinite;
565 | -moz-animation: blink 1.06s steps(1) infinite;
566 | animation: blink 1.06s steps(1) infinite;
567 | }
568 | @-moz-keyframes blink {
569 | 0% { background: #7e7; }
570 | 50% { background: none; }
571 | 100% { background: #7e7; }
572 | }
573 | @-webkit-keyframes blink {
574 | 0% { background: #7e7; }
575 | 50% { background: none; }
576 | 100% { background: #7e7; }
577 | }
578 | @keyframes blink {
579 | 0% { background: #7e7; }
580 | 50% { background: none; }
581 | 100% { background: #7e7; }
582 | }
583 |
584 | /* Can style cursor different in overwrite (non-insert) mode */
585 | div.CodeMirror-overwrite div.CodeMirror-cursor {}
586 |
587 | .cm-tab { display: inline-block; text-decoration: inherit; }
588 |
589 | .CodeMirror-ruler {
590 | border-left: 1px solid #ccc;
591 | position: absolute;
592 | }
593 |
594 | /* DEFAULT THEME */
595 |
596 | .cm-s-default .cm-keyword {color: #708;}
597 | .cm-s-default .cm-atom {color: #219;}
598 | .cm-s-default .cm-number {color: #164;}
599 | .cm-s-default .cm-def {color: #00f;}
600 | .cm-s-default .cm-variable,
601 | .cm-s-default .cm-punctuation,
602 | .cm-s-default .cm-property,
603 | .cm-s-default .cm-operator {}
604 | .cm-s-default .cm-variable-2 {color: #05a;}
605 | .cm-s-default .cm-variable-3 {color: #085;}
606 | .cm-s-default .cm-comment {color: #a50;}
607 | .cm-s-default .cm-string {color: #a11;}
608 | .cm-s-default .cm-string-2 {color: #f50;}
609 | .cm-s-default .cm-meta {color: #555;}
610 | .cm-s-default .cm-qualifier {color: #555;}
611 | .cm-s-default .cm-builtin {color: #30a;}
612 | .cm-s-default .cm-bracket {color: #997;}
613 | .cm-s-default .cm-tag {color: #170;}
614 | .cm-s-default .cm-attribute {color: #00c;}
615 | .cm-s-default .cm-header {color: blue;}
616 | .cm-s-default .cm-quote {color: #090;}
617 | .cm-s-default .cm-hr {color: #999;}
618 | .cm-s-default .cm-link {color: #00c;}
619 |
620 | .cm-negative {color: #d44;}
621 | .cm-positive {color: #292;}
622 | .cm-header, .cm-strong {font-weight: bold;}
623 | .cm-em {font-style: italic;}
624 | .cm-link {text-decoration: underline;}
625 | .cm-strikethrough {text-decoration: line-through;}
626 |
627 | .cm-s-default .cm-error {color: #f00;}
628 | .cm-invalidchar {color: #f00;}
629 |
630 | .CodeMirror-composing { border-bottom: 2px solid; }
631 |
632 | /* Default styles for common addons */
633 |
634 | div.CodeMirror span.CodeMirror-matchingbracket {color: #0f0;}
635 | div.CodeMirror span.CodeMirror-nonmatchingbracket {color: #f22;}
636 | .CodeMirror-matchingtag { background: rgba(255, 150, 0, .3); }
637 | .CodeMirror-activeline-background {background: #e8f2ff;}
638 |
639 | /* STOP */
640 |
641 | /* The rest of this file contains styles related to the mechanics of
642 | the editor. You probably shouldn't touch them. */
643 |
644 | .CodeMirror {
645 | position: relative;
646 | overflow: hidden;
647 | background: white;
648 | }
649 |
650 | .CodeMirror-scroll {
651 | overflow: scroll !important; /* Things will break if this is overridden */
652 | /* 30px is the magic margin used to hide the element's real scrollbars */
653 | /* See overflow: hidden in .CodeMirror */
654 | margin-bottom: -30px; margin-right: -30px;
655 | padding-bottom: 30px;
656 | height: 100%;
657 | outline: none; /* Prevent dragging from highlighting the element */
658 | position: relative;
659 | }
660 | .CodeMirror-sizer {
661 | position: relative;
662 | border-right: 30px solid transparent;
663 | }
664 |
665 | /* The fake, visible scrollbars. Used to force redraw during scrolling
666 | before actuall scrolling happens, thus preventing shaking and
667 | flickering artifacts. */
668 | .CodeMirror-vscrollbar, .CodeMirror-hscrollbar, .CodeMirror-scrollbar-filler, .CodeMirror-gutter-filler {
669 | position: absolute;
670 | z-index: 6;
671 | display: none;
672 | }
673 | .CodeMirror-vscrollbar {
674 | right: 0; top: 0;
675 | overflow-x: hidden;
676 | overflow-y: scroll;
677 | }
678 | .CodeMirror-hscrollbar {
679 | bottom: 0; left: 0;
680 | overflow-y: hidden;
681 | overflow-x: scroll;
682 | }
683 | .CodeMirror-scrollbar-filler {
684 | right: 0; bottom: 0;
685 | }
686 | .CodeMirror-gutter-filler {
687 | left: 0; bottom: 0;
688 | }
689 |
690 | .CodeMirror-gutters {
691 | position: absolute; left: 0; top: 0;
692 | z-index: 3;
693 | }
694 | .CodeMirror-gutter {
695 | white-space: normal;
696 | height: 100%;
697 | display: inline-block;
698 | margin-bottom: -30px;
699 | /* Hack to make IE7 behave */
700 | *zoom:1;
701 | *display:inline;
702 | }
703 | .CodeMirror-gutter-wrapper {
704 | position: absolute;
705 | z-index: 4;
706 | height: 100%;
707 | }
708 | .CodeMirror-gutter-elt {
709 | position: absolute;
710 | cursor: default;
711 | z-index: 4;
712 | }
713 | .CodeMirror-gutter-wrapper {
714 | -webkit-user-select: none;
715 | -moz-user-select: none;
716 | user-select: none;
717 | }
718 |
719 | .CodeMirror-lines {
720 | cursor: text;
721 | min-height: 1px; /* prevents collapsing before first draw */
722 | }
723 | .CodeMirror pre {
724 | /* Reset some styles that the rest of the page might have set */
725 | -moz-border-radius: 0; -webkit-border-radius: 0; border-radius: 0;
726 | border-width: 0;
727 | background: transparent;
728 | font-family: inherit;
729 | font-size: inherit;
730 | margin: 0;
731 | white-space: pre;
732 | word-wrap: normal;
733 | line-height: inherit;
734 | color: inherit;
735 | z-index: 2;
736 | position: relative;
737 | overflow: visible;
738 | -webkit-tap-highlight-color: transparent;
739 | }
740 | .CodeMirror-wrap pre {
741 | word-wrap: break-word;
742 | white-space: pre-wrap;
743 | word-break: normal;
744 | }
745 |
746 | .CodeMirror-linebackground {
747 | position: absolute;
748 | left: 0; right: 0; top: 0; bottom: 0;
749 | z-index: 0;
750 | }
751 |
752 | .CodeMirror-linewidget {
753 | position: relative;
754 | z-index: 2;
755 | overflow: auto;
756 | }
757 |
758 | .CodeMirror-widget {}
759 |
760 | .CodeMirror-code {
761 | outline: none;
762 | }
763 |
764 | /* Force content-box sizing for the elements where we expect it */
765 | .CodeMirror-scroll,
766 | .CodeMirror-sizer,
767 | .CodeMirror-gutter,
768 | .CodeMirror-gutters,
769 | .CodeMirror-linenumber {
770 | -moz-box-sizing: content-box;
771 | box-sizing: content-box;
772 | }
773 |
774 | .CodeMirror-measure {
775 | position: absolute;
776 | width: 100%;
777 | height: 0;
778 | overflow: hidden;
779 | visibility: hidden;
780 | }
781 | .CodeMirror-measure pre { position: static; }
782 |
783 | .CodeMirror div.CodeMirror-cursor {
784 | position: absolute;
785 | border-right: none;
786 | width: 0;
787 | }
788 |
789 | div.CodeMirror-cursors {
790 | visibility: hidden;
791 | position: relative;
792 | z-index: 3;
793 | }
794 | .CodeMirror-focused div.CodeMirror-cursors {
795 | visibility: visible;
796 | }
797 |
798 | .CodeMirror-selected { background: #d9d9d9; }
799 | .CodeMirror-focused .CodeMirror-selected { background: #d7d4f0; }
800 | .CodeMirror-crosshair { cursor: crosshair; }
801 | .CodeMirror ::selection { background: #d7d4f0; }
802 | .CodeMirror ::-moz-selection { background: #d7d4f0; }
803 |
804 | .cm-searching {
805 | background: #ffa;
806 | background: rgba(255, 255, 0, .4);
807 | }
808 |
809 | /* IE7 hack to prevent it from returning funny offsetTops on the spans */
810 | .CodeMirror span { *vertical-align: text-bottom; }
811 |
812 | /* Used to force a border model for a node */
813 | .cm-force-border { padding-right: .1px; }
814 |
815 | @media print {
816 | /* Hide the cursor when printing */
817 | .CodeMirror div.CodeMirror-cursors {
818 | visibility: hidden;
819 | }
820 | }
821 |
822 | /* See issue #2901 */
823 | .cm-tab-wrap-hack:after { content: ''; }
824 |
825 | /* Help users use markselection to safely style text background */
826 | span.CodeMirror-selectedtext { background: none; }
827 | #graphiql-container .doc-explorer {
828 | background: white;
829 | }
830 |
831 | #graphiql-container .doc-explorer-title-bar {
832 | cursor: default;
833 | display: -webkit-flex;
834 | display: flex;
835 | height: 34px;
836 | line-height: 14px;
837 | padding: 8px 8px 5px;
838 | position: relative;
839 | -webkit-user-select: none;
840 | user-select: none;
841 | }
842 |
843 | #graphiql-container .doc-explorer-title {
844 | padding: 10px 0 10px 10px;
845 | font-weight: bold;
846 | text-align: center;
847 | text-overflow: ellipsis;
848 | white-space: nowrap;
849 | overflow-x: hidden;
850 | -webkit-flex: 1;
851 | flex: 1;
852 | }
853 |
854 | #graphiql-container .doc-explorer-back {
855 | color: #3B5998;
856 | cursor: pointer;
857 | margin: -7px 0 -6px -8px;
858 | overflow-x: hidden;
859 | padding: 17px 12px 16px 16px;
860 | text-overflow: ellipsis;
861 | white-space: nowrap;
862 | }
863 |
864 | #graphiql-container .doc-explorer-back:before {
865 | border-left: 2px solid #3B5998;
866 | border-top: 2px solid #3B5998;
867 | content: '';
868 | display: inline-block;
869 | height: 9px;
870 | margin: 0 3px -1px 0;
871 | position: relative;
872 | width: 9px;
873 | -webkit-transform: rotate(-45deg);
874 | transform: rotate(-45deg);
875 | }
876 |
877 | #graphiql-container .doc-explorer-rhs {
878 | position: relative;
879 | }
880 |
881 | #graphiql-container .doc-explorer-contents {
882 | background-color: #ffffff;
883 | border-top: 1px solid #d6d6d6;
884 | bottom: 0;
885 | left: 0;
886 | min-width: 300px;
887 | overflow-y: auto;
888 | padding: 20px 15px;
889 | position: absolute;
890 | right: 0;
891 | top: 47px;
892 | }
893 |
894 | #graphiql-container .doc-type-description p:first-child ,
895 | #graphiql-container .doc-type-description blockquote:first-child {
896 | margin-top: 0;
897 | }
898 |
899 | #graphiql-container .doc-explorer-contents a {
900 | cursor: pointer;
901 | text-decoration: none;
902 | }
903 |
904 | #graphiql-container .doc-explorer-contents a:hover {
905 | text-decoration: underline;
906 | }
907 |
908 | #graphiql-container .doc-value-description {
909 | padding: 4px 0 8px 12px;
910 | }
911 |
912 | #graphiql-container .doc-category {
913 | margin: 20px 0;
914 | }
915 |
916 | #graphiql-container .doc-category-title {
917 | border-bottom: 1px solid #e0e0e0;
918 | color: #777;
919 | cursor: default;
920 | font-size: 14px;
921 | font-variant: small-caps;
922 | font-weight: bold;
923 | letter-spacing: 1px;
924 | margin: 0 -15px 10px 0;
925 | padding: 10px 0;
926 | -webkit-user-select: none;
927 | user-select: none;
928 | }
929 |
930 | #graphiql-container .doc-category-item {
931 | margin: 12px 0;
932 | color: #555;
933 | }
934 |
935 | #graphiql-container .keyword {
936 | color: #B11A04;
937 | }
938 |
939 | #graphiql-container .type-name {
940 | color: #CA9800;
941 | }
942 |
943 | #graphiql-container .field-name {
944 | color: #1F61A0;
945 | }
946 |
947 | #graphiql-container .value-name {
948 | color: #0B7FC7;
949 | }
950 |
951 | #graphiql-container .arg-name {
952 | color: #8B2BB9;
953 | }
954 |
955 | #graphiql-container .arg:after {
956 | content: ', ';
957 | }
958 |
959 | #graphiql-container .arg:last-child:after {
960 | content: '';
961 | }
962 | .CodeMirror-foldmarker {
963 | color: blue;
964 | text-shadow: #b9f 1px 1px 2px, #b9f -1px -1px 2px, #b9f 1px -1px 2px, #b9f -1px 1px 2px;
965 | font-family: arial;
966 | line-height: .3;
967 | cursor: pointer;
968 | }
969 | .CodeMirror-foldgutter {
970 | width: .7em;
971 | }
972 | .CodeMirror-foldgutter-open,
973 | .CodeMirror-foldgutter-folded {
974 | cursor: pointer;
975 | }
976 | .CodeMirror-foldgutter-open:after {
977 | content: "\25BE";
978 | }
979 | .CodeMirror-foldgutter-folded:after {
980 | content: "\25B8";
981 | }
982 | /* The lint marker gutter */
983 | .CodeMirror-lint-markers {
984 | width: 16px;
985 | }
986 |
987 | .CodeMirror-lint-tooltip {
988 | background-color: infobackground;
989 | border: 1px solid black;
990 | border-radius: 4px 4px 4px 4px;
991 | color: infotext;
992 | font-family: monospace;
993 | font-size: 10pt;
994 | overflow: hidden;
995 | padding: 2px 5px;
996 | position: fixed;
997 | white-space: pre;
998 | white-space: pre-wrap;
999 | z-index: 100;
1000 | max-width: 600px;
1001 | opacity: 0;
1002 | transition: opacity .4s;
1003 | -moz-transition: opacity .4s;
1004 | -webkit-transition: opacity .4s;
1005 | -o-transition: opacity .4s;
1006 | -ms-transition: opacity .4s;
1007 | }
1008 |
1009 | .CodeMirror-lint-mark-error, .CodeMirror-lint-mark-warning {
1010 | background-position: left bottom;
1011 | background-repeat: repeat-x;
1012 | }
1013 |
1014 | .CodeMirror-lint-mark-error {
1015 | background-image:
1016 | url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAQAAAADCAYAAAC09K7GAAAAAXNSR0IArs4c6QAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB9sJDw4cOCW1/KIAAAAZdEVYdENvbW1lbnQAQ3JlYXRlZCB3aXRoIEdJTVBXgQ4XAAAAHElEQVQI12NggIL/DAz/GdA5/xkY/qPKMDAwAADLZwf5rvm+LQAAAABJRU5ErkJggg==")
1017 | ;
1018 | }
1019 |
1020 | .CodeMirror-lint-mark-warning {
1021 | background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAQAAAADCAYAAAC09K7GAAAAAXNSR0IArs4c6QAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB9sJFhQXEbhTg7YAAAAZdEVYdENvbW1lbnQAQ3JlYXRlZCB3aXRoIEdJTVBXgQ4XAAAAMklEQVQI12NkgIIvJ3QXMjAwdDN+OaEbysDA4MPAwNDNwMCwiOHLCd1zX07o6kBVGQEAKBANtobskNMAAAAASUVORK5CYII=");
1022 | }
1023 |
1024 | .CodeMirror-lint-marker-error, .CodeMirror-lint-marker-warning {
1025 | background-position: center center;
1026 | background-repeat: no-repeat;
1027 | cursor: pointer;
1028 | display: inline-block;
1029 | height: 16px;
1030 | width: 16px;
1031 | vertical-align: middle;
1032 | position: relative;
1033 | }
1034 |
1035 | .CodeMirror-lint-message-error, .CodeMirror-lint-message-warning {
1036 | padding-left: 18px;
1037 | background-position: top left;
1038 | background-repeat: no-repeat;
1039 | }
1040 |
1041 | .CodeMirror-lint-marker-error, .CodeMirror-lint-message-error {
1042 | background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAAAHlBMVEW7AAC7AACxAAC7AAC7AAAAAAC4AAC5AAD///+7AAAUdclpAAAABnRSTlMXnORSiwCK0ZKSAAAATUlEQVR42mWPOQ7AQAgDuQLx/z8csYRmPRIFIwRGnosRrpamvkKi0FTIiMASR3hhKW+hAN6/tIWhu9PDWiTGNEkTtIOucA5Oyr9ckPgAWm0GPBog6v4AAAAASUVORK5CYII=");
1043 | }
1044 |
1045 | .CodeMirror-lint-marker-warning, .CodeMirror-lint-message-warning {
1046 | background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAAANlBMVEX/uwDvrwD/uwD/uwD/uwD/uwD/uwD/uwD/uwD6twD/uwAAAADurwD2tQD7uAD+ugAAAAD/uwDhmeTRAAAADHRSTlMJ8mN1EYcbmiixgACm7WbuAAAAVklEQVR42n3PUQqAIBBFUU1LLc3u/jdbOJoW1P08DA9Gba8+YWJ6gNJoNYIBzAA2chBth5kLmG9YUoG0NHAUwFXwO9LuBQL1giCQb8gC9Oro2vp5rncCIY8L8uEx5ZkAAAAASUVORK5CYII=");
1047 | }
1048 |
1049 | .CodeMirror-lint-marker-multiple {
1050 | background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAcAAAAHCAMAAADzjKfhAAAACVBMVEUAAAAAAAC/v7914kyHAAAAAXRSTlMAQObYZgAAACNJREFUeNo1ioEJAAAIwmz/H90iFFSGJgFMe3gaLZ0od+9/AQZ0ADosbYraAAAAAElFTkSuQmCC");
1051 | background-repeat: no-repeat;
1052 | background-position: right bottom;
1053 | width: 100%; height: 100%;
1054 | }
1055 | .CodeMirror-hints {
1056 | background: white;
1057 | -webkit-box-shadow: 0 1px 3px rgba(0, 0, 0, 0.45);
1058 | -moz-box-shadow: 0 1px 3px rgba(0, 0, 0, 0.45);
1059 | box-shadow: 0 1px 3px rgba(0, 0, 0, 0.45);
1060 | font-family: 'Consolas', 'Inconsolata', 'Droid Sans Mono', 'Monaco', monospace;
1061 | font-size: 13px;
1062 | list-style: none;
1063 | margin: 0;
1064 | margin-left: -6px;
1065 | max-height: 14.5em;
1066 | overflow-y: auto;
1067 | overflow: hidden;
1068 | padding: 0;
1069 | position: absolute;
1070 | z-index: 10;
1071 | }
1072 |
1073 | .CodeMirror-hints-wrapper {
1074 | background: white;
1075 | -webkit-box-shadow: 0 1px 3px rgba(0, 0, 0, 0.45);
1076 | -moz-box-shadow: 0 1px 3px rgba(0, 0, 0, 0.45);
1077 | box-shadow: 0 1px 3px rgba(0, 0, 0, 0.45);
1078 | margin-left: -6px;
1079 | position: absolute;
1080 | z-index: 10;
1081 | }
1082 |
1083 | .CodeMirror-hints-wrapper .CodeMirror-hints {
1084 | -webkit-box-shadow: none;
1085 | -moz-box-shadow: none;
1086 | box-shadow: none;
1087 | position: relative;
1088 | margin-left: 0;
1089 | z-index: 0;
1090 | }
1091 |
1092 | .CodeMirror-hint {
1093 | border-top: solid 1px #f7f7f7;
1094 | color: #141823;
1095 | cursor: pointer;
1096 | margin: 0;
1097 | max-width: 300px;
1098 | overflow: hidden;
1099 | padding: 2px 6px;
1100 | white-space: pre;
1101 | }
1102 |
1103 | li.CodeMirror-hint-active {
1104 | background-color: #08f;
1105 | border-top-color: white;
1106 | color: white;
1107 | }
1108 |
1109 | .CodeMirror-hint-information {
1110 | border-top: solid 1px #c0c0c0;
1111 | max-width: 300px;
1112 | padding: 4px 6px;
1113 | position: relative;
1114 | z-index: 1;
1115 | }
1116 |
1117 | .CodeMirror-hint-information:first-child {
1118 | border-bottom: solid 1px #c0c0c0;
1119 | border-top: none;
1120 | margin-bottom: -1px;
1121 | }
1122 |
--------------------------------------------------------------------------------
/webpack.config.js:
--------------------------------------------------------------------------------
1 |
2 | const path = require('path');
3 | const webpack = require('webpack')
4 | module.exports = {
5 | //devtool: 'source-maps', // use this when using the chrome devtools debugger
6 | devtool: 'eval',
7 | context: path.join(__dirname, "src"),
8 | entry: {
9 | app: ['babel-polyfill','./index.js', 'webpack-hot-middleware/client']
10 | },
11 | output: {
12 | filename: 'bundle.js',
13 | path: path.join(__dirname, 'build'),
14 | publicPath: '/static/'
15 | },
16 | plugins: [
17 | new webpack.HotModuleReplacementPlugin(),
18 | new webpack.NoErrorsPlugin(),
19 | ],
20 | resolve: {
21 | extensions: ['', '.js'],
22 | root: path.join(__dirname, 'src')
23 | },
24 | module: {
25 | loaders: [
26 | {test: /\.json$/, loader: 'json-loader'},
27 | {test: /\.txt$/, loader: 'raw-loader'},
28 | {test: /\.(png|jpg|jpeg|gif|svg|woff|woff2)$/, loader: 'url-loader?limit=10000'},
29 | {test: /\.(eot|ttf|wav|mp3)$/, loader: 'file-loader'},
30 | {
31 | test: /\.js$/,
32 | loader: 'babel',
33 | include: [path.join(__dirname, 'src')]
34 | }
35 | ]
36 | }
37 | };
38 |
--------------------------------------------------------------------------------