Skip to content

Commit

Permalink
Merging codebase
Browse files Browse the repository at this point in the history
  • Loading branch information
samryoo committed Jun 5, 2019
1 parent 6092d03 commit f9f7a28
Show file tree
Hide file tree
Showing 42 changed files with 378 additions and 16 deletions.
17 changes: 1 addition & 16 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,16 +1 @@
.DS_Store
.env
node_modules/
/dist/
npm-debug.log*
yarn-debug.log*
yarn-error.log*
/test/unit/coverage/

# Editor directories and files
.idea
.vscode
*.suo
*.ntvs*
*.njsproj
*.sln
node_modules
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
16 changes: 16 additions & 0 deletions demo/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
.DS_Store
.env
node_modules/
/dist/
npm-debug.log*
yarn-debug.log*
yarn-error.log*
/test/unit/coverage/

# Editor directories and files
.idea
.vscode
*.suo
*.ntvs*
*.njsproj
*.sln
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
62 changes: 62 additions & 0 deletions demo/server/server.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
const express = require("express");
const request = require("request");
const bodyParser = require("body-parser");
const cors = require("cors");
require("dotenv").config();
const app = express();
const Stache = require("../stache.js");

const API_URL = "https://api.yelp.com/v3/graphql";
const API_KEY = process.env.ACCESS_TOKEN;

const config = {
cacheExpiration: 120, // seconds
uniqueVariables: {
term: String,
location: Number,
radius: Number,
},
queryObject: "search",
queryTypename: "Businesses",
flexArg: "limit",
offsetArg: "offset",
};
const stache = new Stache(config);

app.use(bodyParser.json());

app.use(
cors({
origin: "http://localhost:8080",
optionsSuccessStatus: 200,
})
);

app.post(
"/api",
stache.check,
(req, res, next) => {
if (res.locals.httpRequest) {
request.post(
{
url: API_URL,
method: "POST",
headers: {
Authorization: "Bearer " + API_KEY,
},
json: true,
body: req.body,
},
(err, response, body) => {
res.locals.body = body;
return next();
}
);
} else {
return next();
}
},
stache.it
);

app.listen(3020);
File renamed without changes.
File renamed without changes
File renamed without changes
File renamed without changes
File renamed without changes
File renamed without changes
File renamed without changes
File renamed without changes
File renamed without changes.
109 changes: 109 additions & 0 deletions demo/src/components/Search.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
<template>
<div class="search-page">
<search-bar></search-bar>
<h1>Load time: <b>{{search.total}} millisecond{{search.total === 1 ? '' : 's'}}</b></h1>
<div class="loading" v-if="isLoading">
<img src="../assets/timer2.gif"> Loading...
</div>
<ul v-if="search.business">
<li v-for="business in search.business">
<br><b>{{business.name}}</b> {{business.rating}} ☆</br>
<br>{{business.location.formatted_address}}</br>
<br>{{business.reviews[0].user.name}} says: <i>"{{business.reviews[0].text}}"</i> </br>
<br></br>
</li>
</ul>
</div>
</template>

<script>
import SearchBar from "./SearchBar.vue";
import gql from "graphql-tag";
import { _ } from "vue-underscore";
import Router from "vue-router";
export default {
name: "SearchPage",
components: {
SearchBar
},
apollo: {
$loadingKey: "isLoading",
search: {
query() {
if (this.term && this.location && this.radius && this.limit) {
return gql`
query searchYelp(
$term: String!
$location: String!
$radius: Float!
$limit: Int!
$offset: Int!
) {
search(
term: $term
location: $location
radius: $radius
limit: $limit
sort_by: "rating"
offset: $offset
) {
total
business {
name
rating
price
photos
id
location{
formatted_address
city
state
}
reviews{
text
user{
name
}
id
rating
time_created
url
}
}
}
}
`;
}
},
variables() {
return {
term: this.term,
location: this.location,
radius: this.radius * 1609.34,
limit: +this.limit
};
},
}
},
props: ["term", "location", "radius", "limit"],
data() {
return {
search: {},
isLoading: 0,
};
},
};
</script>

<!-- "scoped" attribute limits CSS to this component only -->
<style scoped>
h1,
h2 {
font-weight: normal;
}

ul {
list-style: none;
}
</style>
File renamed without changes.
File renamed without changes.
File renamed without changes.
190 changes: 190 additions & 0 deletions demo/stache.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,190 @@
const Redis = require("ioredis");
require("dotenv").config();
const redis = new Redis({
host: process.env.DB_HOST,
port: process.env.DB_PORT,
password: process.env.DB_PASS
});

