Browse Source

Merge branch 'master' of https://github.com/jrtechs/github-graphs into HEAD

feature/gh-api-deprecation
jrtechs 4 days ago
parent
commit
7b7f7659f7
75 changed files with 607 additions and 54 deletions
  1. +5
    -5
      .gitignore
  2. +27
    -0
      Dockerfile
  3. +188
    -11
      README.md
  4. +11
    -0
      docker-compose.yml
  5. +0
    -0
      server/gitGraph.service
  6. +14
    -8
      server/package.json
  7. +0
    -0
      server/public/404.html
  8. +0
    -0
      server/public/FriendsGraph.html
  9. +0
    -0
      server/public/GraphGenerator.html
  10. +0
    -0
      server/public/GraphTest.html
  11. +0
    -0
      server/public/OrgRepoGraph.html
  12. +0
    -0
      server/public/TimeLineGraph.html
  13. +0
    -0
      server/public/about.html
  14. +0
    -0
      server/public/favicon.ico
  15. +0
    -0
      server/public/fonts/Comfortaa-Bold.ttf
  16. +0
    -0
      server/public/fonts/Comfortaa-Light.ttf
  17. +0
    -0
      server/public/fonts/Comfortaa-Regular.ttf
  18. +0
    -0
      server/public/ico/baseline-bar_chart-24px.svg
  19. +0
    -0
      server/public/ico/graph.png
  20. +0
    -0
      server/public/ico/outline-timeline-24px.svg
  21. +0
    -0
      server/public/icon.svg
  22. +0
    -0
      server/public/img/DolphinCroissant.png
  23. +0
    -0
      server/public/img/email.svg
  24. +0
    -0
      server/public/img/friends.png
  25. +0
    -0
      server/public/img/friends2.png
  26. +0
    -0
      server/public/img/github-circle.svg
  27. +0
    -0
      server/public/img/githubgraph-logo.svg
  28. +0
    -0
      server/public/img/graphExample.png
  29. +0
    -0
      server/public/img/home-image.png
  30. +0
    -0
      server/public/img/linkedin-box.svg
  31. +0
    -0
      server/public/index.html
  32. +0
    -0
      server/public/js/createOrgInfo.js
  33. +0
    -0
      server/public/js/createOrgRepoGraph.js
  34. +0
    -0
      server/public/js/createOrgTable.js
  35. +0
    -0
      server/public/js/friendsGraph.js
  36. +0
    -0
      server/public/js/githubAPI.js
  37. +0
    -0
      server/public/js/profileGen.js
  38. +0
    -0
      server/public/js/profileTimeLine.js
  39. +0
    -0
      server/public/js/repoGen.js
  40. +0
    -0
      server/public/js/utilities.js
  41. +0
    -0
      server/public/js/vis/img/network/acceptDeleteIcon.png
  42. +0
    -0
      server/public/js/vis/img/network/addNodeIcon.png
  43. +0
    -0
      server/public/js/vis/img/network/backIcon.png
  44. +0
    -0
      server/public/js/vis/img/network/connectIcon.png
  45. +0
    -0
      server/public/js/vis/img/network/cross.png
  46. +0
    -0
      server/public/js/vis/img/network/cross2.png
  47. +0
    -0
      server/public/js/vis/img/network/deleteIcon.png
  48. +0
    -0
      server/public/js/vis/img/network/downArrow.png
  49. +0
    -0
      server/public/js/vis/img/network/editIcon.png
  50. +0
    -0
      server/public/js/vis/img/network/leftArrow.png
  51. +0
    -0
      server/public/js/vis/img/network/minus.png
  52. +0
    -0
      server/public/js/vis/img/network/plus.png
  53. +0
    -0
      server/public/js/vis/img/network/rightArrow.png
  54. +0
    -0
      server/public/js/vis/img/network/upArrow.png
  55. +0
    -0
      server/public/js/vis/img/network/zoomExtends.png
  56. +0
    -0
      server/public/js/vis/vis-graph3d.min.js
  57. +0
    -0
      server/public/js/vis/vis-network.min.css
  58. +0
    -0
      server/public/js/vis/vis-network.min.js
  59. +0
    -0
      server/public/js/vis/vis-timeline-graph2d.min.css
  60. +0
    -0
      server/public/js/vis/vis-timeline-graph2d.min.js
  61. +0
    -0
      server/public/js/vis/vis.css
  62. +0
    -0
      server/public/js/vis/vis.js
  63. +0
    -0
      server/public/js/vis/vis.js.map
  64. +0
    -0
      server/public/js/vis/vis.map
  65. +0
    -0
      server/public/js/vis/vis.min.css
  66. +0
    -0
      server/public/js/vis/vis.min.js
  67. +0
    -0
      server/public/logo.svg
  68. +0
    -0
      server/public/style.css
  69. +2
    -0
      server/routes/api/index.js
  70. +29
    -24
      server/routes/api/v1.js
  71. +310
    -0
      server/routes/api/v2.js
  72. +2
    -2
      server/routes/index.js
  73. +11
    -0
      server/routes/test/v2.js
  74. +0
    -0
      server/run.sh
  75. +8
    -4
      server/server.js

