diff --git a/lib/api/schema.js b/lib/api/schema.js new file mode 100644 index 0000000..9ba9c53 --- /dev/null +++ b/lib/api/schema.js @@ -0,0 +1,21 @@ +var makeExecutableSchema = require('graphql-tools').makeExecutableSchema; +var requireSchema = require('../utils/requireSchema'); +var schema = requireSchema('./schema_def.graphql', require); + +var resolvers = { + Query: { + reports: function report(source, args, context, ast) { + return context.store.listAll(); + }, + report: function report(source, args, context, ast) { + return context.store.get(args.id); + } + } +}; + +var executableSchema = makeExecutableSchema({ + typeDefs: schema, + resolvers: resolvers +}); + +module.exports = executableSchema; diff --git a/lib/api/schema_def.graphql b/lib/api/schema_def.graphql new file mode 100644 index 0000000..b98612b --- /dev/null +++ b/lib/api/schema_def.graphql @@ -0,0 +1,60 @@ +# A list of options for the type of the report +enum ReportType { + STATE + ACTION + STATES + ACTIONS +} + +type Report { + # Report ID + id: ID! + # Type of the report, can be: STATE, ACTION, STATES, ACTIONS + type: ReportType, + # Briefly what happened + title: String, + # Details supplied by the user + description: String, + # The last dispatched action before the report was sent + action: String, + # Stringified actions or the state or both, which should be loaded inti the application to reproduce the exact behavior + payload: String, + # Stringified preloaded state object. Could be the innitial state of teh app or commited state (after dispatching COMMIT action or reaching maxAge) + preloadedState: String, + # Screenshot url or blob as a string + screenshot: String, + # User Agent String + userAgent: String, + # Application version to group the reports and versioning + version: String, + # Used to identify the user who sent the report + userId: String, + # More detailed data about the user, usually it's a stringified object + user: String, + # Everything else you want to send + meta: String, + # Error message which invoked sending the report + exception: String, + # Id to identify the store in case there are multiple stores + instanceId: String, + # Timestamp when the report was added + added: String + # Id to identify the application (from apps table) + appId: ID +} + +# Explore GraphQL query schema +type Query { + # List all reports + reports: [Report] + # Get a report by ID + report( + # Report ID + id: ID! + ): Report +} + +schema { + query: Query + #mutation: Mutation +} diff --git a/lib/middleware/graphiql.js b/lib/middleware/graphiql.js new file mode 100644 index 0000000..1d0b714 --- /dev/null +++ b/lib/middleware/graphiql.js @@ -0,0 +1,13 @@ +var graphiqlExpress = require('apollo-server').graphiqlExpress; + +module.exports = graphiqlExpress({ + endpointURL: '/graphql', + query: + '{\n' + + ' reports {\n' + + ' id,\n' + + ' type,\n' + + ' title\n' + + ' }\n' + + '}' +}); diff --git a/lib/middleware/graphql.js b/lib/middleware/graphql.js new file mode 100644 index 0000000..64bfaf9 --- /dev/null +++ b/lib/middleware/graphql.js @@ -0,0 +1,13 @@ +var apolloExpress = require('apollo-server').apolloExpress; +var schema = require('../api/schema'); + +module.exports = function (store) { + return apolloExpress(function() { + return { + schema: schema, + context: { + store: store + } + }; + }); +}; diff --git a/lib/utils/requireSchema.js b/lib/utils/requireSchema.js new file mode 100644 index 0000000..d5b73ca --- /dev/null +++ b/lib/utils/requireSchema.js @@ -0,0 +1,6 @@ +var fs = require('fs'); + +module.exports = function(name, require) { + return fs.readFileSync(require.resolve(name)).toString(); + // return GraphQL.buildSchema(schema); +}; diff --git a/lib/worker.js b/lib/worker.js index cffa9b9..1419ef9 100644 --- a/lib/worker.js +++ b/lib/worker.js @@ -3,6 +3,8 @@ var app = require('express')(); var bodyParser = require('body-parser'); var cors = require('cors'); var morgan = require('morgan'); +var graphiqlMiddleware = require('./middleware/graphiql'); +var graphqlMiddleware = require('./middleware/graphql'); var createStore = require('./store'); module.exports.run = function(worker) { @@ -22,6 +24,8 @@ module.exports.run = function(worker) { else app.use(morgan('combined')); } + app.use('/graphiql', graphiqlMiddleware); + app.get('*', function(req, res) { res.render('index', { port: worker.options.port }); }); @@ -29,6 +33,9 @@ module.exports.run = function(worker) { app.use(cors({ methods: 'POST' })); app.use(bodyParser.json({ limit: limit })); app.use(bodyParser.urlencoded({ limit: limit, extended: false })); + + app.use('/graphql', graphqlMiddleware(store)); + app.post('/', function(req, res) { if (!req.body) return res.status(404).end(); switch(req.body.op) { diff --git a/package.json b/package.json index 65550c1..79f3278 100644 --- a/package.json +++ b/package.json @@ -28,12 +28,15 @@ }, "homepage": "https://github.com/zalmoxisus/remotedev-server", "dependencies": { + "apollo-server": "^0.3.3", "body-parser": "^1.15.0", "chalk": "^1.1.3", "cors": "^2.7.1", "ejs": "^2.4.1", "express": "^4.13.3", "getport": "^0.1.0", + "graphql": "^0.8.0", + "graphql-tools": "^0.8.1", "js-data": "^2.9.0", "knex": "^0.12.6", "lodash": "^4.15.0",