Skip to content
Permalink
master
Switch branches/tags

Name already in use

A tag already exists with the provided branch name. Many Git commands accept both tag and branch names, so creating this branch may cause unexpected behavior. Are you sure you want to create this branch?
Go to file
 
 
Cannot retrieve contributors at this time
'use strict'
const etag = require('etag')
const restify = require('restify')
const server = restify.createServer()
server.use(restify.fullResponse())
server.use(restify.bodyParser())
server.use(restify.queryParser())
server.use(restify.authorizationParser())
const bookshop = require('./bookshop.js')
const status = {
'request header missing': 422,
'content-type header must be application/json': 422,
'authorization header missing': 403,
'username already exists': 400,
'invalid credentials': 403,
'failed to make API call': 500,
'book not found': 400,
'missing username and/or password': 403,
'missing username': 403,
'error creating account': 500,
'shopping cart empty': 400,
'missing name': 403,
'parameter missing': 422,
'missing key in request body': 400,
ok: 200,
added: 201,
created: 201,
notModified: 304,
badRequest: 400
}
const defaultPort = 8000
server.get('/', (req, res, next) => {
res.redirect(`/books?q=${req.params.q}`, next)
})
/**
* @api {get} /books book search
* @apiDescription search the Google book collection
* on the Google Books API using the keyword specified in
* the URL parameter.
* @apiGroup Books
* @apisamplerequest off
* @apiPermission none
* @apiHeader Content-Type application/json
* @apiHeader If-None-Match (optional) the eTag hash from the last request
* @apiParam {String} q Query string
* @apiSuccess {Object} response top-level object
* @apiSuccess {Array} response.books an array of books
* @apiSuccess {String} response.books.title the book title
* @apiSuccess {String} response.books.link link to the book resource
* @apiExample {curl} Example usage:
* curl -i http://api.example.com/books?q=nodejs
* @apiSuccessExample {json} Success-Response:
* HTTP/1.1 200 OK
* {
* "books": [
* {
* "title": "Node.js in Action",
* "link": "http://localhost/books/YzfuvQAACAAJ"
* },
* {
* "title": "Professional Node.js",
* "link": "http://localhost/books/ZH6bpbcrlvYC"
* },
* {
* "title": "Smashing Node.js",
* "link": "http://localhost/books/G1y_5kpmatUC"
* }
* ]
* }
* @apiError 422/Unprocessable-Entity query parameter 'q' missing
* @apiErrorExample {json} List error
* HTTP/1.1 422 Unprocessable Entity
* {
* "error": "parameter missing"
* }
*/
server.get('/books', (req, res) => {
bookshop.search(req, (err, data) => {
res.setHeader('content-type', 'application/json')
res.setHeader('accepts', 'GET')
const etagHash = etag(JSON.stringify(data)).replace(/"/g, '')
res.setHeader('ETag', etagHash)
if (err) {
res.send(status[err.message], {error: err.message})
} else if(String(req.headers['if-none-match']) === String(etagHash)) {
res.send(status.notModified)
} else {
res.setHeader('ETag', etag(JSON.stringify(data)))
res.send(status.ok, data)
}
res.end()
})
})
/**
* @api {get} /books/:isbn book details
* @apiDescription return the details of a book based on its ISBN number
* @apiGroup Books
* @apisamplerequest off
* @apiPermission none
* @apiHeader Content-Type application/json
* @apiHeader If-None-Match (optional) the eTag hash from the last request
* @apiParam {String} isbn The ISBN10 number
* @apiSuccess {Object} response top-level object
* @apiSuccess {Object} response.book an object representing the book resource
* @apiSuccess {String} response.book.isbn the ISBN10 number of the book
* @apiSuccess {String} response.book.title the book title
* @apiSuccess {Array} response.book.authors array of Strings representing the authors
* @apiSuccess {String} response.book.description description the book resource
* @apiExample {curl} Example usage:
* curl -i http://api.example.com/books/1617292575
* @apiSuccessExample {json} Success-Response:
* HTTP/1.1 200 OK
* {
* "book": {
* "isbn": "1617292575",
* "title": "Node.js in Action",
* "authors": [
* "Mike Cantelon",
* "Alex R. Young"
* ],
* "description": "JavaScript on the server? You bet!"
* }
* }
* @apiError 422/Unprocessable-Entity query parameter 'isbn' missing
* @apiErrorExample {json} List error
* HTTP/1.1 422 Unprocessable Entity
* {
* "error": "parameter missing"
* }
*/
server.get('/books/:isbn', (req, res) => {
bookshop.getBook(req, (err, bookData) => {
res.setHeader('content-type', 'application/json')
res.setHeader('accepts', 'GET')
const etagHash = etag(JSON.stringify(bookData)).replace(/"/g, '')
res.setHeader('ETag', etagHash)
if (err) {
res.send(status[err.message], {error: err.message})
} else if(String(req.headers['if-none-match']) === String(etagHash)) {
res.send(status.notModified)
} else {
res.send(status.ok, {book: bookData})
}
res.end()
})
})
/**
* @api {post} /cart add to shopping cart
* @apiDescription add a book to the user's shopping cart
* @apiGroup Cart
* @apisamplerequest off
* @apiPermission valid user account
* @apiHeader Authorization Basic Access Authentication token
* @apiHeader Content-Type application/json
* @apiParam {String} isbn the book isbn number
* @apiParamExample {json} Request Body
* {
* "isbn": "0596517742"
* }
* @apiSuccessExample {json} Success-Response:
* HTTP/1.1 200 OK
* {
* "book": {
* "__v": 0,
* "title": "JavaScript: The Good Parts",
* "authors": "Douglas Crockford",
* "description": "Describes the reliable features of JavaScript."
* "account": "johndoe",
* "_id": "5956a17d60e9832157c7e45d"
* }
* }
* @apiError 401/Unauthorized Invalid basic auth credentials
*/
server.post('/cart', (req, res) => {
bookshop.addToCart(req, (err, data) => {
res.setHeader('content-type', 'application/json')
res.setHeader('accepts', 'GET, POST')
if (err) {
res.send(status[err.message], {error: err.message})
} else {
res.send(status.ok, {book: data})
}
res.end()
})
})
/**
* @api {get} /cart retrieve shopping cart
* @apiDescription return a list of the books in the shopping cart
* @apiGroup Cart
* @apisamplerequest off
* @apiPermission valid user account
* @apiHeader Authorization Basic Access Authentication token
* @apiHeader Content-Type application/json
* @apiHeader If-None-Match (optional) the eTag hash from the last request
* @apiSuccess {Object} response top-level object
* @apiSuccess {Array} response.cart an array of books
* @apiSuccess {String} response.cart.title the title of the book
* @apiSuccess {String} response.cart.subtitle the subtitle of the book
* @apiSuccess {String} response.cart.authors the book authors
* @apiSuccess {String} response.cart.bookID the Google ID of the book
* @apiSuccess {String} response.cart.isbn the ISBN of the book
* @apiSuccessExample {json} Success-Response:
* HTTP/1.1 200 OK
* {
* "cart": [
* {
* "_id": "5863c2ad7a620417d0293760",
* "title": "JavaScript",
* "subtitle": "The Definitive Guide",
* "authors": "David Flanagan",
* "bookID": "4RChxt67lvwC",
* "isbn": "0596805527",
* "account": "testuser",
* "__v": 0
* },
* {
* "_id": "5863c2e87a620417d0293761",
* "title": "JavaScript",
* "subtitle": "The Good Parts",
* "authors": "Douglas Crockford",
* "bookID": "F9ybAgAAQBAJ",
* "isbn": "0596517742",
* "account": "testuser",
* "__v": 0
* },
* ]
* }
*/
server.get('/cart', (req, res) => {
bookshop.getCart(req, (err, data) => {
res.setHeader('content-type', 'application/json')
res.setHeader('accepts', 'GET, POST')
const etagHash = etag(JSON.stringify(data)).replace(/"/g, '')
res.setHeader('ETag', etagHash)
if (err) {
res.send(status[err.message], {error: err.message})
} else if(String(req.headers['if-none-match']) === String(etagHash)) {
res.send(status.notModified)
} else {
res.setHeader('ETag', etag(JSON.stringify(data)))
res.send(status.ok, data)
}
res.end()
})
})
/**
* @api {post} /accounts create account
* @apiDescription create a new user account
* @apiGroup Accounts
* @apisamplerequest off
* @apiPermission valid user account (basic access authentication)
* @apiHeader Authorization Basic Access Authentication token
* @apiHeader Content-Type application/json
* @apiParam {String} name the full name of the user
* @apiParamExample {json} Request Body
* {
* "name": "John Doe"
* }
* @apiSuccessExample {json} Success-Response:
* HTTP/1.1 201 Created
* {
* "user": {
* "name": "John Doe",
* "username": "johndoe"
* }
* }
* @apiErrorExample {json} Authorization error
* HTTP/1.1 403 Forbidden
* {
* "error": "authorization header missing"
* }
*/
server.post('/accounts', (req, res) => {
bookshop.addUser(req, (err, data) => {
res.setHeader('content-type', 'application/json')
res.setHeader('accepts', 'GET, POST')
console.log('headers set')
if (err) {
console.log('sending badrequest')
res.send(status[err.message], {error: err.message})
} else {
console.log('sending OK')
res.send(status.added, {user: data})
}
res.end()
})
})
if (process.env['NODE_ENV'] === undefined) {
console.log('ERROR: env var NODE_ENV needs to be set to production or development')
process.exit()
}
console.log(`NODE_ENV: ${process.env['NODE_ENV']}`)
const port = process.env.PORT || defaultPort
server.listen(port, err => {
if (err) {
console.error(err)
} else {
console.log(`App is ready on port ${port}`)
}
})