+ 5
- 5
.gitignore View File

@ -2,8 +2,8 @@
# This .gitignore file was automatically created by Microsoft(R) Visual Studio. # This .gitignore file was automatically created by Microsoft(R) Visual Studio.
################################################################################ ################################################################################
/.vs
/node_modules/.bin
/node_modules
/package-lock.json
.env
*/.vs
*/node_modules/.bin
*/node_modules
*/package-lock.json
*/.env

+ 27
- 0
Dockerfile View File

@ -0,0 +1,27 @@
FROM ubuntu
LABEL maintainer="Jeffery Russell"
# install all dependencies
RUN apt-get update && \
apt-get upgrade -y && \
apt-get install -y build-essential && \
apt-get install -y sudo curl && \
curl -sL https://deb.nodesource.com/setup_13.x | sudo -E bash - && \
apt-get install -y nodejs && \
apt-get update && \
apt-get clean
# Create a working directory for the container
RUN mkdir /github-graphs
# copy files from the directory of the Dockerfile to the docker container
COPY /server /github-graphs/server
COPY README.md /github-graphs/
COPY LICENSE /github-graphs/
# setup working directory to the default in the container
WORKDIR /github-graphs/server
# Install dependencies and start the program at RUN
RUN npm install
CMD ["node", "server.js"]

+ 188
- 11
README.md View File

