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

Work on MVP #16

Merged
merged 19 commits into from
Feb 18, 2020
Merged
Show file tree
Hide file tree
Changes from 9 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
426 changes: 397 additions & 29 deletions package-lock.json

Large diffs are not rendered by default.

6 changes: 5 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
"scripts": {
"start": "node src/index.js",
"start-debug": "cross-env DEBUG=gh-canary:* npm run start",
"lint": "eslint src test",
"lint": "eslint src/server test",
wentwrong marked this conversation as resolved.
Show resolved Hide resolved
"test": "npm run lint && mocha test/**/*-test.js",
"test-debug": "cross-env DEBUG=gh-canary:* npm test",
"watch": "npm test -- -w",
Expand All @@ -27,13 +27,17 @@
"cross-env": "^7.0.0",
"eslint": "^6.8.0",
"mocha": "^7.0.1",
"morgan": "^1.9.1",
"proxyquire": "^2.1.3",
"sinon": "^8.1.1"
},
"dependencies": {
"@octokit/rest": "^16.43.1",
"debug": "^4.1.1",
"dedent": "^0.7.0",
"dotenv": "^8.2.0",
"express": "^4.17.1",
"globby": "^11.0.0",
"got": "^10.4.0"
}
}
22 changes: 0 additions & 22 deletions src/app.js

This file was deleted.

16 changes: 16 additions & 0 deletions src/client/public/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Dashboard</title>
<meta name="description" content="Dashboard for github pull-requests notifier">
<meta name="author" content="wentwrong">
<script src="script.js"></script>
</head>

<body>
<div class="pull-requests">
</div>
<script>new App()</script>
</body>
</html>
33 changes: 33 additions & 0 deletions src/client/public/script.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
class App {
constructor() {
this.pullRequestsDiv = document.querySelector('.pull-requests');
this.renderPage();
}

async renderPage () {
try {
const response = await fetch('api/v1/pulls/list');
const { pullRequestList } = await response.json();

pullRequestList.forEach(pr => this._createNewPullRequestDiv(pr));
}
catch (err) {
console.log(err);
const errorMessage = document.createElement('div');
errorMessage.innerHTML = 'An error has occured while fetching data from server.';

this.pullRequestsDiv.appendChild(errorMessage);
}
}

_createNewPullRequestDiv (pullRequest) {
const pullRequestDiv = document.createElement('div');

pullRequestDiv.innerHTML = (`<p>
<a href="${pullRequest['html_url']}">
${pullRequest['title']}
</a> by ${pullRequest['user']['login']}</p>`);

this.pullRequestsDiv.appendChild(pullRequestDiv);
}
}
6 changes: 0 additions & 6 deletions src/config.js

This file was deleted.

3 changes: 1 addition & 2 deletions src/index.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
const App = require('./app');
const App = require('./server/app');

if (require.main === module) {
const app = new App();

app.run();
}

Expand Down
42 changes: 42 additions & 0 deletions src/server/app.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
const config = require('./config');
const express = require('express');
const Server = require('./my-server');
const createRoutes = require('./routes');
const morgan = require('morgan');

class App {
constructor ({ port = config.PORT, host = config.HOST } = {}) {
this.port = port;
this.host = host;
}

async _configExpress (e) {
const routes = await createRoutes(config.ROUTES_DIR);

e.use(routes);

if (config.NODE_ENV === 'development')
e.use(morgan('dev'));
wentwrong marked this conversation as resolved.
Show resolved Hide resolved


e.use(express.static(config.STATIC_DIR));

return e;
}

async run () {
const configuredExpress = await this._configExpress(express());

this.server = new Server(configuredExpress, this.port, this.host);
await this.server.start();
}

async close () {
if (this.server)
await this.server.stop();
else
throw new Error('Stop when application not running');
}
}

module.exports = App;
28 changes: 28 additions & 0 deletions src/server/config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
require('dotenv').config();
const debug = require('./debug');

process
wentwrong marked this conversation as resolved.
Show resolved Hide resolved
.on('unhandledRejection', reason => {
debug.error('Unhandled promise rejection');
debug.error(reason);
})
.on('uncaughtException', err => {
debug.error('Uncaught exception');
debug.error(err);
});