class Stache {
constructor(config, supersets = true) {
this.it = this.it.bind(this);
this.stage = this.stage.bind(this);
this.check = this.check.bind(this);
this.makeQueryString = this.makeQueryString.bind(this);
this.config = config;
this.supersets = supersets;
}

denormalize(object) {
const newObj = {};
for (let key in object) {
let workingObj = newObj;
let path = key.split(".");
for (let i = 1; i < path.length; i += 1) {
const e = path[i];
if (i === path.length - 1) workingObj[e] = object[key];
if (!workingObj[e]) {
if (Number(path[i + 1]) || Number(path[i + 1]) === 0) {
workingObj[e] = [];
} else workingObj[e] = {};
}
workingObj = workingObj[e];
}
}
return newObj;
}

normalize(object) {
return Object.assign(
{},
...(function flattener(objectBit, path = "") {
return [].concat(
...Object.keys(objectBit).map(key => {
return typeof objectBit[key] === "object" && objectBit[key] !== null
? flattener(objectBit[key], `${path}.${key}`)
: { [`${path}.${key}`]: objectBit[key] };
})
);
})(object)
);
}

offsetKeys(object, offset) {
let newObj = {};
for (let key in object) {
let path = key.split(".");
if (path[4]) {
path[4] = +path[4] + offset;
newObj[path.join(".")] = object[key];
}
}
return newObj;
}

makeQueryString(variables, req) {
let queryString = "";
for (let key in variables) {
let stringy = "req.body.variables.".concat(key);
queryString = queryString.concat(eval(stringy));
}
return queryString.toLowerCase();
}

check(req, res, next) {
console.log("\n");
res.locals.query = this.makeQueryString(this.config.uniqueVariables, req);
res.locals.start = Date.now(); // demo timer
redis.get(res.locals.query, (err, result) => {
if (err) {
console.log("~~ERROR~~ in redis.get: ", err); // more error handling?
} else if (result) {
let parsedResult = JSON.parse(result);
// ***EXACT MATCH***
if (
parsedResult[`.data.${this.config.queryObject}.count`] ===
req.body.variables[this.config.flexArg]
) {
parsedResult[".data.search.total"] = Date.now() - res.locals.start; // for timer
console.log(
`*** EXACT: ${
req.body.variables[this.config.flexArg]
} from cache ***`
);
console.log(
`Returned from cache: ${Date.now() - res.locals.start} ms`
);
return res.send(this.denormalize(parsedResult));
// ***SUBSET MATCH***
} else {
res.locals.subset = {};
res.locals.subset[
`.data.${this.config.queryObject}.__typename`
] = this.config.queryTypename;
let max = 0;
for (let key in parsedResult) {
let path = key.split(".");
if (path[4] && +path[4] < req.body.variables[this.config.flexArg]) {
if (+path[4] > max) max = +path[4];
res.locals.subset[key] = parsedResult[key];
}
}
if (req.body.variables[this.config.flexArg] > max + 1)
res.locals.offset = max + 1; // initializing res.locals.offset will mean that we have a superset
}
}
this.stage(req, res, next);
});
}

stage(req, res, next) {
// ***SUBSET ROUTE***
if (res.locals.subset && !res.locals.offset) {
console.log(
`*** SUBSET: get ${
req.body.variables[this.config.flexArg]
} from cache ***`
);
console.log(`Returned from cache: ${Date.now() - res.locals.start} ms`);
res.locals.subset = this.denormalize(res.locals.subset);
res.locals.subset.data.search.total = Date.now() - res.locals.start; // for timer
return res.send(res.locals.subset);
}
// ***SUPERSET ROUTE***
else if (res.locals.subset && res.locals.offset && this.supersets) {
console.log(
`*** SUPERSET: fetch ${req.body.variables[this.config.flexArg] -
res.locals.offset} add'l ***`
);
req.body.variables[this.config.offsetArg] = res.locals.offset;
req.body.variables[this.config.flexArg] =
req.body.variables[this.config.flexArg] - res.locals.offset;
res.locals.httpRequest = true;
next();
// ***NO MATCH ROUTE***
} else {
console.log(
`*** NO MATCH: fetch ${req.body.variables[this.config.flexArg]} ***`
);
req.body.variables[this.config.offsetArg] = 0; // need to initialize offset for any API request
res.locals.httpRequest = true;
next();
}
}

it(req, res) {
let normalized;
// ***SUPERSET ROUTE***
if (res.locals.subset && res.locals.offset && this.supersets) {
res.locals.superset = Object.assign(
{},
res.locals.subset,
this.offsetKeys(this.normalize(res.locals.body), res.locals.offset)
);
normalized = res.locals.superset;
normalized[`.data.${this.config.queryObject}.count`] =
req.body.variables[this.config.flexArg] +
req.body.variables[this.config.offsetArg];
res.locals.superset = this.denormalize(res.locals.superset);
res.locals.superset.data.search.total = Date.now() - res.locals.start; // demo timer
res.send(res.locals.superset);
}
// ***NO MATCH ROUTE***
else {
res.locals.body.data.search.total = Date.now() - res.locals.start; // demo timer
normalized = this.normalize(res.locals.body);
normalized[`.data.${this.config.queryObject}.count`] =
req.body.variables[this.config.flexArg];
res.send(res.locals.body);
}
console.log(`Inserted to Redis: ${Date.now() - res.locals.start} ms`);
redis.set(
res.locals.query,
JSON.stringify(normalized),
"ex",
this.config.cacheExpiration
);
}
}

module.exports = Stache;
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.

0 comments on commit f9f7a28

Please sign in to comment.