Skip to content

imjohnbo/search-api #245

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

Merged
merged 2 commits into from
Mar 20, 2020
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
11 changes: 11 additions & 0 deletions api/javascript/search/.env
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
GLITCH_DEBUGGER=true
# Environment Config

# reference these in your code with process.env.SECRET

GH_APP_ID=
GH_CLIENT_ID=
GH_CLIENT_SECRET=
INSTALLATION_ID=

# note: .env is a shell file so there can't be spaces around =
44 changes: 44 additions & 0 deletions api/javascript/search/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
GitHub Search API demo
=================

This project employs several authentication strategies to avoid rate limiting while using the GitHub Search API:
1. Using each user's OAuth access token, if available -- this will allow you a maximum of [30 requests per-user / per-minute](https://developer.github.com/v3/search/#rate-limit)
2. Falling back to a server-to-server token, associated with a given installation of your GitHub App -- this will allow you a maximum of [30 requests per-organization / per-minute](https://developer.github.com/v3/search/#rate-limit)
3. Falling back again to simplified functionality, such as validating a given GitHub username, via GET /users/:username -- this will allow you a minimum of [5000 requests per-organization / per-hour](https://developer.github.com/apps/building-github-apps/understanding-rate-limits-for-github-apps/)

Step 1a: Prereqs via [Glitch](https://glitch.com/~github-search-api)
-----------

* Remix this app :)

Step 1b: Prereqs locally
-----------
* Install `node` from [the website](https://nodejs.org/en/) or [Homebrew](https://brew.sh/)
* `git clone` the project
* Navigate to the project directory and install dependencies using `npm i`

Step 2: App creation and variable-setting
-----------
* Create a new [GitHub App](https://developer.github.com/apps/building-github-apps/creating-a-github-app/).
* Homepage URL = `<Your Glitch App URL>`
* User authorization callback URL = `<Your Glitch App URL>/authorized`
* Webhook URL (unused) = `<Your Glitch App URL>/hooks`
* Download your private key at the bottom of the app settings page.
* Make a new file in Glitch called `.data/pem` and paste the contents of the private key.
* Set the following variables in your Glitch `.env` file:
* `GH_CLIENT_ID` Client ID on app settings page
* `GH_CLIENT_SECRET` Client secret on app settings page
* `GH_APP_ID` App ID on app settings page
* `INSTALLATION_ID` Installation ID, which you can retrieve from [here](https://developer.github.com/v3/apps/installations/#installations)

Step 3a: Running via Glitch
-----------
* Navigate to your URL for live-reloaded goodness

Step 3b: Running locally
-----------
* `npm start`

FYI
-----------
* This app is single-user (for now). It stores the OAuth token in a file found at `.data/oauth`.
28 changes: 28 additions & 0 deletions api/javascript/search/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
{
"name": "github-search-api",
"version": "0.0.1",
"description": "Demo of the GitHub Search API, using several authentication strategies to avoid rate limits.",
"main": "server.js",
"scripts": {
"start": "node server.js"
},
"dependencies": {
"express": "^4.16.4",
"node-fetch": "^2.5.0",
"node-localstorage": "^1.3.1",
"@octokit/app": "^1.1.0",
"@octokit/request": "^2.2.0"
},
"engines": {
"node": "8.x"
},
"repository": {
"url": "https://github-search-api.glitch.me/"
},
"license": "MIT",
"keywords": [
"node",
"glitch",
"express"
]
}
109 changes: 109 additions & 0 deletions api/javascript/search/public/client.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
const searchInput = document.querySelector('.search-input');
const searchResults = document.querySelector('.search-results');
const searchError = document.querySelector('.search-error');
const searchButton = document.querySelector('.search');
const login = document.querySelector('.login');
const loginButton = document.querySelector('.login-button');
const loginText = document.querySelector('.login-text');
const authType = document.querySelector('.auth-type');
const authTarget = document.querySelector('.auth-target');
const hitsRemaining = document.querySelector('.hits-remaining');
const hitsTotal = document.querySelector('.hits-total');
const scheme = document.querySelector('.scheme');

let localState = {};

// TODO change from javascript handler to <form>
loginButton && loginButton.addEventListener('click', (evt) => {

window.location = `https://github.com/login/oauth/authorize?scope=repo&client_id=${localState.clientId}&state=${localState.oAuthState}`;
console.log(localState.clientId);
console.log(localState.oAuthState);
});

searchInput && searchInput.addEventListener('input', (evt) => {
const val = evt.target.value;
if (!val) {
searchResults.innerHTML = '';
searchError.hidden = true;
}
});

searchButton && searchButton.addEventListener('click', (user) => {
if (searchInput.value === '') return;
searchResults.innerHTML = '';
searchError.hidden = true;
search()
.then(data => data.json())
.then(showResults)
.then(syncState)
.catch(err => {
searchError.innerHTML = 'Error encountered while searching.'
searchError.hidden = false;
});
});

function search() {
return fetch(`/search/${searchInput.value}`, {
headers: {
"Content-Type": "application/json",
}
});
};

function showResults(results) {
// just one result from User API
if (!results.items && !results.items.length) {
if (results.login) {
searchResults.innerHTML = `This user <a href="${results.html_url}">was found</a> on GitHub`;
}
else {
searchResults.innerHTML = 'This user could not be found on GitHub.';
}
}
// array of results from Search API
else if (results.items.length) {
results.items.forEach(createRow);
}
}

function createRow(result) {
let node = document.createElement('li');
let text = document.createTextNode(result.login)
node.appendChild(text);
searchResults.appendChild(node);
}

function updateUI() {
authType.innerHTML = localState.authType;
authTarget.innerHTML = localState.authTarget;
hitsRemaining.innerHTML = `(${localState.rateLimitRemaining} /`;
hitsTotal.innerHTML = ` ${localState.rateLimitTotal})`;

if (localState.oAuthToken) {
loginText.innerHTML = 'Logged in.';
loginButton.disabled = true;
}

if (localState.rateLimitRemaining) {
scheme.hidden = false;
}
}

function syncState() {
fetch(`/state`)
.then(data => data.json())
.then(remoteState => {
localState = remoteState;
updateUI();
});
}

// this executes immediately
(() => {
// await this.getRateLimits(this.getQueryAuthToken());
scheme.hidden = true;
syncState();
})();


86 changes: 86 additions & 0 deletions api/javascript/search/public/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
<!DOCTYPE html>
<html lang="en">
<head>
<title>GitHub Search API</title>
<link id="favicon" rel="icon" href="https://glitch.com/edit/favicon-app.ico" type="image/x-icon">
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">

<style>
html, body {
height: 100%;}
body {
display: flex;
flex-direction: column;
}
.content {
flex: 1 0 auto;
}
.footer {
flex-shrink: 0;
background: #eee;
}
.search-error {
color: red;
}

</style>

<link href="https://unpkg.com/primer/build/build.css" rel="stylesheet">

</head>
<body>

<div class="content container-lg p-6">

<h2>
Try searching for a GitHub user:
</h2>

<br>

<input class="form-control search-input" type="text" placeholder="username"/>
<button class="btn btn-primary search" type="button">
Search
</button>

<br>

<p class="search-error"></p>

<br>

<div class="login">
<p class="login-text">
For more API queries, try logging in to GitHub?
</p>
<button class="btn login-button">
Login
</button>
</div>

<br>

<ul class="search-results pt-3 pl-3"></ul>

</div>

<footer class="footer pb-2 pt-3 text-center">
<div class="scheme">
<p>
You are using <strong><span class="auth-type"></span> authentication</strong> against the <strong><span class="auth-target"></span> API</strong>.
</p>
<p>
<span class="hits-remaining"></span><span class="hits-total"></span>
</p>
</div>
</footer>

<script src="/client.js"></script>
<!-- include the Glitch button to show what the webpage is about and
to make it easier for folks to view source and remix -->
<div class="glitchButton" style="position:fixed;top:20px;right:20px;"></div>
<script src="https://button.glitch.me/button.js"></script>
</body>
</html>
Loading