const [ OWNER, REPO ] = process.env.REPO_SLUG ?
process.env.REPO_SLUG.split('/') : [ null, null ];

module.exports = {
PORT: process.env.PORT || 3000,
HOST: process.env.HOST || '127.0.0.1',
NODE_ENV: process.env.NODE_ENV || 'production',
OWNER,
REPO,
API_PREFIX: '/api/v1',
MOCK_GITHUB_PREFIX: '/github-api',
STATIC_DIR: 'src/client/public',
ROUTES_DIR: 'src/server/routes',
GITHUB_API_URL: 'https://api.github.com'
};
39 changes: 39 additions & 0 deletions src/server/controllers/pullcontroller.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
const config = require('../config');
const debug = require('../debug');
const { Octokit } = require('@octokit/rest');

class PullController {
static async list (req, res) {
const octokit = Octokit({
baseUrl: config.GITHUB_API_URL
});

const options = octokit.pulls.list.endpoint.merge({
owner: config.OWNER,
repo: config.REPO
});

try {
const pullRequests = [];

for await (const page of octokit.paginate.iterator(options))
pullRequests.push(page['data'].filter(pr => pr['author_association'] === 'NONE'));

res.send({ pullRequestList: pullRequests.flat() });
}
catch (err) {
debug.error(`Error happened when ${req.ip} requests ${req.path}`);
debug.error(err);

res
.status(500)
.json({ error: 'An error has occured while fetching pull-requests from server.' });
}
}

static async addComment (req, res) {
return res.json({ message: 'NOT IMPLEMENTED YET' });
}
}

module.exports = PullController;
1 change: 1 addition & 0 deletions src/debug.js → src/server/debug.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,6 @@ const debug = require('debug');

module.exports = {
log: debug('gh-canary:log'),
warn: debug('gh-canary:warn'),
error: debug('gh-canary:error')
};
10 changes: 8 additions & 2 deletions src/my-server.js → src/server/my-server.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,14 @@ class Server {
log(`Server started on ${this.host}:${this.port}`);
}
catch (err) {
error(`Server failed to start`);

switch (err.code) {
wentwrong marked this conversation as resolved.
Show resolved Hide resolved
case 'EACCES':
error(`Server requires elevated privileges`);
break;
case 'EADDRINUSE':
error(`Server is already in use`);
break;
}
throw err;
}
}
Expand Down
14 changes: 14 additions & 0 deletions src/server/routes/api/v1/pulls/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
const express = require('express');
const PullController = require('../../../../controllers/pullcontroller');

class PullsRouter extends express.Router {
constructor (...props) {
super(...props);

this.get('/list', PullController.list)
.post('/addComment', PullController.addComment);

}
}

module.exports = PullsRouter;
29 changes: 29 additions & 0 deletions src/server/routes/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
const path = require('path');
const globby = require('globby');
const debug = require('../debug');
const mainRouter = require('express').Router();

async function createRoutes (routesDir) {
try {
const files = await globby('**/!(index.js)/*.js', { cwd: path.resolve(routesDir) });

files
.map(filename => ({
routePrefix: path.dirname(filename),
Router: require(`./${filename}`)
}))
.map(({
routePrefix,
Router
}) => mainRouter.use(`/${routePrefix}`, new Router()));

return mainRouter;
}
catch (err) {
debug.error('An error has occured when processing app routes');
debug.error(err);
throw err;
}
}

module.exports = createRoutes;
9 changes: 0 additions & 9 deletions static/index.html

This file was deleted.

10 changes: 5 additions & 5 deletions test/routes/index-test.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
const { expect } = require('chai');
const got = require('got');
const App = require('../../');
const App = require('../../src/index');

describe('Index page', () => {
describe('Index', () => {
const app = new App();

before(async () => {
Expand All @@ -13,10 +13,10 @@ describe('Index page', () => {
await app.close();
});

wentwrong marked this conversation as resolved.
Show resolved Hide resolved
it('app should show "Hello Node.js" message', async () => {
const response = await got(`http://${app.server.host}:${app.server.port}`);
it(`should return 200 response`, async () => {
const response = await got(`http://${app.server.host}:${app.server.port}/`);

expect(response.body).contain('Dashboard');
expect(response.statusCode).equal(200);
expect(response.body).include('Hello Node.js');
});
});