Skip to content
Open
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
2 changes: 1 addition & 1 deletion completed/recipes/api.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ const pool = new pg.Pool({
user: "postgres",
host: "localhost",
database: "recipeguru",
password: "lol",
password: "ryan",
port: 5432,
});

Expand Down
13 changes: 12 additions & 1 deletion index.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,19 @@ const path = require("path");
const completed = require("./completed");
const recipes = require("./recipes/api");
const ingredients = require("./ingredients/api");
const movies = require("./paginationPractice")
const rateLimit = require('express-rate-limit')

const limiter = rateLimit({
windowMs: 15 * 60 * 1000, // 15 minutes
limit: 5, // Limit each IP to 100 requests per `window` (here, per 15 minutes)
message: "Too many requests from this IP, please try again after 15 minutes",
standardHeaders: true, // Return rate limit info in the `RateLimit-*` headers
legacyHeaders: false, // Disable the `X-RateLimit-*` headers
})

const app = express();

const app = express();
app.get("/", (_, res) => res.sendFile(path.join(__dirname, "./index.html")));
app.get("/style.css", (_, res) =>
res.sendFile(path.join(__dirname, "./style.css"))
Expand All @@ -19,6 +29,7 @@ app.use("/images", express.static("./images"));
app.use("/completed", completed);
app.use("/recipes", recipes);
app.use("/ingredients", ingredients);
app.use("/movies", limiter, movies);

app.get("/hello", (req, res) => res.json({ status: "ok" }));

Expand Down
32 changes: 28 additions & 4 deletions ingredients/api.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
const path = require("path");
const express = require("express");
const router = express.Router();

const pg = require("pg");
// client side static assets
router.get("/", (_, res) => res.sendFile(path.join(__dirname, "./index.html")));
router.get("/client.js", (_, res) =>
Expand All @@ -14,13 +14,28 @@ router.get("/client.js", (_, res) =>

// connect to postgres

const pool = new pg.Pool({
user: "postgres",
host: "localhost",
password: "ryan",
database: "recipeguru",
port: 5432
})

router.get("/type", async (req, res) => {
const { type } = req.query;
console.log("get ingredients", type);

// return all ingredients of a type

res.status(501).json({ status: "not implemented", rows: [] });
const query = 'SELECT * FROM ingredients where type=$1';
const values = [type];
const result = await pool.query(query, values);
if ( result ) {
res.status(200).json({ status: "success", rows: result.rows });
}
else {
res.status(500).json({ status: "error", rows: [] });
}
});

router.get("/search", async (req, res) => {
Expand All @@ -29,9 +44,18 @@ router.get("/search", async (req, res) => {
console.log("search ingredients", term, page);

// return all columns as well as the count of all rows as total_count
const query = 'SELECT *, COUNT(*) OVER() AS total_count from ingredients where title ILIKE $1 OFFSET $2 LIMIT 5';

const values = [`%${term}%`, page * 5];
// make sure to account for pagination and only return 5 rows at a time
const result = await pool.query(query, values);

res.status(501).json({ status: "not implemented", rows: [] });
if ( result ) {
res.status(200).json({ status: "success", rows: result.rows });
}
else {
res.status(500).json({ status: "error", rows: [] });
}
});

/**
Expand Down
21 changes: 21 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
"license": "Apache-2.0",
"dependencies": {
"express": "^4.18.1",
"express-rate-limit": "^7.5.0",
"image-downloader": "^4.3.0",
"pg": "^8.7.3"
},
Expand Down
59 changes: 59 additions & 0 deletions paginationPractice.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
const pg = require("pg");
const express = require("express");
const router = express.Router();
const pool = new pg.Pool({
user: "postgres",
host: "localhost",
password: "lol",
database: "omdb",
port: 5432
})

router.get("/", async (req, res) => {
//Implementing seek based pagination
const limit = 10;
const cursor = req.query.cursor;
let query = `SELECT * FROM movies ORDER BY id ASC LIMIT ${limit + 1}`;

if (cursor) {
query = `SELECT * FROM movies WHERE id > $1 ORDER BY id ASC LIMIT ${limit + 1}`;
}

try {
const result = cursor ? await pool.query(query, [cursor]) : await pool.query(query);
const movies = result.rows;
let nextCursor = null;
if (movies.length > limit) {
nextCursor = movies.pop().id;
}
// Implementing Hateoas rules
const baseUrl = `http://${req.headers.host}${req.baseUrl}`;
console.log(baseUrl);
if (cursor) {
const prevCursor = cursor - limit;
res.json({
movies,
links: {
self: `${baseUrl}?cursor=${cursor}`,
next: `${baseUrl}?cursor=${nextCursor}`,
prev: `${baseUrl}?cursor=${prevCursor}`
}
});
} else {
res.json({
movies,
links: {
self: `${baseUrl}`,
next: `${baseUrl}?cursor=${nextCursor}`
}
});
}

}
catch (err) {
console.log(err);
res.status(500).json({ error: "Internal server error" });
}
});

module.exports = router;
26 changes: 26 additions & 0 deletions practice.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
SELECT
i.title as ingredient_title,
i.image as ingredient_image,
r.title as recipe_title,
r.body as recipe_body
from recipe_ingredients ri

inner join ingredients i
on i.id = ri.ingredient_id

inner join recipes r
on r.recipe_id = ri.recipe_id


SELECT
m.name,
ARRAY(SELECT ecn.name from english_category_names ecn
INNER JOIN
movie_keywords mk,
on
mk.category_id = ecn.category_id
WHERE
mk.movie_id = m.id
LIMIT 5) as keywords
FROM movies m
where name like '%star wars%'
32 changes: 30 additions & 2 deletions recipes/api.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
const path = require("path");
const express = require("express");
const router = express.Router();
const pg = require("pg");

// client side static assets
router.get("/", (_, res) => res.sendFile(path.join(__dirname, "./index.html")));
Expand All @@ -22,15 +23,29 @@ router.get("/detail", (_, res) =>
*/

// connect to postgres
const pool = new pg.Pool({
user: "postgres",
host: "localhost",
password: "ryan",
database: "recipeguru",
port: 5432
})

router.get("/search", async function (req, res) {
console.log("search recipes");

// return recipe_id, title, and the first photo as url
//
// for recipes without photos, return url as default.jpg
const query = "SELECT DISTINCT ON (recipes.recipe_id) recipes.recipe_id, title, coalesce(recipes_photos.url, 'default.jpg') from recipes LEFT JOIN recipes_photos ON recipes.recipe_id = recipes_photos.recipe_id";
const result = await pool.query(query);

res.status(501).json({ status: "not implemented", rows: [] });
if (result) {
res.status(200).json({ status: "success", rows: result.rows });
}
else {
res.status(500).json({ status: "error", rows: [] });
}
});

router.get("/get", async (req, res) => {
Expand All @@ -42,16 +57,29 @@ router.get("/get", async (req, res) => {
// name the ingredient type `ingredient_type`
// name the ingredient title `ingredient_title`
//
const ingredientPromise = pool.query(`SELECT i.title AS ingredient_title, i.image as ingredient_image, i.type as ingredient_type FROM recipe_ingredients ri INNER JOIN ingredients i on ri.ingredient_id = i.id WHERE ri.recipe_id = $1`, [recipeId]);
//
// return all photo rows as photos
// return the title, body, and url (named the same)
//
//
const photoPromise = pool.query(`SELECT title, body, COALESCE(i.url, 'default.jpg') as url from recipes r LEFT JOIN recipes_photos i on r.recipe_id = i.recipe_id WHERE r.recipe_id = $1`, [recipeId]);
// return the title as title
// return the body as body
// if no row[0] has no photo, return it as default.jpg

res.status(501).json({ status: "not implemented" });
const [{ rows: photosRows }, { rows: ingredientsRows }] = await Promise.all([
photoPromise,
ingredientPromise,
]);
console.log(photosRows);

res.json({
ingredients: ingredientsRows,
photos: photosRows.map((photo) => photo.url),
title: photosRows[0].title,
body: photosRows[0].body,
});
});
/**
* Student code ends here
Expand Down