Skip to content

completed assignment #337

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

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
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
4 changes: 2 additions & 2 deletions .eslintrc.json
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
{
"env": {
"browser": true,
"commonjs": true,
"es2021": true,
"node": true
"es2021": true
},
"extends": "eslint:recommended",
"parserOptions": {
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -55,11 +55,11 @@ Before writing any code, write out all desired tables in the data model and dete

**Try to keep your design to FOUR tables**. With three tables it will be hard to meet all requirements, and more than 5 is likely overkill.

#### Project Scaffolding
#### [X] Project Scaffolding

- Put an Express application together starting with the `package.json` and a `knexfile.js`. Use existing projects as reference if needed.

#### Migrations and Seeds
#### [X] Migrations and Seeds

- Write a migration file that creates all tables necessary to model this data
- Write seed files to populate the tables with test data. **Hint**: Keep your recipes simple or this step could become extremely time consuming.
133 changes: 133 additions & 0 deletions api/recipes/model.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
const db = require('../../data/db-config');

async function getRecipeById(recipe_id) {
const recipeRows = await db('recipes as r')
.leftJoin('steps as s', 'r.recipe_id', '=', 's.recipe_id')
.leftJoin('step_ingredients as si', 'si.step_id', '=', 's.step_id')
.leftJoin('ingredients as i', 'i.ingredient_id', '=', 'si.ingredient_id')
.select(
'r.recipe_id',
'r.recipe_name',
'r.created_at',
's.step_id',
's.step_number',
's.step_instructions',
'i.ingredient_id',
'i.ingredient_name',
'si.quantity'
)
.orderBy('s.step_number')
.where('s.recipe_id', recipe_id)

//console.log(recipeRows);

const recipes = {
recipe_id: recipeRows[0].recipe_id,
recipe_name: recipeRows[0].recipe_name,
steps: recipeRows.reduce((acc,row) => {
// new step without any ingredients
if(!row.ingredient_id || row.ingredient_id == null){
return acc.concat({
step_id: row.step_id,
step_number: row.step_number,
step_instructions: row.step_instructions,
})
}
// new step with a single ingredient
if(row.ingredient_id && !acc.find(step=>step.step_id === row.step_id)){
return acc.concat({
step_id: row.step_id,
step_number: row.step_number,
step_instructions: row.step_instructions,
ingredients: [
{
ingredient_id: row.ingredient_id,
ingredient_name: row.ingredient_name,
quantity: row.quantity
}
]
})
}
// new step with multiple ingredients (repeat step, different ingredient)
const currentStep = acc.find(step=>step.step_id === row.step_id)
currentStep.ingredients.push({
ingredient_id: row.ingredient_id,
ingredient_name: row.ingredient_name,
quantity: row.quantity
})

return acc;
}, [])
}

return recipes

// let result = rows.reduce((acc, row) => {
// //console.log(row);
// if (row.step_number) {
// acc.steps.push({ step_id: row.step_id, step_number: row.step_number, step_instructions: row.step_instructions, ingredients: [] })

// // if (rows.steps.ingredients) {
// // acc.steps.ingredients.push({ ingredient_id: row.ingredient_id, ingredient_name: row.ingredient_name, quantity: row.quantity })
// // }
// }
// //console.log(acc);
// return acc;
// }, {
// recipe_id: rows[0].recipe_id,
// recipe_name: rows[0].recipe_name,
// created_at: rows[0].created_at,
// steps: []
// })

// //console.log(result);
// return result;
}

/**
* {
"recipe_id" : 1,
"recipe_name": "Spaghetti Bolognese",
"created_at": "2021-01-01 08:23:19.120",
"steps": [
{
"step_id": 11,
"step_number": 1,
"step_instructions": "Put a large saucepan on a medium heat",
"ingredients": []
},
{
"step_id": 12,
"step_number": 2,
"step_instructions": "Add 1 tbsp olive oil",
"ingredients": [
{ "ingredient_id": 27, "ingredient_name": "olive oil", "quantity": 0.014 }
]
},
]
}
*
*
*
* SELECT
r.recipe_id
,r.recipe_name
,r.created_at
,s.step_id
,s.step_number
,s.step_instructions
,si.ingredient_id
,i.ingredient_name
,si.quantity
FROM recipes r
LEFT JOIN steps s
ON r.recipe_id = s.recipe_id
INNER JOIN step_ingredients si
ON s.step_id = si.step_id
LEFT JOIN ingredients i
ON si.ingredient_id = i.ingredient_id
*/

module.exports = {
getRecipeById
}
16 changes: 16 additions & 0 deletions api/recipes/router.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
const express = require('express')
const Recipe = require('./model');

const router = express.Router()

router.get('/:recipe_id',(req,res,next)=> {
console.log('you have reached the router');
const { recipe_id } = req.params;

Recipe.getRecipeById(recipe_id)
.then(results => {
res.json(results);
}) .catch(next);
});

module.exports = router;
12 changes: 12 additions & 0 deletions api/server.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
const express = require('express');
const RecipeRouter = require('./recipes/router')

const server = express()

server.use(express.json())
server.use('/api/recipes', RecipeRouter);
server.use((err,req,res,next) => { // eslint-disable-line
res.status(err.status || 500).json({ message: err.message})
})

module.exports = server
5 changes: 5 additions & 0 deletions data/db-config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
const knex = require('knex');

const config = require('../knexfile.js');

module.exports = knex(config.development);
60 changes: 60 additions & 0 deletions data/migrations/20231224002017_first_migration.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
/**
* @param { import("knex").Knex } knex
* @returns { Promise<void> }
*/

exports.up = async function(knex) {
await knex.schema
.createTable('recipes',table=>{
table.increments('recipe_id')
table.string('recipe_name')
table.timestamp('created_at')
.defaultTo(knex.fn.now())
})
.createTable('steps',table=>{
table.increments('step_id')
table.integer('step_number').unsigned()
table.string('step_instructions')
table.integer('recipe_id')
.unsigned()
.notNullable()
.references('recipe_id')
.inTable('recipes')
.onDelete('RESTRICT')
.onUpdate('RESTRICT')
})
.createTable('ingredients',table=>{
table.increments('ingredient_id')
table.string('ingredient_name')
})
.createTable('step_ingredients',table=>{
table.increments('step_ingredient_id')
table.integer('step_id')
.unsigned()
.notNullable()
.references('step_id')
.inTable('steps')
.onDelete('RESTRICT')
.onUpdate('RESTRICT')
table.integer('ingredient_id')
.unsigned()
.notNullable()
.references('ingredient_id')
.inTable('ingredients')
.onDelete('RESTRICT')
.onUpdate('RESTRICT')
table.integer('quantity')
})
};

/**
* @param { import("knex").Knex } knex
* @returns { Promise<void> }
*/
exports.down = async function(knex) {
await knex.schema
.dropTableIfExists('step_ingredients')
.dropTableIfExists('ingredients')
.dropTableIfExists('steps')
.dropTableIfExists('recipes')
};
Binary file added data/recipes.db3
Binary file not shown.
9 changes: 9 additions & 0 deletions data/seeds/00-cleanup.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
const cleaner = require('knex-cleaner')

exports.seed = function(knex) {
// Deletes ALL existing entries
return(cleaner.clean(knex, {
mode: 'truncate', // resets ids
ignoreTables: ['knex_migrations','knex_migrations_lock']
}))
};
9 changes: 9 additions & 0 deletions data/seeds/01-recipes.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@

exports.seed = function(knex, Promise) {
// Deletes ALL existing entries
return knex('recipes').insert([
{recipe_name: 'Potatoes au Gratin'},
{recipe_name: 'Roasted Chicken'},
{recipe_name: 'Fried Onions'}
]);
};
12 changes: 12 additions & 0 deletions data/seeds/02-steps.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
exports.seed = function(knex, Promise) {
// Deletes ALL existing entries
return knex('steps').insert([
{recipe_id: 1, step_number: 1, step_instructions: 'boil potatoes'},
{recipe_id: 1, step_number: 2, step_instructions: 'cover in cheese'},
{recipe_id: 2, step_number: 1, step_instructions: 'catch chicken'},
{recipe_id: 2, step_number: 2, step_instructions: 'kill chicken'},
{recipe_id: 2, step_number: 3, step_instructions: 'cook chicken'},
{recipe_id: 3, step_number: 1, step_instructions: 'slice onions'},
{recipe_id: 3, step_number: 2, step_instructions: 'fry onions'},
]);
};
10 changes: 10 additions & 0 deletions data/seeds/03-ingredients.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
exports.seed = function(knex, Promise) {
// Deletes ALL existing entries
return knex('ingredients').insert([
{ingredient_name: 'potatoes'},
{ingredient_name: 'cheese'},
{ingredient_name: 'chicken'},
{ingredient_name: 'salt'},
{ingredient_name: 'onions'},
]);
};
12 changes: 12 additions & 0 deletions data/seeds/04-step-ingredients.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
exports.seed = function(knex, Promise) {
// Deletes ALL existing entries
return knex('step_ingredients').insert([
{step_id: 1, ingredient_id: 1, quantity: 12},
{step_id: 1, ingredient_id: 4, quantity: 2},
{step_id: 2, ingredient_id: 2, quantity: 9},
{step_id: 3, ingredient_id: 3, quantity: 1},
{step_id: 5, ingredient_id: 4, quantity: 2},
{step_id: 6, ingredient_id: 5, quantity: 6},
{step_id: 7, ingredient_id: 4, quantity: 2},
]);
};
7 changes: 7 additions & 0 deletions index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
const server = require('./api/server')

const PORT = process.env.PORT || 9000

server.listen(PORT, () => {
console.log(`listening on port ${PORT}...`)
})
23 changes: 23 additions & 0 deletions knexfile.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@

module.exports = {

development: {
client: 'sqlite3',
useNullAsDefault: true, // needed for sqlite
connection: {
filename: './data/recipes.db3'
},
migrations: {
directory: './data/migrations'
},
seeds: {
directory: './data/seeds'
},
pool: {
afterCreate: (conn, done) => {
// runs after a connection is made to the sqlite engine
conn.run('PRAGMA foreign_keys = ON', done); // turn off FK enforcement
}
}
},
};
Loading