Skip to content

Commit

Permalink
Add hot-reload for server side
Browse files Browse the repository at this point in the history
  • Loading branch information
li-kai committed Aug 26, 2018
1 parent 9a09950 commit c11fde0
Show file tree
Hide file tree
Showing 9 changed files with 2,236 additions and 377 deletions.
9 changes: 0 additions & 9 deletions api/data/.babelrc
Original file line number Diff line number Diff line change
Expand Up @@ -6,22 +6,13 @@
"targets": {
"node": "current"
},
"modules": false,
"loose": true,
"useBuiltIns": true
}
],
"flow",
"bluebird"
],
"env": {
"development": {
"plugins": ["transform-es2015-modules-commonjs"]
},
"test": {
"plugins": ["transform-es2015-modules-commonjs"]
}
},
"plugins": [
["transform-object-rest-spread", {
"useBuiltIns": true
Expand Down
1 change: 1 addition & 0 deletions api/data/.env.example
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
NODE_ENV=development
DB_DEBUG=false
PORT=8081
16 changes: 9 additions & 7 deletions api/data/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
},
"scripts": {
"ci": "yarn lint && yarn test",
"start": "cross-env NODE_ENV=development babel-node src | bunyan -o short --color",
"start": "webpack | bunyan -o short --color",
"test": "jest --config jest.config.js --coverage",
"test:watch": "jest --config jest.config.js --watch",
"lint": "eslint . --ignore-path ../../.gitignore",
Expand All @@ -31,13 +31,10 @@
"axios": "^0.18.0",
"bluebird": "^3.5.1",
"bunyan": "^1.8.12",
"cross-env": "^5.1.3",
"dotenv": "^5.0.1",
"fs-extra": "^5.0.0",
"graphql": "^0.13.2",
"graphql-tools": "^2.21.0",
"knex": "^0.14.4",
"koa-bunyan-logger": "^2.0.0",
"lodash": "^4.17.5",
"sanitize-filename": "^1.6.1",
"sqlite3": "^3.1.13"
Expand All @@ -47,17 +44,22 @@
"babel-core": "^6.26.0",
"babel-eslint": "^8.2.2",
"babel-jest": "^22.4.1",
"babel-loader": "^7.1.5",
"babel-plugin-transform-object-rest-spread": "^6.26.0",
"babel-preset-bluebird": "^1.0.1",
"babel-preset-env": "^1.6.1",
"babel-preset-env": "^1.7.0",
"babel-preset-flow": "^6.23.0",
"eslint": "^4.18.2",
"eslint-config-airbnb-base": "^12.1.0",
"eslint-config-prettier": "^2.9.0",
"eslint-import-resolver-node": "^0.3.2",
"eslint-plugin-import": "^2.9.0",
"eslint-plugin-prettier": "^2.6.0",
"jest": "^22.4.2",
"prettier": "^1.11.1"
"jest": "^23.5.0",
"prettier": "^1.14.2",
"start-server-webpack-plugin": "^2.2.5",
"webpack": "^4.17.1",
"webpack-cli": "^3.1.0",
"webpack-node-externals": "^1.7.2"
}
}
87 changes: 87 additions & 0 deletions api/data/src/graphql/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
import { gql } from 'apollo-server';
import _ from 'lodash';
import jsonData from './jsonData';

const typeDefs = gql`
# Describes a module for, may span different semesters
type Module {
code: String!
title: String!
department: String
description: String
credit: Float
workload: String
prerequisite: String
corequisite: String
corsBiddingStats: [CorsBiddingStats]
# Refers to the history of the module throughout semesters
history: [ModuleInfo]!
}
# Describes a particular module for a semester
type ModuleInfo {
semester: Int
examDate: String
examOpenBook: Boolean
examDuration: String
examVenue: String
timetable: [Lesson]
}
# Bidding stats for Cors
type CorsBiddingStats {
quota: Int
bidders: Int
lowestBid: Int
lowestSuccessfulBid: Int
highestBid: Int
faculty: String
studentAcctType: String
acadYear: String
semester: Int
round: String
group: String
}
# A lesson conducted, may it be a lecture, laboratory or lecture
type Lesson {
classNo: String!
lessonType: String!
weekText: String!
dayText: String!
startTime: String!
endTime: String!
venue: String!
}
# the schema allows the following query:
type Query {
modules(acadYear: String!, first: Int, offset: Int): [Module!]!
module(acadYear: String!, code: String!): Module
}
schema {
query: Query
}
`;

const resolvers = {
Query: {
modules(root, { acadYear, first, offset }) {
const yearData = jsonData[acadYear];
if (yearData == null) {
return [];
}
const modules = Object.values(yearData);
return modules.slice(offset, offset ? offset + first : first);
},
module(root, { acadYear, code }) {
return _.get(jsonData, [acadYear, code]);
},
},
};

export default {
typeDefs,
resolvers,
};
File renamed without changes.
127 changes: 26 additions & 101 deletions api/data/src/index.js
Original file line number Diff line number Diff line change
@@ -1,111 +1,36 @@
import { ApolloServer, gql } from 'apollo-server';
import _ from 'lodash';
import { ApolloServer } from 'apollo-server';
import playgroundConfig from './config/graphqlPlayground';
import log from './util/log';
import jsonData from './jsonData';

const typeDefs = gql`
# Describes a module for, may span different semesters
type Module {
code: String!
title: String!
department: String
description: String
credit: Float
workload: String
prerequisite: String
corequisite: String
corsBiddingStats: [CorsBiddingStats]
# Refers to the history of the module throughout semesters
history: [ModuleInfo]!
}
# Describes a particular module for a semester
type ModuleInfo {
semester: Int
examDate: String
examOpenBook: Boolean
examDuration: String
examVenue: String
timetable: [Lesson]
}
# Bidding stats for Cors
type CorsBiddingStats {
quota: Int
bidders: Int
lowestBid: Int
lowestSuccessfulBid: Int
highestBid: Int
faculty: String
studentAcctType: String
acadYear: String
semester: Int
round: String
group: String
}
# A lesson conducted, may it be a lecture, laboratory or lecture
type Lesson {
classNo: String!
lessonType: String!
weekText: String!
dayText: String!
startTime: String!
endTime: String!
venue: String!
}
# the schema allows the following query:
type Query {
modules(acadYear: String!, first: Int, offset: Int): [Module!]!
module(acadYear: String!, code: String!): Module
}
schema {
query: Query
}
`;

const resolvers = {
Query: {
modules(root, { acadYear, first, offset }) {
const yearData = jsonData[acadYear];
if (yearData == null) {
return [];
}
const modules = Object.values(yearData);
return modules.slice(offset, offset ? offset + first : first);
function makeServer() {
// enable hot-reload server side
// eslint-disable-next-line global-require
const graphqlSchema = require('./graphql').default;
return new ApolloServer({
typeDefs: graphqlSchema.typeDefs,
/* Apollo is mutating resolvers */
resolvers: { ...graphqlSchema.resolvers },
playground: playgroundConfig,
formatError: (error) => {
log.error(error);
return error;
},
module(root, { acadYear, code }) {
return _.get(jsonData, [acadYear, code]);
formatResponse: (response) => {
log.info(response);
return response;
},
},
};
});
}

const server = new ApolloServer({
typeDefs,
/* Apollo is mutating resolvers */
resolvers: { ...resolvers },
playground: playgroundConfig,
formatError: (error) => {
log.error(error);
return error;
},
formatResponse: (response) => {
log.info(response);
return response;
},
let serverInstance = makeServer();
serverInstance.listen(process.env.PORT).then(({ url }) => {
log.info(`🚀 Server ready at ${url}`);
});

if (process.env.NODE_ENV !== 'test') {
server.listen().then(({ url }) => {
log.info(`🚀 Server ready at ${url}`);
if (module.hot) {
module.hot.accept('./graphql', async () => {
await serverInstance.stop();
serverInstance = makeServer();
await serverInstance.listen(process.env.PORT);
});
}

/* For testing purposes */
export default {
typeDefs,
resolvers,
};
4 changes: 2 additions & 2 deletions api/data/src/index.test.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { graphql } from 'graphql';
import { makeExecutableSchema } from 'graphql-tools';
import index from './index';
import { makeExecutableSchema } from 'apollo-server';
import index from './';

const schema = makeExecutableSchema(index);

Expand Down
39 changes: 39 additions & 0 deletions api/data/webpack.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
require('dotenv').config();
const webpack = require('webpack');
const path = require('path');
const nodeExternals = require('webpack-node-externals');
const StartServerPlugin = require('start-server-webpack-plugin');

const IS_DEV = process.env.NODE_ENV === 'development';

module.exports = {
mode: IS_DEV ? 'development' : 'production',
entry: ['webpack/hot/poll?1000', './src/index'],
watch: IS_DEV,
target: 'node',
stats: 'minimal',
externals: [
nodeExternals({
whitelist: ['webpack/hot/poll?1000'],
}),
],
module: {
rules: [
{
test: /\.js$/,
use: 'babel-loader',
exclude: /node_modules/,
},
],
},
plugins: [
new StartServerPlugin('server.js'),
new webpack.HotModuleReplacementPlugin(),
new webpack.NoEmitOnErrorsPlugin(),
new webpack.EnvironmentPlugin(['NODE_ENV']),
],
output: {
path: path.join(__dirname, 'build'),
filename: 'server.js',
},
};
Loading

0 comments on commit c11fde0

Please sign in to comment.