Skip to content
This repository has been archived by the owner on Nov 21, 2022. It is now read-only.

Commit

Permalink
Rewrite in React + Refactor (#333)
Browse files Browse the repository at this point in the history
* Added react-scripts

* Move Tailwind

* Setup base pages

* Added me page

* Move Footer & GithubCorner to React, update .env

* More styling, GithubCorner, reminder and footer are now showing

* Fixed fonts

* Update not found page

* Reorganised pages, began working on username form

* Finished TimeMessage

* Finished UsernameForm

* Began simplifying api

* Further simplified API

* Further simplified API

* API now works too

* Setup pr list file structure

* Began working on PR list

* Finished PR list

* Fix spaces

* Remove handlebars templates

* Removed old public folder

* Spacing fixes, better error handling

* Fix share buttons

* Remove unused dependencies

* Styling and spacing fixes

* Replace eslint with prettier, use 2 spaces

* Use REACT_APP_HOSTNAME

* Updated README and start commands

* Fix env files

* Allow react to built and the web app run from a single server

* Add build folder to .gitignore, fix missing space & use arrow function

* Updated .env.example

* Update start script ready for deploy

* Update Dockerfile and README
  • Loading branch information
rafaelklaessen authored and jenkoian committed Oct 29, 2018
1 parent 7d2017d commit 7d66365
Show file tree
Hide file tree
Showing 75 changed files with 8,214 additions and 2,299 deletions.
4 changes: 2 additions & 2 deletions .editorconfig
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@ root = true

[*]
indent_style = space
indent_size = 4
indent_size = 2
end_of_line = lf

charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
max_line_length = 120
max_line_length = 80
11 changes: 7 additions & 4 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,11 @@ GITHUB_TOKEN=YOUR_TOKEN
# production or development
NODE_ENV=production

# code used to hook up google analytics
GA_CODE=YOUR_CODE
# Code used to hook up google analytics.
REACT_APP_GA_CODE=YOUR_CODE

# url of the current environment
APP_URL=http://localhost:5000
# URL of the frontend React app.
REACT_APP_HOSTNAME=http://localhost:3000

# URL of the backend API. This is the same as the URL of the frontend React app in production.
REACT_APP_API_URL=http://localhost:5000
1 change: 0 additions & 1 deletion .eslintignore

This file was deleted.

42 changes: 0 additions & 42 deletions .eslintrc

This file was deleted.

1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,4 @@ Thumbs.db
# Compiled js
public/js/main-compiled.js
public/js/main-compiled.js.map
build/
3 changes: 3 additions & 0 deletions .prettierrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
singleQuote: true
tabWidth: 2
printWidth: 80
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,4 @@ COPY --chown=octocat:octocat . /app

EXPOSE 5000

ENTRYPOINT ["node", "index.js"]
ENTRYPOINT ["yarn", "start"]
16 changes: 12 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,17 @@ Useful checker web app to see how close you are to achieving the requirements fo

## 2018!

I had some grand plans for this years edition but unfortunately I've not had the time. It basically equates to using
~I had some grand plans for this years edition but unfortunately I've not had the time. It basically equates to using
React on the front end. I did make a start, and may look to release it midway through Hacktoberfest. If you fancy hacking
along with me, keep an eye on the react branch. Other than that, I've done a simple reskin and more or less
kept it as it were last year (obviously updated the year and new PR requirement).
kept it as it were last year (obviously updated the year and new PR requirement).~

With massive thanks to [Rafael Klaessen](https://github.com/rafaelklaessen) the react version is now merged and live!

I will add some tickets when I think of them of improvements etc. feel free to suggest any you can think of though too.

You may notice the domain has changed, the old heroku app is still up and I will deploy to both, but the new domain is hosted on
digital ocean as they were kind enough to offer me some hosting vouchers.
You may notice the domain has changed, the old heroku app is still up and I will deploy to both, but the new domain is hosted on
digital ocean as they were kind enough to offer me some hosting vouchers.

Happy hacking!

Expand All @@ -44,6 +46,12 @@ Happy hacking!

* Point browser to [localhost:5000](http://localhost:5000)

Want to run the API server and the frontend in their own processes? Use this:
```bash
$ yarn start-frontend
$ yarn start-server
```

### Running the app within Docker

As an alternative to the section above, you can run the app within a Docker container:
Expand Down
18 changes: 18 additions & 0 deletions api/controllers/pr/errors.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
'use strict';

const statusCodes = {
notUser: 400
};

const errorDescriptions = {
notUser: 'Username must belong to a user account.'
};

const getStatusCode = error => statusCodes[error] || 400;

const getErrorDescription = error =>
errorDescriptions[error] ||
"Couldn't find any data or we hit an error, err try again?";

module.exports.getStatusCode = getStatusCode;
module.exports.getErrorDescription = getErrorDescription;
26 changes: 26 additions & 0 deletions api/controllers/pr/findPrs/getNextPage.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
'use strict';

const getNextPage = (response, github, pullRequestData) =>
new Promise((resolve, reject) => {
github.getNextPage(response, (err, res) => {
if (err) {
return reject();
}

const newPullRequestData = pullRequestData.concat(res.data.items);

if (github.hasNextPage(res)) {
getNextPage(res, github, newPullRequestData).then(pullRequestData =>
resolve(pullRequestData)
);
return;
}

if (process.env.NODE_ENV !== 'production') {
console.log(`Found ${pullRequestData.length} pull requests.`);
}
resolve(newPullRequestData);
});
});

module.exports = getNextPage;
66 changes: 66 additions & 0 deletions api/controllers/pr/findPrs/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
'use strict';

const _ = require('lodash');
const moment = require('moment');
const logCallsRemaining = require('../logCallsRemaining');
const loadPrs = require('./loadPrs');

const findPrs = (github, username) => {
return loadPrs(github, username)
.then(pullRequestData =>
_.map(pullRequestData, event => {
const repo = event.pull_request.html_url.substring(
0,
event.pull_request.html_url.search('/pull/')
);

const hacktoberFestLabels = _.some(
event.labels,
label => label.name.toLowerCase() === 'hacktoberfest'
);

return {
has_hacktoberfest_label: hacktoberFestLabels,
number: event.number,
open: event.state === 'open',
repo_name: repo.replace('https://github.com/', ''),
title: event.title,
url: event.html_url,
created_at: moment(event.created_at).format('MMMM Do YYYY'),
user: {
login: event.user.login,
url: event.user.html_url
}
};
})
)
.then(prs => {
const checkMergeStatus = _.map(prs, pr => {
const repoDetails = pr.repo_name.split('/');
const pullDetails = {
owner: repoDetails[0],
repo: repoDetails[1],
number: pr.number
};

return github.pullRequests
.checkMerged(pullDetails)
.then(logCallsRemaining)
.then(res => res.meta.status === '204 No Content')
.catch(err => {
// 404 means there wasn't a merge
if (err.code === 404) {
return false;
}

throw err;
});
});

return Promise.all(checkMergeStatus).then(mergeStatus =>
_.zipWith(prs, mergeStatus, (pr, merged) => _.assign(pr, { merged }))
);
});
};

module.exports = findPrs;
42 changes: 42 additions & 0 deletions api/controllers/pr/findPrs/loadPrs.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
'use strict';

const getNextPage = require('./getNextPage');

const buildQuery = (username, searchYear) =>
`-label:invalid+created:${searchYear}-09-30T00:00:00-12:00..${searchYear}-10-31T23:59:59-12:00+type:pr+is:public+author:${username}`;

const loadPrs = (github, username) =>
new Promise((resolve, reject) => {
const today = new Date();
const currentMonth = today.getMonth();
const currentYear = today.getFullYear();
const searchYear = currentMonth < 9 ? currentYear - 1 : currentYear;

github.search.issues(
{
q: buildQuery(username, searchYear),
// 30 is the default but this makes it clearer/allows it to be tweaked
per_page: 100
},
(err, res) => {
if (err) {
return reject();
}

const pullRequestData = res.data.items;
if (github.hasNextPage(res)) {
getNextPage(res, github, pullRequestData).then(pullRequestData =>
resolve(pullRequestData)
);
return;
}

if (process.env.NODE_ENV !== 'production') {
console.log(`Found ${pullRequestData.length} pull requests.`);
}
resolve(pullRequestData);
}
);
});

module.exports = loadPrs;
47 changes: 47 additions & 0 deletions api/controllers/pr/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
'use strict';

const logCallsRemaining = require('./logCallsRemaining');
const findPrs = require('./findPrs');
const { getStatusCode, getErrorDescription } = require('./errors');

/**
* GET /
*/
exports.index = (req, res) => {
const github = req.app.get('github');
const username = req.query.username;

if (!username) {
return res.status(400).json({
error_description: 'No username provided!'
});
}

Promise.all([
findPrs(github, username),
github.users.getForUser({ username }).then(logCallsRemaining)
])
.then(([prs, user]) => {
if (user.data.type !== 'User') {
return Promise.reject('notUser');
}

const data = {
prs,
username,
userImage: user.data.avatar_url
};

res.json(data);
})
.catch(err => {
console.log('Error: ' + err);

const statusCode = getStatusCode(err);
const errorDescription = getErrorDescription(err);

res.status(statusCode).json({
error_description: errorDescription
});
});
};
13 changes: 13 additions & 0 deletions api/controllers/pr/logCallsRemaining.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
'use strict';

const logCallsRemaining = res => {
const callsRemaining = res.meta['x-ratelimit-remaining'];

if (process.env.NODE_ENV !== 'production' || callsRemaining < 100) {
console.log(`API calls remaining: ${callsRemaining}`);
}

return res;
};

module.exports = logCallsRemaining;
48 changes: 48 additions & 0 deletions api/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
'use strict';

const express = require('express');
const bodyParser = require('body-parser');
const cors = require('cors');
const dotenv = require('dotenv');
const setupGithubApi = require('./setupHelpers/setupGithubApi');
const setupErrorHandling = require('./setupHelpers/setupErrorHandling');
const PrController = require('./controllers/pr');
const path = require('path');

const start = () => {
// Load environment variables from .env file
dotenv.load();

const app = express();

const githubApi = setupGithubApi();

const port = process.env.PORT || 5000;

app.set('port', port);
app.set('github', githubApi);

setupErrorHandling(app);

app.use(express.static(path.join(__dirname, '../build')));

app.use(bodyParser.json());

const corsOptions = {
origin: process.env.REACT_APP_HOSTNAME
};

app.use(cors(corsOptions));

app.get('/prs', PrController.index);

app.get('/*', (req, res) => {
res.sendFile(path.join(__dirname, '../build', 'index.html'));
});

app.listen(port, () => {
console.log(`Express server listening on port ${port}`);
});
};

module.exports = start;
Loading

0 comments on commit 7d66365

Please sign in to comment.