@ -4,40 +4,217 @@ Website for visualizing a persons github network.
![Example Graph](./doc/graphExample.png) ![Example Graph](./doc/graphExample.png)
If you are lucky, you can find the site live [here](https://github-graphs.com/);
If you are lucky, you can find the site live [here](https://github-graphs.com/).
# Built With
## Built With
- [BootStrap](https://getbootstrap.com/) - [BootStrap](https://getbootstrap.com/)
- [jQuery](https://jquery.com/) - [jQuery](https://jquery.com/)
- [Vis JS](http://visjs.org/) - [Vis JS](http://visjs.org/)
- [Github v3 API](https://developer.github.com/v3/)
- [Github v3 API](https://developer.github.com/v3/)
- [Node.js](https://nodejs.org/en/) - [Node.js](https://nodejs.org/en/)
![javascript](./doc/javaScript.jpg) ![javascript](./doc/javaScript.jpg)
# Running
Create a .env file with the code below filled in.
## Deployment
The easiest way to get started with Github-Graphs is to fork this repository
and follow the instructions below.
#### Create a new OAuth app
The objective of creating an app under your github account is to access an
endpoint through the GitHub API and obtain credentials to set your environment
file. For more information on how to create a new OAuth app, consult the corresponding
Github developer documentation [here](https://developer.github.com/apps/building-oauth-apps/creating-an-oauth-app/).
#### Create a .env file
After forking this repository, run the command `cd server/`. Inside that folder,
setup your environment credentials by creating a `.env file` with the code below filled in.
``` ```
CLIENT_ID = <your_github_username>
CLIENT_SECRET = <your_generated_personal_access>
CLIENT_ID = <your_client_ID_from_oauth_app>
CLIENT_SECRET = <your_generated_personal_access_from_oauth_app>
SESSION_SECRET = <any_string_you_want> SESSION_SECRET = <any_string_you_want>
PORT = <any_number> PORT = <any_number>
``` ```
The creation of your OAuth app and the .env file are required steps,
irrespective of your desired deployment environment. For specific directions,
continue by following the steps specified below.
### Deployment on the local machine
#### Install dependencies
Dependencies are installed using `npm`. Therefore, please install the package manager
before proceeding. If you already have `npm` installed, run the command below inside
`server/` in order to install the dependencies in the package directory.
```bash ```bash
npm install npm install
``` ```
#### Explore GitHub-Graphs
Inside `server/`, run the following command to start the program, and in your
browser, check `localhost:8000` to visualize your Github network.
```bash ```bash
node server.js node server.js
``` ```
### Deployment in a Docker container
# Contributing
Github-graphs can also be deployed inside a docker container and displayed in
your browser through port mapping. To get started run the following commands
inside your forked repository.
The easiest way to deploy in a docker container is through the use of our proposed
`docker-compose.yml` file. If you choose this methodology, make sure the port numbers
in your `.env` file matches the docker-compose file. Note that this approach will work
only on systems which have Docker and Docker-compose both installed.
Considering the example provided in our provided docker-compose,
the port number of the .env file should be `PORT= 8000`. Therefore, you could
visualize the Github-Graphs page at `localhost:8080` after running:
```
docker-compose up -d --build
```
In order to clean the environment, you can run the following command.
```
docker-compose down --rmi all
```
Besides the use of docker-compose, deployment with just docker is possible with the following commands:\
```
docker build -t <choose_name_for_image> .
docker run -d --name <choose_name_for_container> -p <local_port_num>:<port_num_from_env_file> <name_of_image>
```
We are very open to new contributors. If you want to contribute to this project, and don't know where to start, look at the [open issues](https://github.com/jrtechs/github-graphs/issues). Once you know what you want to work on, comment on the issue and file a pull request.
For instance, assume I name my image `graph-app`, my container `github-graphs`,
and set the port number in my .env file to `8000`, I can decide to listen on my localhost at port `8080`.
Therefore, my commands are:
```
docker build -t graph-app .
docker run -d --name github-graphs -p 8080:8000 graph-app
```
If you are willing to read the debugging statement on the CLI, do not add `-d` to the `docker run` statement.
At this step, you can now visualize Github graphs at `localhost:<local_port_num>`.
## Contributing
We are very open to new contributors. If you want to contribute to this project,
and don't know where to start, look at the [open issues](https://github.com/jrtechs/github-graphs/issues).
Once you know what you want to work on, comment on the issue and file a pull request.
## API Reference
`GET https://github-graphs.com/api/friends/<username>`
Fetches `https://api.github.com/users/<username>/followers` [(GitHub Reference)](https://developer.github.com/v3/users/followers/#list-followers-of-a-user)
and `https://api.github.com/users/<username>/following` [(GitHub Reference)](https://developer.github.com/v3/users/followers/#list-users-followed-by-another-user)
to generate an array of users following `<username>` and that `<username>` follows each with values `login` and `avatar_url`.
Example result:
```json
[
{
"login": "jrtechs",
"avatar_url": "https://avatars1.githubusercontent.com/u/13894625?s=460&v=4"
}
]
```
---
`GET https://github-graphs.com/api/repositories/<username>`
Fetches `https://api.github.com/users/<username>/repos` and returns an array of the repositories `<username>` owns.
Example result:
```json
[
{
"name": "bash_manager",
"created_at": "2017-09-27T14:38:01Z",
"homepage": "",
"description": "Python scripts I use to manage my ssh connections, drive mounts, and other bash related things. ",
"language": "Python",
"forks": 26,
"watchers": 4,
"open_issues_count": 7,
"license": {
"key": "mpl-2.0",
"name": "Mozilla Public License 2.0",
"spdx_id": "MPL-2.0",
"url": "https://api.github.com/licenses/mpl-2.0",
"node_id": "MDc6TGljZW5zZTE0"
},
"html_url": "https://github.com/jrtechs/bash_manager"
}
]
```
---
`GET https://github-graphs.com/api/org/users/<organization_name>`
Fetches `https://api.github.com/orgs/<organization_name>/members` [(GitHub Reference)](https://developer.github.com/v3/orgs/members/#members-list)
to generate an array of users that are in `<organization_name>` each with values `login` and `avatar_url`.
Example result:
```json
[
{
"login": "jrtechs",
"avatar_url": "https://avatars1.githubusercontent.com/u/13894625?s=460&v=4"
}
]
```
---
`GET https://github-graphs.com/api/org/repositories/<organization_name>`
Fetches `https://api.github.com/orgs/<organization_name>/repos` [(GitHub Reference)](https://developer.github.com/v3/repos/#list-organization-repositories)
to return an array of repositories `<organization_name>` owns.
Example result:
```json
[
{
"name": "vue",
"created_at": "2013-07-29T03:24:51Z",
"homepage": "http://vuejs.org",
"description": "🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.",
"language": "JavaScript",
"forks": 23228,
"watchers": 154891,
"open_issues_count": 419,
"license": {
"key": "mit",
"name": "MIT License",
"spdx_id": "MIT",
"url": "https://api.github.com/licenses/mit",
"node_id": "MDc6TGljZW5zZTEz"
},
"html_url": "https://github.com/vuejs/vue"
}
]
```

+ 11
- 0
docker-compose.yml View File

@ -0,0 +1,11 @@
version: '3.7'
services:
github-graphs:
build:
dockerfile: Dockerfile
image: graph-app:latest
container_name: github-graphs
ports:
- "8080:8000"

gitGraph.service → server/gitGraph.service View File


package.json → server/package.json View File

@ -1,28 +1,34 @@
{ {
"name": "github-graphs", "name": "github-graphs",
"version": "0.0.1",
"version": "0.0.2",
"description": "Generates graphs of github things.", "description": "Generates graphs of github things.",
"main": "server.js", "main": "server.js",
"scripts": { "scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"coveralls": "nyc npm test && nyc report --reporter=text-lcov | coveralls",
"test": "mocha routes/test --exit --reporter spec",
"start": "node server.js" "start": "node server.js"
}, },
"repository": { "repository": {
"type": "git", "type": "git",
"url": "git+https://github.com/jrtechs/github-graphs.git" "url": "git+https://github.com/jrtechs/github-graphs.git"
}, },
"author": "Jeffery Russell",
"author": "Jeffery Russell, Eric Lang",
"license": "ISC", "license": "ISC",
"bugs": { "bugs": {
"url": "https://github.com/jrtechs/github-graphs/issues" "url": "https://github.com/jrtechs/github-graphs/issues"
}, },
"homepage": "https://github.com/jrtechs/github-graphs#readme", "homepage": "https://github.com/jrtechs/github-graphs#readme",
"dependencies": { "dependencies": {
"dotenv": "^8.2.0",
"express": "^4.16.4",
"express-session": "^1.15.6",
"dotenv": "~8.2.0",
"express": "~4.16.4",
"express-session": "~1.15.6",
"fs": "0.0.1-security", "fs": "0.0.1-security",
"got": "^9.6.0",
"memory-cache": "^0.2.0"
"got": "~9.6.0",
"memory-cache": "~0.2.0"
},
"devDependencies": {
"coveralls": "*",
"mocha": "*",
"nyc": "*"
} }
} }

public/404.html → server/public/404.html View File


public/FriendsGraph.html → server/public/FriendsGraph.html View File


public/GraphGenerator.html → server/public/GraphGenerator.html View File


public/GraphTest.html → server/public/GraphTest.html View File


public/OrgRepoGraph.html → server/public/OrgRepoGraph.html View File


public/TimeLineGraph.html → server/public/TimeLineGraph.html View File


public/about.html → server/public/about.html View File


public/favicon.ico → server/public/favicon.ico View File


public/fonts/Comfortaa-Bold.ttf → server/public/fonts/Comfortaa-Bold.ttf View File


public/fonts/Comfortaa-Light.ttf → server/public/fonts/Comfortaa-Light.ttf View File


public/fonts/Comfortaa-Regular.ttf → server/public/fonts/Comfortaa-Regular.ttf View File


public/ico/baseline-bar_chart-24px.svg → server/public/ico/baseline-bar_chart-24px.svg View File


public/ico/graph.png → server/public/ico/graph.png View File


public/ico/outline-timeline-24px.svg → server/public/ico/outline-timeline-24px.svg View File


public/icon.svg → server/public/icon.svg View File


public/img/DolphinCroissant.png → server/public/img/DolphinCroissant.png View File


public/img/email.svg → server/public/img/email.svg View File


public/img/friends.png → server/public/img/friends.png View File


public/img/friends2.png → server/public/img/friends2.png View File


public/img/github-circle.svg → server/public/img/github-circle.svg View File


public/img/githubgraph-logo.svg → server/public/img/githubgraph-logo.svg View File


public/img/graphExample.png → server/public/img/graphExample.png View File


public/img/home-image.png → server/public/img/home-image.png View File


public/img/linkedin-box.svg → server/public/img/linkedin-box.svg View File


public/index.html → server/public/index.html View File


public/js/createOrgInfo.js → server/public/js/createOrgInfo.js View File


public/js/createOrgRepoGraph.js → server/public/js/createOrgRepoGraph.js View File


public/js/createOrgTable.js → server/public/js/createOrgTable.js View File


public/js/friendsGraph.js → server/public/js/friendsGraph.js View File


public/js/githubAPI.js → server/public/js/githubAPI.js View File


public/js/profileGen.js → server/public/js/profileGen.js View File


public/js/profileTimeLine.js → server/public/js/profileTimeLine.js View File


public/js/repoGen.js → server/public/js/repoGen.js View File


public/js/utilities.js → server/public/js/utilities.js View File


public/js/vis/img/network/acceptDeleteIcon.png → server/public/js/vis/img/network/acceptDeleteIcon.png View File


public/js/vis/img/network/addNodeIcon.png → server/public/js/vis/img/network/addNodeIcon.png View File


public/js/vis/img/network/backIcon.png → server/public/js/vis/img/network/backIcon.png View File


public/js/vis/img/network/connectIcon.png → server/public/js/vis/img/network/connectIcon.png View File


public/js/vis/img/network/cross.png → server/public/js/vis/img/network/cross.png View File


public/js/vis/img/network/cross2.png → server/public/js/vis/img/network/cross2.png View File


public/js/vis/img/network/deleteIcon.png → server/public/js/vis/img/network/deleteIcon.png View File


public/js/vis/img/network/downArrow.png → server/public/js/vis/img/network/downArrow.png View File


public/js/vis/img/network/editIcon.png → server/public/js/vis/img/network/editIcon.png View File


public/js/vis/img/network/leftArrow.png → server/public/js/vis/img/network/leftArrow.png View File


public/js/vis/img/network/minus.png → server/public/js/vis/img/network/minus.png View File


public/js/vis/img/network/plus.png → server/public/js/vis/img/network/plus.png View File


public/js/vis/img/network/rightArrow.png → server/public/js/vis/img/network/rightArrow.png View File


public/js/vis/img/network/upArrow.png → server/public/js/vis/img/network/upArrow.png View File


public/js/vis/img/network/zoomExtends.png → server/public/js/vis/img/network/zoomExtends.png View File


public/js/vis/vis-graph3d.min.js → server/public/js/vis/vis-graph3d.min.js View File


public/js/vis/vis-network.min.css → server/public/js/vis/vis-network.min.css View File


public/js/vis/vis-network.min.js → server/public/js/vis/vis-network.min.js View File


public/js/vis/vis-timeline-graph2d.min.css → server/public/js/vis/vis-timeline-graph2d.min.css View File


public/js/vis/vis-timeline-graph2d.min.js → server/public/js/vis/vis-timeline-graph2d.min.js View File


public/js/vis/vis.css → server/public/js/vis/vis.css View File


public/js/vis/vis.js → server/public/js/vis/vis.js View File


public/js/vis/vis.js.map → server/public/js/vis/vis.js.map View File


public/js/vis/vis.map → server/public/js/vis/vis.map View File


public/js/vis/vis.min.css → server/public/js/vis/vis.min.css View File


public/js/vis/vis.min.js → server/public/js/vis/vis.min.js View File


public/logo.svg → server/public/logo.svg View File


public/style.css → server/public/style.css View File


+ 2
- 0
server/routes/api/index.js View File

@ -0,0 +1,2 @@
exports.V1 = require('./v1');
exports.V2 = require('./v2');

routes/api.js → server/routes/api/v1.js View File

@ -16,13 +16,18 @@ const API_PAGINATION = "&per_page=" + API_PAGINATION_SIZE;
const REPOS_PATH = "/repos"; const REPOS_PATH = "/repos";
const got_options = {
json: true,
username : process.env.CLIENT_ID,
password : process.env.CLIENT_SECRET
};
/** /**
* Queries data from the github APi server and returns it as * Queries data from the github APi server and returns it as
* a json object in a promise. * a json object in a promise.
*
*
* This makes no attempt to cache * This makes no attempt to cache
*
*
* @param {*} requestURL endpoint on githubapi: ex: /users/jrtechs/following * @param {*} requestURL endpoint on githubapi: ex: /users/jrtechs/following
*/ */
function queryGithubAPIRaw(requestURL) function queryGithubAPIRaw(requestURL)
@ -40,7 +45,7 @@ function queryGithubAPIRaw(requestURL)
} }
console.log(queryURL); console.log(queryURL);
got(queryURL, { json: true }).then(response =>
got(queryURL, got_options).then(response =>
{ {
resolve(response.body); resolve(response.body);
cache.put(requestURL, response.body); cache.put(requestURL, response.body);
@ -56,8 +61,8 @@ function queryGithubAPIRaw(requestURL)
/** /**
* Queries data from the github api server * Queries data from the github api server
* and caches the results locally. * and caches the results locally.
*
* @param {*} requestURL
*
* @param {*} requestURL
*/ */
function queryGitHubAPI(requestURL) function queryGitHubAPI(requestURL)
{ {
@ -87,7 +92,7 @@ function queryGitHubAPI(requestURL)
/** /**
* Fetches all content from a particular github api endpoint * Fetches all content from a particular github api endpoint
* using their pagination schema. * using their pagination schema.
*
*
* @param {*} username username of github client * @param {*} username username of github client
* @param {*} apiPath following or followers * @param {*} apiPath following or followers
* @param {*} page current pagination page * @param {*} page current pagination page
@ -137,9 +142,9 @@ function fetchAllWithPagination(apiPath, page, lst)
/** /**
* Makes a copy of a JS object with certain properties * Makes a copy of a JS object with certain properties
*
* @param {*} props
* @param {*} obj
*
* @param {*} props
* @param {*} obj
*/ */
function copyWithProperties(props, obj) function copyWithProperties(props, obj)
{ {
@ -155,10 +160,10 @@ function copyWithProperties(props, obj)
/** /**
* Combines the list of friends and followers ignoring duplicates * Combines the list of friends and followers ignoring duplicates
* that are already in the list. (person is both following and followed by someone) * that are already in the list. (person is both following and followed by someone)
*
*
* This also removes any unused properties like events_url and organizations_url * This also removes any unused properties like events_url and organizations_url
*
* @param {*} followingAndFollowers
*
* @param {*} followingAndFollowers
*/ */
function minimizeFriends(people) function minimizeFriends(people)
{ {
@ -172,7 +177,7 @@ function minimizeFriends(people)
{ {
ids.add(people[i].id); ids.add(people[i].id);
friendLst.push({ friendLst.push({
login: people[i].login,
login: people[i].login,
avatar_url: people[i].avatar_url avatar_url: people[i].avatar_url
}); });
} }
@ -185,8 +190,8 @@ function minimizeFriends(people)
* Fetches all the people that are either following or is followed * Fetches all the people that are either following or is followed
* by a person on github. This will cache the results to make simultaneous * by a person on github. This will cache the results to make simultaneous
* connections easier and less demanding on the github API. * connections easier and less demanding on the github API.
*
* @param {*} user
*
* @param {*} user
*/ */
function queryFriends(user) function queryFriends(user)
{ {
@ -204,7 +209,7 @@ function queryFriends(user)
cache.put("/friends/" + user, fList); cache.put("/friends/" + user, fList);
}).catch((err)=> }).catch((err)=>
{ {
console.log(err);
console.log(err);
reject("API ERROR"); reject("API ERROR");
}) })
}).catch((error)=> }).catch((error)=>
@ -223,13 +228,13 @@ function queryFriends(user)
/** /**
*
*
* Fetches all of the members of an organization from the * Fetches all of the members of an organization from the
* API or cache * API or cache
* *
* /orgs/RITlug/members?page=1 * /orgs/RITlug/members?page=1
* *
* @param {*} orgName
* @param {*} orgName
*/ */
function getOrganizationMembers(orgName) function getOrganizationMembers(orgName)
{ {
@ -259,8 +264,8 @@ function getOrganizationMembers(orgName)
/** /**
* Minimizes the JSON for a list of repositories * Minimizes the JSON for a list of repositories
*
* @param {*} repositories
*
* @param {*} repositories
*/ */
function minimizeRepositories(repositories) function minimizeRepositories(repositories)
{ {
@ -268,7 +273,7 @@ function minimizeRepositories(repositories)
for(var i = 0; i < repositories.length; i++) for(var i = 0; i < repositories.length; i++)
{ {
rList.push(copyWithProperties(["name", "created_at", "homepage",
rList.push(copyWithProperties(["name", "created_at", "homepage",
"description", "language", "forks", "watchers", "description", "language", "forks", "watchers",
"open_issues_count", "license", "html_url"], "open_issues_count", "license", "html_url"],
repositories[i])); repositories[i]));
@ -279,7 +284,7 @@ function minimizeRepositories(repositories)
/** /**
* Fetches all repositories from the API * Fetches all repositories from the API
*
*
* @param {*} user name of org/user * @param {*} user name of org/user
* @param {*} orgsOrUsers either /users/ or /orgs/ * @param {*} orgsOrUsers either /users/ or /orgs/
*/ */
@ -405,7 +410,7 @@ routes.get('/*', (request, result) =>
} }
} }
catch(error)
catch(error)
{ {
result.write("[]"); result.write("[]");
}; };
@ -418,4 +423,4 @@ routes.get('/*', (request, result) =>
} }
}); });
module.exports = routes;
module.exports = routes;

+ 310
- 0
server/routes/api/v2.js View File

@ -0,0 +1,310 @@
const routes = require('express').Router();
const got = require("got");
const cache = require('memory-cache');
const dotenv = require("dotenv").config();
const GITHUB_API = "https://api.github.com";
const authenticate = `client_id=${process.env.CLIENT_ID}&client_secret=${process.env.CLIENT_SECRET}`;
const API_FOLLOWING = "/following";
const API_FOLLOWERS = "/followers";
const API_USER_PATH = "/users/";
const API_ORGS_PATH = "/orgs/";
const API_PAGINATION_SIZE = 100; // 100 is the max, 30 is the default
// if this is too large, it would be infeasible to make graphs for people following popular people
const API_MAX_PAGES = 2;
const API_PAGINATION = "&per_page=" + API_PAGINATION_SIZE;
const REPOS_PATH = "/repos";
/**
* Queries data from the github APi server and returns it as
* a json object in a promise.
*
* This makes no attempt to cache
*
* @param {*} requestURL endpoint on githubapi: ex: /users/jrtechs/following
*/
const queryGithubAPIRaw = async requestURL => {
let queryURL = requestURL.includes("?page=") ? `${GITHUB_API}${requestURL}&${authenticate}` :`${GITHUB_API}${requestURL}?${authenticate}`;
console.log(queryURL);
try {
const req = await got(queryURL, { json: true });
cache.put(requestURL, req);
return req;
} catch (error) {
console.log(error);
cache.put(requestURL, `${error.statusCode} - ${error.statusMessage}`);
}
}
/**
* Queries data from the github api server
* and caches the results locally.
*
* @param {*} requestURL
*/
const queryGitHubAPI = async requestURL => {
const apiData = cache.get(requestURL);
if (apiData) {
console.log("Fetched From Cache");
return apiData
}
try {
return await queryGithubAPIRaw(requestURL);
} catch (error) {
console.log(error);
}
}
/**
* Fetches all content from a particular github api endpoint
* using their pagination schema.
*
* @param {*} username username of github client
* @param {*} apiPath following or followers
* @param {*} page current pagination page
* @param {*} lst list we are building on
*/
const fetchAllWithPagination = async (apiPath, page, lst) => {
try {
const req = await queryGithubAPIRaw(`${apiPath}?page=${page}${API_PAGINATION}`);
if (req.body.hasOwnProperty("length")) {
const list = lst.concat(req.body);
if(page < API_MAX_PAGES && req.length === API_PAGINATION_SIZE) {
const redo = await fetchAllWithPagination(apiPath, page + 1, list);
return redo;
}
return list;
}
} catch (error) {
console.log("Error with api request");
}
}
/**
* Makes a copy of a JS object with certain properties
*
* @param {*} props
* @param {*} obj
*/
const copyWithProperties = (props, obj) => {
let newO = new Object();
props.forEach(prop => {
newO[prop] = obj[prop];
})
return newO;
}
/**
* Combines the list of friends and followers ignoring duplicates
* that are already in the list. (person is both following and followed by someone)
*
* This also removes any unused properties like events_url and organizations_url
*
* @param {*} followingAndFollowers
*/
const minimizeFriends = people => {
let friendLst = [];
let ids = new Set();
people.forEach(person => {
if(!ids.has(person.id)) {
ids.add(person.id);
friendLst.push({
login: person.login,
avatar_url: person.avatar_url
});
}
});
return friendLst;
}
/**
* Fetches all the people that are either following or is followed
* by a person on github. This will cache the results to make simultaneous
* connections easier and less demanding on the github API.
*
* @param {*} user
*/
const queryFriends = async user => {
const cacheHit = cache.get("/friends/" + user);
if (cacheHit){
console.log("Friends cache hit");
return cacheHit;
}
try {
const followers = await fetchAllWithPagination(API_USER_PATH + user + API_FOLLOWERS, 1, []);
const following = await fetchAllWithPagination(API_USER_PATH + user + API_FOLLOWING, 1, []);
const fList = minimizeFriends(following.concat(followers));
cache.put(`/friends/${user}`, fList);
return fList;
} catch (error) {
console.log("API Error", err);
}
}
/**
*
* Fetches all of the members of an organization from the
* API or cache
*
* /orgs/RITlug/members?page=1
*
* @param {*} orgName
*/
const getOrganizationMembers = async orgName => {
const cacheHit = cache.get("/org/users/" + orgName);
if (cacheHit){
console.log("Org members cache hit");
return cacheHit;
}
try {
const members = await fetchAllWithPagination(API_ORGS_PATH + orgName + "/members", 1, []);
const membersMin = minimizeFriends(members);
cache.put("/org/users/" + orgName, membersMin);
return membersMin;
} catch (error) {
console.log(error);
}
}
/**
* Minimizes the JSON for a list of repositories
*
* @param {*} repositories
*/
const minimizeRepositories = repositories => {
let rList = [];
repositories.forEach(repo => {
rList.push(copyWithProperties(["name", "created_at", "homepage",
"description", "language", "forks", "watchers",
"open_issues_count", "license", "html_url"],
repo));
})
return rList;
}
/**
* Fetches all repositories from the API
*
* @param {*} user name of org/user
* @param {*} orgsOrUsers either /users/ or /orgs/
*/
const queryRepositories = async (user, orgsOrUsers) => {
const cacheHit = cache.get(user + REPOS_PATH);
if (cacheHit) {
console.log("Repositories cache hit");
return cacheHit;
}
try {
const repos = await fetchAllWithPagination(orgsOrUsers + user + REPOS_PATH, 1, []);
const minRepos = minimizeRepositories(repos);
cache.put(`${user}${REPOS_PATH}`, minRepos);
return minRepos;
} catch (error) {
console.log(error)
console.log("bad things went down");
}
}
/**
* /users/name/following/followers
*/
routes.get("/friends/:name", async (req, res)=> {
try {
const query = await queryFriends(req.params.name);
res.json(query);
} catch (error) {
res.status(500).json({error: 'API error fetching friends'});
}
});
routes.get("/org/users/:name", async (request, res) => {
try {
const orgMembers = await getOrganizationMembers(request.params.name);
res.json(orgMembers).end();
} catch (error) {
res.status(500).json({error: 'API error fetching friends'}).end();
}
});
routes.get("/repositories/:name", async (req, res) => {
try {
const repos = await queryRepositories(req.params.name, API_USER_PATH);
res.json(repos).end();
} catch (error) {
res.status(500).json({error: 'API error fetching friends'}).end();
}
});
routes.get("/org/repositories/:name", async (req, res) => {
try {
const repos = await queryRepositories(req.params.name, API_ORGS_PATH);
res.json(repos).end();
} catch (error) {
res.status(500).json({error: 'API error fetching friends'}).end();
}
});
routes.get('/*', (request, result) =>
{
var gitHubAPIURL = request.url;
result.setHeader('Content-Type', 'application/json');
queryGitHubAPI(gitHubAPIURL).then((data)=>
{
if(data.hasOwnProperty("id") || data[0].hasOwnProperty("id"))
{
result.write(JSON.stringify(data));
}
else
{
result.write("[]");
}
result.end();
}).catch((error)=>
{
try
{
if(error.hasOwnProperty("id") || error[0].hasOwnProperty("id"))
{
result.write(JSON.stringify(error));
}
else
{
result.write("[]");
}
}
catch(error)
{
result.write("[]");
};
result.end();
});
if(cache.size() > 50000)
{
cache.clear();
}
});
module.exports = routes;

routes/index.js → server/routes/index.js View File

@ -1,6 +1,6 @@
const routes = require('express').Router(); const routes = require('express').Router();
const api = require('./api');
routes.use('/api', api);
const apiV1 = require('./api/v1');
routes.use('/api', apiV1);
routes.get("/", (request, response) => { routes.get("/", (request, response) => {
response.redirect("index.html"); response.redirect("index.html");

+ 11
- 0
server/routes/test/v2.js View File

@ -0,0 +1,11 @@
const assert = require('assert');
const V2 = require('../api/v2');
describe('github api v2', function() {
it('successfully queries friends', async function() {
var queryFriends = V2.queryFriends;
// it was this point that Eric realized he doesn't understand
// module.exports very well.
assert.strictEqual(typeof(queryFriends), typeof(queryFriends));
});
});

run.sh → server/run.sh View File


server.js → server/server.js View File

@ -1,10 +1,14 @@
const crypto = require('crypto');
const app = express();
const dotenv = require("dotenv").config();
const express = require("express"); const express = require("express");
const session = require('express-session'); const session = require('express-session');
const dotenv = require("dotenv").config();
const app = express();
const sessionProperties = { const sessionProperties = {
secret: process.env.SESSION_SECRET,
secret: process.env.SESSION_SECRET || crypto.randomBytes(64),
cookie: { maxAge: 6000000 }, cookie: { maxAge: 6000000 },
resave: false, resave: false,
saveUninitialized: false saveUninitialized: false
@ -18,4 +22,4 @@ const routes = require('./routes');
app.use('/', routes); app.use('/', routes);
app.listen(process.env.PORT, () => console.log(`App listening on port ${process.env.PORT}!`));
app.listen(process.env.PORT || 8100, () => console.log(`App listening on port ${process.env.PORT || 8100}!`));

Loading…
Cancel
Save