Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add hot-reload for server side #1108

Merged
merged 1 commit into from
Aug 26, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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,
};
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
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,
};
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