├── .babelrc ├── .eslintrc.js ├── .gitignore ├── Procfile ├── package-lock.json ├── package.json ├── readme.md └── src ├── api ├── helpers │ └── jwt.js ├── index.js ├── middlewares │ ├── is-artist.js │ └── passport-jwt.js └── resources │ ├── playlist │ ├── index.js │ ├── playlist.controller.js │ ├── playlist.model.js │ ├── playlist.router.js │ └── playlist.service.js │ ├── song │ ├── index.js │ ├── song.controller.js │ ├── song.model.js │ └── song.router.js │ └── user │ ├── index.js │ ├── user.controller.js │ ├── user.model.js │ ├── user.router.js │ └── user.service.js ├── app.js └── config ├── config.js ├── db.js └── swagger.json /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["env"], 3 | "plugins": ["transform-runtime"] 4 | } 5 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | extends: ['airbnb-base', 'plugin:prettier/recommended'], 3 | plugins: ['prettier'], 4 | env: { 5 | node: true 6 | }, 7 | rules: { 8 | env: { 9 | node: true 10 | }, 11 | 'prettier/prettier': [ 12 | 'error', 13 | { 14 | singleQuote: true 15 | } 16 | ], 17 | quotes: [ 18 | 2, 19 | 'single', 20 | { 21 | avoidEscape: true, 22 | allowTemplateLiterals: true 23 | } 24 | ], 25 | 'prettier/prettier': [ 26 | 'error', 27 | { 28 | trailingComma: 'es5', 29 | singleQuote: true, 30 | printWidth: 120 31 | } 32 | ], 33 | 'no-console': 0, 34 | 'import/prefer-default-export': 0, 35 | import: 0, 36 | 'func-names': 0, 37 | 'space-before-function-paren': 0, 38 | 'comma-dangle': 0, 39 | 'max-len': 0, 40 | 'import/extensions': 0, 41 | 'no-underscore-dangle': 0, 42 | 'consistent-return': 0, 43 | 'no-debugger': 0, 44 | 'no-unused-vars': [ 45 | 1, 46 | { 47 | argsIgnorePattern: 'res|next|^err' 48 | } 49 | ], 50 | 'arrow-body-style': [2, 'as-needed'], 51 | 'no-unused-expressions': [ 52 | 2, 53 | { 54 | allowTaggedTemplates: true 55 | } 56 | ], 57 | 'no-param-reassign': [ 58 | 2, 59 | { 60 | props: false 61 | } 62 | ] 63 | } 64 | }; 65 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .idea 3 | .DS_Store 4 | dist 5 | docs.md -------------------------------------------------------------------------------- /Procfile: -------------------------------------------------------------------------------- 1 | web: npm run start -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "music-api", 3 | "version": "1.0.0", 4 | "description": "A Music Restful API for Egghead Course: Secure Restful API with Nodejs, and Mongoose", 5 | "main": "index.js", 6 | "scripts": { 7 | "dev": "nodemon src/app.js --exec babel-node --presets env", 8 | "build": "babel src -d dist --source-maps inline --copy-files", 9 | "heroku-postbuild": "npm install", 10 | "start": "babel-node src/app.js" 11 | }, 12 | "keywords": [], 13 | "author": "", 14 | "license": "ISC", 15 | "dependencies": { 16 | "bcryptjs": "^2.4.3", 17 | "express": "^4.16.3", 18 | "joi": "^13.3.0", 19 | "jsonwebtoken": "^8.2.2", 20 | "mongoose": "^5.1.3", 21 | "mongoose-paginate": "^5.0.3", 22 | "morgan": "^1.9.0", 23 | "passport": "^0.4.0", 24 | "passport-jwt": "^4.0.0", 25 | "swagger-ui-express": "^3.0.9" 26 | }, 27 | "devDependencies": { 28 | "babel-cli": "^6.26.0", 29 | "babel-plugin-transform-runtime": "^6.23.0", 30 | "babel-preset-env": "^1.6.1", 31 | "eslint-config-airbnb-base": "^12.1.0", 32 | "eslint-plugin-import": "^2.11.0", 33 | "nodemon": "^1.17.3" 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | ## Install Dependencies 2 | 3 | ```shell 4 | npm install 5 | ``` 6 | 7 | ## Start MongoDB 8 | 9 | ```shell 10 | mongod 11 | ``` 12 | 13 | ## Run Application 14 | 15 | ```shell 16 | npm start 17 | ``` 18 | -------------------------------------------------------------------------------- /src/api/helpers/jwt.js: -------------------------------------------------------------------------------- 1 | import jwt from 'jsonwebtoken'; 2 | import { getConfig } from '../../config/config'; 3 | 4 | const config = getConfig(process.env.NODE_ENV); 5 | export default { 6 | issue(payload, expiresIn) { 7 | return jwt.sign(payload, config.secret, { 8 | expiresIn, 9 | }); 10 | }, 11 | }; 12 | -------------------------------------------------------------------------------- /src/api/index.js: -------------------------------------------------------------------------------- 1 | import express from 'express'; 2 | import { songRouter } from './resources/song'; 3 | import { userRouter } from './resources/user/user.router'; 4 | import { playListRouter } from './resources/playlist'; 5 | 6 | export const restRouter = express.Router(); 7 | restRouter.use('/songs', songRouter); 8 | restRouter.use('/users', userRouter); 9 | restRouter.use('/playlist', playListRouter); 10 | -------------------------------------------------------------------------------- /src/api/middlewares/is-artist.js: -------------------------------------------------------------------------------- 1 | import { ARTIST_ROLE } from '../resources/user/user.model'; 2 | 3 | export const isArtist = (req, res, next) => { 4 | if (req.user.role !== ARTIST_ROLE) { 5 | return res.json({ err: 'unauthorized, not an artists' }); 6 | } 7 | next(); 8 | }; 9 | -------------------------------------------------------------------------------- /src/api/middlewares/passport-jwt.js: -------------------------------------------------------------------------------- 1 | import Passport from 'passport'; 2 | import PassportJWT from 'passport-jwt'; 3 | import { getConfig } from '../../config/config'; 4 | import User from '../resources/user/user.model'; 5 | 6 | const config = getConfig(process.env.NODE_ENV); 7 | export const configJWTStrategy = () => { 8 | const opts = { 9 | jwtFromRequest: PassportJWT.ExtractJwt.fromAuthHeaderAsBearerToken(), 10 | secretOrKey: config.secret, 11 | }; 12 | Passport.use( 13 | new PassportJWT.Strategy(opts, (paylod, done) => { 14 | User.findOne({ _id: paylod.id }, (err, user) => { 15 | if (err) { 16 | return done(err); 17 | } 18 | if (user) { 19 | return done(null, user); 20 | } 21 | return done(null, false); 22 | }); 23 | }) 24 | ); 25 | }; 26 | -------------------------------------------------------------------------------- /src/api/resources/playlist/index.js: -------------------------------------------------------------------------------- 1 | export { playListRouter } from './playlist.router'; 2 | -------------------------------------------------------------------------------- /src/api/resources/playlist/playlist.controller.js: -------------------------------------------------------------------------------- 1 | import playlistService from './playlist.service'; 2 | import Playlist from './playlist.model'; 3 | 4 | export default { 5 | async create(req, res) { 6 | try { 7 | const { value, error } = playlistService.validateBody(req.body); 8 | if (error && error.details) { 9 | return res.json(error); 10 | } 11 | const playlist = await Playlist.create(Object.assign({}, value, { user: req.user._id })); 12 | return res.json(playlist); 13 | } catch (err) { 14 | console.error(err); 15 | return res.status(500).send(err); 16 | } 17 | }, 18 | async findAll(req, res) { 19 | try { 20 | const playlists = await Playlist.find() 21 | .populate('songs') 22 | .populate('user', 'firstName lastName'); 23 | return res.json(playlists); 24 | } catch (err) { 25 | console.error(err); 26 | return res.status(500).send(err); 27 | } 28 | }, 29 | }; 30 | -------------------------------------------------------------------------------- /src/api/resources/playlist/playlist.model.js: -------------------------------------------------------------------------------- 1 | import mongoose from 'mongoose'; 2 | 3 | const { Schema } = mongoose; 4 | const playListSchema = new Schema({ 5 | name: { 6 | type: String, 7 | required: [true, 'Playlist must have name'], 8 | }, 9 | songs: [ 10 | { 11 | type: mongoose.Schema.Types.ObjectId, 12 | ref: 'Song', 13 | required: true, 14 | }, 15 | ], 16 | user: { 17 | type: mongoose.Schema.Types.ObjectId, 18 | ref: 'User', 19 | required: true, 20 | }, 21 | }); 22 | export default mongoose.model('Playlist', playListSchema); 23 | -------------------------------------------------------------------------------- /src/api/resources/playlist/playlist.router.js: -------------------------------------------------------------------------------- 1 | import express from 'express'; 2 | import passport from 'passport'; 3 | import playlistController from './playlist.controller'; 4 | 5 | export const playListRouter = express.Router(); 6 | playListRouter 7 | .route('/') 8 | .post(passport.authenticate('jwt', { session: false }), playlistController.create) 9 | .get(passport.authenticate('jwt', { session: false }), playlistController.findAll); 10 | -------------------------------------------------------------------------------- /src/api/resources/playlist/playlist.service.js: -------------------------------------------------------------------------------- 1 | import Joi from 'joi'; 2 | 3 | export default { 4 | validateBody(body) { 5 | const schema = Joi.object().keys({ 6 | songs: Joi.array() 7 | .items() 8 | .required(), 9 | name: Joi.string().required(), 10 | }); 11 | const { value, error } = Joi.validate(body, schema); 12 | if (error && error.details) { 13 | return { error }; 14 | } 15 | return { value }; 16 | }, 17 | }; 18 | -------------------------------------------------------------------------------- /src/api/resources/song/index.js: -------------------------------------------------------------------------------- 1 | export { songRouter } from './song.router'; 2 | -------------------------------------------------------------------------------- /src/api/resources/song/song.controller.js: -------------------------------------------------------------------------------- 1 | import Joi from 'joi'; 2 | import Song from './song.model'; 3 | 4 | export default { 5 | async create(req, res) { 6 | try { 7 | const schema = Joi.object().keys({ 8 | title: Joi.string().required(), 9 | url: Joi.string().required(), 10 | rating: Joi.number() 11 | .integer() 12 | .min(0) 13 | .max(5) 14 | .optional(), 15 | }); 16 | const { value, error } = Joi.validate(req.body, schema); 17 | if (error && error.details) { 18 | return res.status(400).json(error); 19 | } 20 | const song = await Song.create(Object.assign({}, value, { artist: req.user._id })); 21 | return res.json(song); 22 | } catch (err) { 23 | console.error(err); 24 | return res.status(500).send(err); 25 | } 26 | }, 27 | async findAll(req, res) { 28 | try { 29 | const { page, perPage } = req.query; 30 | const options = { 31 | page: parseInt(page, 10) || 1, 32 | limit: parseInt(perPage, 10) || 10, 33 | populate: { 34 | path: 'artist', 35 | select: 'firstName lastName', 36 | }, 37 | }; 38 | const songs = await Song.paginate({}, options); 39 | return res.json(songs); 40 | } catch (err) { 41 | console.error(err); 42 | return res.status(500).send(err); 43 | } 44 | }, 45 | async findOne(req, res) { 46 | try { 47 | const { id } = req.params; 48 | const song = await Song.findById(id).populate('artist', 'firstName lastName'); 49 | if (!song) { 50 | return res.status(404).json({ err: 'could not find song' }); 51 | } 52 | return res.json(song); 53 | } catch (err) { 54 | console.error(err); 55 | return res.status(500).send(err); 56 | } 57 | }, 58 | async delete(req, res) { 59 | try { 60 | const { id } = req.params; 61 | const song = await Song.findOneAndRemove({ _id: id }); 62 | if (!song) { 63 | return res.status(404).json({ err: 'could not find song' }); 64 | } 65 | return res.json(song); 66 | } catch (err) { 67 | console.error(err); 68 | return res.status(500).send(err); 69 | } 70 | }, 71 | async update(req, res) { 72 | try { 73 | const { id } = req.params; 74 | const schema = Joi.object().keys({ 75 | title: Joi.string().optional(), 76 | url: Joi.string().optional(), 77 | rating: Joi.number() 78 | .integer() 79 | .min(0) 80 | .max(5) 81 | .optional(), 82 | }); 83 | const { value, error } = Joi.validate(req.body, schema); 84 | if (error && error.details) { 85 | return res.status(400).json(error); 86 | } 87 | const song = await Song.findOneAndUpdate({ _id: id }, value, { new: true }); 88 | if (!song) { 89 | return res.status(404).json({ err: 'could not find song' }); 90 | } 91 | return res.json(song); 92 | } catch (err) { 93 | console.error(err); 94 | return res.status(500).send(err); 95 | } 96 | }, 97 | }; 98 | -------------------------------------------------------------------------------- /src/api/resources/song/song.model.js: -------------------------------------------------------------------------------- 1 | import mongoose from 'mongoose'; 2 | import mongoosePaginate from 'mongoose-paginate'; 3 | 4 | const { Schema } = mongoose; 5 | const songSchema = new Schema({ 6 | title: { 7 | type: String, 8 | required: [true, 'Song must have title'], 9 | }, 10 | url: { 11 | type: String, 12 | required: [true, 'Song must have url'], 13 | }, 14 | rating: { 15 | type: Number, 16 | default: 0, 17 | min: 0, 18 | max: 5, 19 | }, 20 | artist: { 21 | type: mongoose.Schema.Types.ObjectId, 22 | ref: 'User', 23 | required: true, 24 | }, 25 | }); 26 | songSchema.plugin(mongoosePaginate); 27 | export default mongoose.model('Song', songSchema); 28 | -------------------------------------------------------------------------------- /src/api/resources/song/song.router.js: -------------------------------------------------------------------------------- 1 | import express from 'express'; 2 | import passport from 'passport'; 3 | import songController from './song.controller'; 4 | import { isArtist } from '../../middlewares/is-artist'; 5 | 6 | export const songRouter = express.Router(); 7 | // 1.authenticated user can view all the songs 8 | // 2.an artist can create, update, and delete song 9 | 10 | const artistPolicy = [passport.authenticate('jwt', { session: false }), isArtist]; 11 | songRouter 12 | .route('/') 13 | .post(artistPolicy, songController.create) 14 | .get(passport.authenticate('jwt', { session: false }), songController.findAll); 15 | 16 | songRouter 17 | .route('/:id') 18 | .get(passport.authenticate('jwt', { session: false }), songController.findOne) 19 | .delete(artistPolicy, songController.delete) 20 | .put(artistPolicy, songController.update); 21 | -------------------------------------------------------------------------------- /src/api/resources/user/index.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HaiderMalik12/build-and-secure-restful-api/875cd5f11ca9816773445cae5b110dbd5c95ae3a/src/api/resources/user/index.js -------------------------------------------------------------------------------- /src/api/resources/user/user.controller.js: -------------------------------------------------------------------------------- 1 | import userService from './user.service'; 2 | import User, { STANDARD_ROLE } from './user.model'; 3 | import jwt from '../../helpers/jwt'; 4 | 5 | export default { 6 | async signup(req, res) { 7 | try { 8 | const { value, error } = userService.validateSignup(req.body); 9 | if (error) { 10 | return res.status(400).json(error); 11 | } 12 | const encryptedPass = userService.encryptPassword(value.password); 13 | 14 | const user = await User.create({ 15 | email: value.email, 16 | firstName: value.firstName, 17 | lastName: value.lastName, 18 | password: encryptedPass, 19 | role: value.role || STANDARD_ROLE, 20 | }); 21 | return res.json({ success: true }); 22 | } catch (err) { 23 | console.error(err); 24 | return res.status(500).send(err); 25 | } 26 | }, 27 | async login(req, res) { 28 | try { 29 | const { value, error } = userService.validateLogin(req.body); 30 | if (error) { 31 | return res.status(400).json(error); 32 | } 33 | const user = await User.findOne({ email: value.email }); 34 | if (!user) { 35 | return res.status(401).json({ err: 'unauthorized' }); 36 | } 37 | const authenticted = userService.comparePassword(value.password, user.password); 38 | if (!authenticted) { 39 | return res.status(401).json({ err: 'unauthorized' }); 40 | } 41 | const token = jwt.issue({ id: user._id }, '1d'); 42 | return res.json({ token }); 43 | } catch (err) { 44 | console.error(err); 45 | return res.status(500).send(err); 46 | } 47 | }, 48 | authenticate(req, res) { 49 | return res.json(req.user); 50 | }, 51 | }; 52 | -------------------------------------------------------------------------------- /src/api/resources/user/user.model.js: -------------------------------------------------------------------------------- 1 | import mongoose from 'mongoose'; 2 | 3 | export const STANDARD_ROLE = 2; 4 | export const ARTIST_ROLE = 1; 5 | const { Schema } = mongoose; 6 | const userSchema = new Schema({ 7 | firstName: { 8 | type: String, 9 | required: true, 10 | }, 11 | lastName: { 12 | type: String, 13 | required: true, 14 | }, 15 | email: { 16 | type: String, 17 | required: true, 18 | unique: true, 19 | }, 20 | password: { 21 | type: String, 22 | required: true, 23 | }, 24 | role: { 25 | default: 2, 26 | required: true, 27 | type: Number, 28 | }, 29 | }); 30 | 31 | export default mongoose.model('User', userSchema); 32 | -------------------------------------------------------------------------------- /src/api/resources/user/user.router.js: -------------------------------------------------------------------------------- 1 | import express from 'express'; 2 | import passport from 'passport'; 3 | import userController from './user.controller'; 4 | 5 | export const userRouter = express.Router(); 6 | userRouter.post('/signup', userController.signup); 7 | userRouter.post('/login', userController.login); 8 | userRouter.get('/me', passport.authenticate('jwt', { session: false }), userController.authenticate); 9 | -------------------------------------------------------------------------------- /src/api/resources/user/user.service.js: -------------------------------------------------------------------------------- 1 | import Joi from 'joi'; 2 | import bcrypt from 'bcryptjs'; 3 | 4 | export default { 5 | encryptPassword(palinText) { 6 | const salt = bcrypt.genSaltSync(10); 7 | return bcrypt.hashSync(palinText, salt); 8 | }, 9 | comparePassword(plainText, encrypedPassword) { 10 | return bcrypt.compareSync(plainText, encrypedPassword); 11 | }, 12 | validateSignup(body) { 13 | const schema = Joi.object().keys({ 14 | firstName: Joi.string().required(), 15 | lastName: Joi.string().required(), 16 | email: Joi.string() 17 | .email() 18 | .required(), 19 | password: Joi.string().required(), 20 | role: Joi.number().integer(), 21 | }); 22 | const { value, error } = Joi.validate(body, schema); 23 | if (error && error.details) { 24 | return { error }; 25 | } 26 | return { value }; 27 | }, 28 | validateLogin(body) { 29 | const schema = Joi.object().keys({ 30 | email: Joi.string() 31 | .email() 32 | .required(), 33 | password: Joi.string().required(), 34 | }); 35 | const { value, error } = Joi.validate(body, schema); 36 | if (error && error.details) { 37 | return { error }; 38 | } 39 | return { value }; 40 | }, 41 | }; 42 | -------------------------------------------------------------------------------- /src/app.js: -------------------------------------------------------------------------------- 1 | import express from 'express'; 2 | import logger from 'morgan'; 3 | import passport from 'passport'; 4 | import swaggerUi from 'swagger-ui-express'; 5 | import { connect } from './config/db'; 6 | import { restRouter } from './api'; 7 | import swaggerDocument from './config/swagger.json'; 8 | import { configJWTStrategy } from './api/middlewares/passport-jwt'; 9 | 10 | const app = express(); 11 | const PORT = process.env.PORT || 3000; 12 | 13 | connect(); 14 | app.use(express.json()); 15 | app.use(express.urlencoded({ extended: true })); 16 | if (process.env.NODE_ENV === 'development') { 17 | app.use(logger('dev')); 18 | } 19 | app.use(passport.initialize()); // req.user 20 | configJWTStrategy(); 21 | app.use('/api', restRouter); 22 | app.use( 23 | '/', 24 | swaggerUi.serve, 25 | swaggerUi.setup(swaggerDocument, { 26 | explorer: true, 27 | }) 28 | ); 29 | app.use((req, res, next) => { 30 | const error = new Error('Not found'); 31 | error.message = 'Invalid route'; 32 | error.status = 404; 33 | next(error); 34 | }); 35 | app.use((error, req, res, next) => { 36 | res.status(error.status || 500); 37 | return res.json({ 38 | error: { 39 | message: error.message, 40 | }, 41 | }); 42 | }); 43 | 44 | app.listen(PORT, () => { 45 | console.log(`Server is running at PORT http://localhost:${PORT}`); 46 | }); 47 | -------------------------------------------------------------------------------- /src/config/config.js: -------------------------------------------------------------------------------- 1 | const config = { 2 | production: { 3 | secret: process.env.secret, 4 | MONGO_URI: process.env.MONGO_URI, 5 | port: process.env.PORT, 6 | }, 7 | development: { 8 | secret: 'I_AME_GERER', 9 | MONGO_URI: 'mongodb://localhost/music_api', 10 | port: 3000, 11 | }, 12 | }; 13 | 14 | export const getConfig = env => config[env] || config.development; 15 | -------------------------------------------------------------------------------- /src/config/db.js: -------------------------------------------------------------------------------- 1 | import mongoose from 'mongoose'; 2 | import { getConfig } from './config'; 3 | 4 | const config = getConfig(process.env.NODE_ENV); 5 | mongoose.Promise = global.Promise; 6 | export const connect = () => mongoose.connect(config.MONGO_URI); 7 | -------------------------------------------------------------------------------- /src/config/swagger.json: -------------------------------------------------------------------------------- 1 | { 2 | "title" : "Music API Documentation", 3 | "description" : "MUSIC API course for egghead", 4 | "license": { 5 | "name": "MIT", 6 | "url": "https://opensource.org/licenses/MIT" 7 | }, 8 | "version": "1.0.0", 9 | "host": "polar-plateau-87434.herokuapp.com", 10 | "basePath": "/api", 11 | "tags": [ 12 | { 13 | "name": "Songs", 14 | "description": "API for Songs Endpoints" 15 | }, 16 | { 17 | "name": "Users", 18 | "description": "API for Users Endpoints" 19 | }, 20 | { 21 | "name": "PlayLists", 22 | "description": "API for Playlist Endpoints" 23 | } 24 | ], 25 | "schemes": ["http","https"], 26 | "consumes": ["application/json"], 27 | "produces": ["application/json"], 28 | "paths" :{ 29 | "/users/signup":{ 30 | "post":{ 31 | "tags": ["Users"], 32 | "summary": "API Endpoint for create User", 33 | "description" : "Create new User in database", 34 | "parameters":[ 35 | { 36 | "name": "user", 37 | "description": "User params to regitser new user", 38 | "in": "body", 39 | "required": "true", 40 | "schema" : { 41 | "$ref" : "#definitions/User" 42 | } 43 | } 44 | ], 45 | "produces" : ["application/json"], 46 | "responses":{ 47 | "200":{ 48 | "description" : "New user has Created", 49 | "schema": { 50 | "$ref" : "#definitions/SignupRsp" 51 | } 52 | } 53 | } 54 | } 55 | }, 56 | "/users/login":{ 57 | "post":{ 58 | "tags": ["Users"], 59 | "summary": "API Endpoint to login his/her account", 60 | "description" : "Login user", 61 | "parameters":[ 62 | { 63 | "name": "user", 64 | "description": "User params to login", 65 | "in": "body", 66 | "required": "true", 67 | "schema" : { 68 | "$ref" : "#definitions/UserLogin" 69 | } 70 | } 71 | ], 72 | "produces" : ["application/json"], 73 | "responses":{ 74 | "200":{ 75 | "description" : "New user has loggedIn", 76 | "schema": { 77 | "$ref" : "#definitions/UserLoginRsp" 78 | } 79 | } 80 | } 81 | } 82 | }, 83 | "/songs":{ 84 | "post":{ 85 | "tags": ["Songs"], 86 | "summary": "API Endpoint for create Song", 87 | "description" : "Create new Song in database", 88 | "parameters":[ 89 | { 90 | "name": "song", 91 | "description": "Song params to create new song", 92 | "in": "body", 93 | "required": "true", 94 | "schema" : { 95 | "$ref" : "#definitions/Song" 96 | } 97 | }, 98 | { 99 | "name": "authorization", 100 | "description": "Access token to authorize the user", 101 | "in": "header", 102 | "type": "string", 103 | "required": true 104 | } 105 | ], 106 | "produces" : ["application/json"], 107 | "responses":{ 108 | "200":{ 109 | "description" : "New Song Created", 110 | "schema": { 111 | "$ref" : "#definitions/Song" 112 | } 113 | } 114 | } 115 | }, 116 | "get":{ 117 | "tags": ["Songs"], 118 | "summary": "API Endpoint to find all Song", 119 | "description" : "Find all Songs from the database", 120 | "parameters":[ 121 | { 122 | "name": "page", 123 | "type": "integer", 124 | "description": "Define the page index for the records", 125 | "in": "query" 126 | }, 127 | { 128 | "name": "perPage", 129 | "type": "integer", 130 | "description": "Define the limiet for the records i.e 10", 131 | "in": "query" 132 | }, 133 | { 134 | "name": "authorization", 135 | "description": "Access token to authorize the user", 136 | "in": "header", 137 | "type": "string", 138 | "required": true 139 | } 140 | ], 141 | "produces" : ["application/json"], 142 | "responses":{ 143 | "200":{ 144 | "description" : "All the songs", 145 | "schema": { 146 | "$ref" : "#definitions/Songs" 147 | } 148 | } 149 | } 150 | } 151 | }, 152 | "/songs/{id}":{ 153 | "parameters":[ 154 | { 155 | "name" : "id", 156 | "in": "path", 157 | "required": "true", 158 | "description": "id of the song", 159 | "type": "string" 160 | 161 | }, 162 | { 163 | "name": "authorization", 164 | "description": "Access token to authorize the user", 165 | "in": "header", 166 | "type": "string", 167 | "required": true 168 | } 169 | ], 170 | "get":{ 171 | "tags": ["Songs"], 172 | "summary": "API Endpoint to find single Song", 173 | "description" : "Find one from the database", 174 | "produces" : ["application/json"], 175 | "responses":{ 176 | "200":{ 177 | "description" : "Song has found", 178 | "schema": { 179 | "$ref" : "#definitions/Song" 180 | } 181 | } 182 | } 183 | }, 184 | "delete":{ 185 | "tags": ["Songs"], 186 | "summary": "API Endpoint to delete the Song", 187 | "description" : "Delete song from the database", 188 | "produces" : ["application/json"], 189 | "responses":{ 190 | "200":{ 191 | "description" : "Song has deleted", 192 | "schema": { 193 | "$ref" : "#definitions/Song" 194 | } 195 | } 196 | } 197 | }, 198 | "put":{ 199 | "tags": ["Songs"], 200 | "summary": "API Endpoint for update Song", 201 | "description" : "Update the existing Song in database", 202 | "parameters":[ 203 | { 204 | "name": "song", 205 | "description": "Song params to update the song", 206 | "in": "body", 207 | "schema" : { 208 | "$ref" : "#definitions/UpdateSong" 209 | } 210 | } 211 | ], 212 | "produces" : ["application/json"], 213 | "responses":{ 214 | "200":{ 215 | "description" : "Song has Updated", 216 | "schema": { 217 | "$ref" : "#definitions/Song" 218 | } 219 | } 220 | } 221 | } 222 | }, 223 | "/playlist": { 224 | "post": { 225 | "tags": ["PlayLists"], 226 | "summary": "Create a new PlayList", 227 | "description": "Add new songs in the PlayList", 228 | "parameters": [ 229 | { 230 | "name": "playlist", 231 | "description": "Songs that user want to add in the PlayList", 232 | "in": "body", 233 | "required": true, 234 | "schema": { 235 | "$ref": "#definitions/PlayList" 236 | } 237 | }, 238 | { 239 | "name": "authorization", 240 | "description": "Access token to authorize the user", 241 | "in": "header", 242 | "type": "string", 243 | "required": true 244 | } 245 | ], 246 | "produces": ["application/json"], 247 | "responses": { 248 | "200": { 249 | "description": "new PlayList is created", 250 | "schema": { 251 | "$ref": "#definitions/PlayList" 252 | } 253 | } 254 | } 255 | }, 256 | "get": { 257 | "tags": ["PlayLists"], 258 | "summary": "Find all the PlayList with Songs", 259 | "description": "View all the Songs in the PlayList", 260 | "parameters": [ 261 | { 262 | "name": "authorization", 263 | "description": "Access token to authorize the user", 264 | "in": "header", 265 | "type": "string", 266 | "required": true 267 | } 268 | ], 269 | "produces": ["application/json"], 270 | "responses": { 271 | "200": { 272 | "description": "View all the PlayList", 273 | "schema": { 274 | "type": "array", 275 | "$ref": "#definitions/PlayLists" 276 | } 277 | } 278 | } 279 | } 280 | } 281 | }, 282 | "definitions":{ 283 | "SignupRsp":{ 284 | "properties":{ 285 | "success": { 286 | "type":"boolean" 287 | } 288 | } 289 | }, 290 | "User":{ 291 | "required": ["firstName","lastName", "email", "password"], 292 | "properties":{ 293 | "_id": { 294 | "type" : "string" 295 | }, 296 | "firstName": { 297 | "type" : "string" 298 | }, 299 | "lastName": { 300 | "type" : "string" 301 | }, 302 | "email": { 303 | "type" : "string" 304 | }, 305 | "password": { 306 | "type" : "string" 307 | }, 308 | "role": { 309 | "type": "integer" 310 | } 311 | } 312 | }, 313 | "UserLogin":{ 314 | "required": ["email", "password"], 315 | "properties":{ 316 | "email": { 317 | "type" : "string" 318 | }, 319 | "password": { 320 | "type" : "string" 321 | } 322 | } 323 | }, 324 | "UserLoginRsp":{ 325 | "properties":{ 326 | "token": { 327 | "type" : "string" 328 | } 329 | } 330 | }, 331 | "Song":{ 332 | "required": ["title","url"], 333 | "properties":{ 334 | "_id": { 335 | "type" : "string" 336 | }, 337 | "title": { 338 | "type" : "string" 339 | }, 340 | "url": { 341 | "type" : "string" 342 | }, 343 | "rating": { 344 | "type" : "integer", 345 | "format" :"Int32" 346 | } 347 | } 348 | }, 349 | "UpdateSong":{ 350 | "properties":{ 351 | "title": { 352 | "type" : "string" 353 | }, 354 | "url": { 355 | "type" : "string" 356 | }, 357 | "rating": { 358 | "type" : "integer", 359 | "format" :"Int32" 360 | } 361 | } 362 | }, 363 | "Songs":{ 364 | "properties":{ 365 | "docs": { 366 | "type" : "array", 367 | "$ref": "#definitions/Song" 368 | }, 369 | "total": { 370 | "type" : "integer" 371 | }, 372 | "pages": { 373 | "type" : "integer" 374 | }, 375 | "limit": { 376 | "type" : "integer" 377 | }, 378 | "page":{ 379 | "type": "integer" 380 | } 381 | } 382 | }, 383 | "PlayList": { 384 | "required": ["name"], 385 | "type": "object", 386 | "properties": { 387 | "_id": { 388 | "type": "string", 389 | "uniqueItems": true 390 | }, 391 | "name": { 392 | "type": "string" 393 | }, 394 | "songs": { 395 | "type": "array", 396 | "items": { 397 | "type": "string" 398 | } 399 | } 400 | } 401 | }, 402 | "PlayListCustomRsp": { 403 | "type": "object", 404 | "properties": { 405 | "_id": { 406 | "type": "string", 407 | "uniqueItems": true 408 | }, 409 | "name": { 410 | "type": "string" 411 | }, 412 | "songs": { 413 | "type": "array", 414 | "items": { 415 | "$ref": "#definitions/Song" 416 | } 417 | }, 418 | "user":{ 419 | "$ref" : "#definitions/UserCustomRsp" 420 | } 421 | } 422 | }, 423 | "UserCustomRsp":{ 424 | "properties":{ 425 | "firstName" :{ 426 | "type" :"string" 427 | }, 428 | "lastName" :{ 429 | "type" :"string" 430 | } 431 | } 432 | }, 433 | "PlayLists": { 434 | "type": "array", 435 | "items": { 436 | "$ref": "#definitions/PlayListCustomRsp" 437 | } 438 | } 439 | } 440 | } --------------------------------------------------------------------------------