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 README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ If you want to deploy you'll also need:
DEV_DATABASE_URL=postgres://postgres@localhost:5432/girlscode_library_app
```

You may change this to 'postgres://<user>:<password>@localhost:5432/girlscode_library_app'
You may change this to 'postgres://username:password@localhost:5432/girlscode_library_app' if using different username/password than default.

3. Create the DB with `npx sequelize db:create`
4. Run the migrations with `npx sequelize db:migrate`
Expand Down
52 changes: 52 additions & 0 deletions src/migrations/20190120141524-review.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
'use strict';

module.exports = {
up: (queryInterface, Sequelize) => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh hello 4 space tabs!

We don't have a documented convention, but the rest of the code is 2 spaces for tabs.

return queryInterface.createTable('Reviews', {
id: {
allowNull: false,
autoIncrement: true,
primaryKey: true,
type: Sequelize.INTEGER
},
BookId: {
allowNull: false,
type: Sequelize.INTEGER,
references: {
model: 'Books',
key: 'id'
}
},
UserId: {
allowNull: false,
type: Sequelize.INTEGER,
references: {
model: 'Users',
key: 'id'
}
},
Rating: {
allowNull: false,
type: Sequelize.INTEGER
},
Text: {
defaultValue: false,
allowNull: false,
type: Sequelize.STRING
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What does this translate down to in PostgreSQL? Is it a VARCHAR? Because if so, this will have a 255 character limit.

},
createdAt: {
allowNull: false,
type: Sequelize.DATE
},
updatedAt: {
allowNull: false,
type: Sequelize.DATE
}
});
},
down: (queryInterface, Sequelize) => {
(queryInterface, Sequelize) => {
return queryInterface.dropTable('Reviews');
}
}
};
17 changes: 17 additions & 0 deletions src/models/review.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
'use strict';
module.exports = (sequelize, DataTypes) => {
const Review = sequelize.define('Review', {
BookId: DataTypes.INTEGER,
UserId: DataTypes.INTEGER,
Rating: DataTypes.INTEGER,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should validate that this is between the 2 numbers we accept. (1 to 5?)

Text: DataTypes.STRING
}, {});
Review.associate = function({Book, User}) {
Review.belongsTo(Book);
Review.belongsTo(User);

};


return Review;
};
4 changes: 3 additions & 1 deletion src/models/user.js
Original file line number Diff line number Diff line change
Expand Up @@ -64,8 +64,10 @@ module.exports = (sequelize, DataTypes) => {
beforeValidate: hashPassword
}
});
User.associate = function({Loan}) {
User.associate = function({Loan, Review}) {
User.hasMany(Loan);
User.hasMany(Review);
};

return User;
};
17 changes: 12 additions & 5 deletions src/routes/books.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import { Router } from 'express';
import { Book } from '../models';
import { Book, Review } from '../models';
import catchAsync from '../lib/catchAsync';
import { pick } from 'lodash';
import Sequelize from 'sequelize';

const router = Router();

const Op = Sequelize.Op;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You could also change the import line above to something like import { Op }, Sequelize from 'sequelize;`. I think...

// Fields people are permitted to modify using the API.
const permittedParams = ['title', 'author'];

Expand All @@ -14,7 +14,6 @@ router.get('/', catchAsync(async (req, res) => {
let books;
if (req.query.q) {
try {
const Op = Sequelize.Op;
books =
await Book.findAll({ where:
{
Expand Down Expand Up @@ -58,13 +57,21 @@ router.post('/', catchAsync(async (req, res) => {
// Get a single book by ID.
// TODO: Should render HTML, not return JSON.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This TODO can be removed; it's rendering HTML now!

router.get('/:id', catchAsync(async (req, res) => {
let book;
let reviews;
let rating = 0;
try {
const book = await Book.findByPk(req.params.id);
res.json(book);
book = await Book.findByPk(req.params.id);
reviews = await Review.findAll({where: {BookId: {[Op.eq]: book.id}}});
if(reviews.length > 0) {
rating = reviews.reduce((a, review) => a + review.Rating,0) / reviews.length;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This code feels like something best placed in the Book or Review model instead, what do you think?

}
// res.json(book);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's OK to delete this :)

} catch(e) {
console.warn(e);
res.status(400).send(e.toString());
}
res.render('books/show', {book: book, reviews: reviews, rating: rating});
}));

// Update a book by ID. Supply fields in the same way as creating a book.
Expand Down
2 changes: 2 additions & 0 deletions src/routes/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import books from './books';
import users from './users';
import sessions from './sessions';
import loans from './loans';
import review from './review';

import { Book, User } from '../models';
import catchAsync from '../lib/catchAsync';
Expand All @@ -14,6 +15,7 @@ router.use('/books', books);
router.use('/users', users);
router.use('/sessions', sessions);
router.use('/loans', loans);
router.use('/review', review);

router.get('/login', (req, res) => res.redirect('/sessions/new'));

Expand Down
23 changes: 23 additions & 0 deletions src/routes/review.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { Router } from 'express';
import { ensureLoggedIn } from 'connect-ensure-login';

import { Book, Review} from '../models';
import catchAsync from '../lib/catchAsync';

const router = Router();
const env = process.env.NODE_ENV || 'development';

router.post('/',
ensureLoggedIn(),
catchAsync(async (req, res) => {
const {BookId, Text, Rating} = req.body;

const review = await req.user.createReview({BookId, Text, Rating});


// const loan = await req.user.createLoan({BookId, dueDate});
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's this?


res.redirect('/books/'+BookId);
})
);
export default router;
4 changes: 3 additions & 1 deletion src/views/books/index.ejs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@

<% books.forEach((book) => { %>
<p>
<%- book.title %> by <%- book.author %><br />
<a href="/books/<%- book.id %>"><%- book.title %> by <%- book.author %></a>
<br />
<% if (book.onLoan) { %>
On loan
<% } else { %>
Expand All @@ -14,6 +15,7 @@
</form>
<% } %>
</p>
<p><%- book.id %></p>
<hr>
<% }) %>

Expand Down
25 changes: 25 additions & 0 deletions src/views/books/show.ejs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<p><%- book.title %></p>
<p><%- book.author %></p>
<p>average rating: <%- rating %></p>

<% reviews.forEach((review) => { %>
<p>Stars: <%- review.Rating %> <%- review.Text %></p>
<hr/>
<% }) %>
<hr/>
<form action="/review" method="post">
<input type="hidden" name="BookId" value="<%- book.id %>">
<p>Rate this book</p>
<label for="Rating">Stars</label>
<select name="Rating">
<option value="1">1</option>
<option value="2">2</option>
<option value="3">3</option>
</select>
<br/>
<label for="Text">My opinion about this book</label>
<br/>
<textarea name="Text" rows="8" cols="80"></textarea>
<br/>
<button type="submit">Submit my review</button>
</form>