First commit
This commit is contained in:
commit
d1089b77e4
9
.env.example
Normal file
9
.env.example
Normal file
@ -0,0 +1,9 @@
|
||||
// CONFIG
|
||||
PORT=3000
|
||||
CORS=*
|
||||
|
||||
// MONGO
|
||||
DB_USER=
|
||||
DB_PASSWORD=
|
||||
DB_HOST=
|
||||
DB_NAME=
|
14
.eslintrc.json
Normal file
14
.eslintrc.json
Normal file
@ -0,0 +1,14 @@
|
||||
{
|
||||
"parserOptions": {
|
||||
"ecmaVersion": 2018
|
||||
},
|
||||
"extends": ["eslint:recommended", "prettier"],
|
||||
"env": {
|
||||
"es6": true,
|
||||
"node": true,
|
||||
"mocha": true
|
||||
},
|
||||
"rules": {
|
||||
"no-console": "warn"
|
||||
}
|
||||
}
|
3
.gitignore
vendored
Normal file
3
.gitignore
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
/node_modules/
|
||||
.env
|
||||
|
5
.prettierrc.json
Normal file
5
.prettierrc.json
Normal file
@ -0,0 +1,5 @@
|
||||
{
|
||||
"tabWidth": 2,
|
||||
"semi": true,
|
||||
"singleQuote": true
|
||||
}
|
13
config/index.js
Normal file
13
config/index.js
Normal file
@ -0,0 +1,13 @@
|
||||
require('dotenv').config();
|
||||
|
||||
const config = {
|
||||
dev: process.env.NODE_ENV !== 'production',
|
||||
port: process.env.PORT || 3030,
|
||||
CORS: process.env.CORS,
|
||||
dbUser: process.env.DB_USER,
|
||||
dbPassword: process.env.DB_PASSWORD,
|
||||
dbHost: process.env.DB_HOST,
|
||||
dbName: process.env.DB_NAME,
|
||||
}
|
||||
|
||||
module.exports = { config }
|
22
index.js
Normal file
22
index.js
Normal file
@ -0,0 +1,22 @@
|
||||
const app = require('express')();
|
||||
const bodyParser = require('body-parser');
|
||||
const multer = require('multer');
|
||||
const upload = multer();
|
||||
const { config } = require('./config/index');
|
||||
const { logErrors, wrapErrors, errorHandler } = require('./utils/middleware/errorHandlers');
|
||||
const notFoundHandler = require('./utils/middleware/notFoundHandler');
|
||||
const moviesApi = require('./routes/movies.js');
|
||||
|
||||
app.use(bodyParser.json());
|
||||
app.use(bodyParser.urlencoded({ extended: true }));
|
||||
|
||||
moviesApi(app);
|
||||
app.use(notFoundHandler);
|
||||
|
||||
app.use(logErrors);
|
||||
app.use(wrapErrors);
|
||||
app.use(errorHandler);
|
||||
|
||||
app.listen(config.port, () => {
|
||||
console.log(`listening http://localhost:${config.port}`);
|
||||
});
|
71
lib/mongo.js
Normal file
71
lib/mongo.js
Normal file
@ -0,0 +1,71 @@
|
||||
const { MongoClient, ObjectId } = require('mongodb');
|
||||
const { config } = require('../config');
|
||||
|
||||
const USER = encodeURIComponent(config.dbUser);
|
||||
const PASSWORD = encodeURIComponent(config.dbPassword);
|
||||
const DB_NAME = config.dbName;
|
||||
const HOST = config.dbHost;
|
||||
const PORT = config.dbPort;
|
||||
|
||||
// const MONGO_URI = `mongodb+srv://${USER}:${PASSWORD}@${HOST}/${DB_NAME}?retryWrites=true&w=majority`
|
||||
const MONGO_URI = `mongodb://127.0.0.1:27017/${DB_NAME}`
|
||||
|
||||
class MongoLib {
|
||||
constructor() {
|
||||
this.client = new MongoClient(MONGO_URI, { useNewUrlParser: true });
|
||||
this.dbName = DB_NAME;
|
||||
}
|
||||
|
||||
connect() {
|
||||
if (MongoLib.connection) return MongoLib.connection;
|
||||
MongoLib.connection = new Promise((resolve, reject) => {
|
||||
this.client.connect(err => {
|
||||
if (err) {
|
||||
reject(err);
|
||||
}
|
||||
console.log('MongoDB connected');
|
||||
resolve(this.client.db(this.dbName))
|
||||
});
|
||||
});
|
||||
return MongoLib.connection;
|
||||
}
|
||||
|
||||
getAll(collection, query) {
|
||||
return this.connect().then(db => {
|
||||
return db.collection(collection).find(query).toArray();
|
||||
});
|
||||
}
|
||||
|
||||
get(collection, id) {
|
||||
return this.connect().then(db => {
|
||||
return db.collection(collection).findOne({_id: ObjectId(id)});
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
create(collection, data) {
|
||||
return this.connect()
|
||||
.then(db => {
|
||||
return db.collection(collection).insertOne(data);
|
||||
})
|
||||
.then(result => result.insertedId);
|
||||
}
|
||||
|
||||
update(collection, id, data) {
|
||||
return this.connect()
|
||||
.then(db => {
|
||||
return db.collection(collection).updateOne({_id: ObjectId(id) }, { $set: data });
|
||||
})
|
||||
.then(result => result.upsertedId || id);
|
||||
}
|
||||
|
||||
delete(collection, id) {
|
||||
return this.connect()
|
||||
.then(db => {
|
||||
return db.collection(collection).deleteOne({_id: ObjectId(id)});
|
||||
})
|
||||
.then(() => id);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = MongoLib;
|
7543
package-lock.json
generated
Normal file
7543
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
34
package.json
Normal file
34
package.json
Normal file
@ -0,0 +1,34 @@
|
||||
{
|
||||
"name": "movies-api",
|
||||
"version": "1.0.0",
|
||||
"description": "",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"test": "mocha --exit",
|
||||
"dev": "DEBUG=app:* nodemon index",
|
||||
"start": "NODE_ENV=production node index"
|
||||
},
|
||||
"keywords": [],
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"devDependencies": {
|
||||
"eslint": "^7.32.0",
|
||||
"eslint-config-prettier": "^8.3.0",
|
||||
"eslint-plugin-prettier": "^4.0.0",
|
||||
"mocha": "^9.1.1",
|
||||
"multer": "^1.4.3",
|
||||
"nodemon": "^2.0.12",
|
||||
"prettier": "^2.4.0",
|
||||
"proxyquire": "^2.1.3",
|
||||
"sinon": "^11.1.2",
|
||||
"supertest": "^6.1.6"
|
||||
},
|
||||
"dependencies": {
|
||||
"@hapi/boom": "^9.1.4",
|
||||
"@hapi/joi": "^17.1.1",
|
||||
"body-parser": "^1.19.0",
|
||||
"dotenv": "^10.0.0",
|
||||
"express": "^4.17.1",
|
||||
"mongodb": "^4.1.2"
|
||||
}
|
||||
}
|
87
routes/movies.js
Normal file
87
routes/movies.js
Normal file
@ -0,0 +1,87 @@
|
||||
const express = require('express');
|
||||
const { moviesMock } = require('../utils/mocks/movies');
|
||||
const MoviesService = require('../services/movies');
|
||||
const {
|
||||
movieIdSchema,
|
||||
createMovieSchema,
|
||||
updateMovieSchema,
|
||||
} = require('../utils/schemas/movies');
|
||||
|
||||
const validationHandler = require('../utils/middleware/validationHandler');
|
||||
|
||||
function moviesApi(app) {
|
||||
const router = express.Router();
|
||||
app.use('/api/movies', router);
|
||||
|
||||
const moviesService = new MoviesService();
|
||||
|
||||
router.get('/', async (req, res, next) => {
|
||||
const { tags } = req.query;
|
||||
try {
|
||||
const movies = await moviesService.list({ tags });
|
||||
res.status(200).json({
|
||||
data: movies,
|
||||
message: 'movies listed',
|
||||
});
|
||||
} catch (err) {
|
||||
next(err);
|
||||
}
|
||||
});
|
||||
|
||||
router.get('/:id', validationHandler({id:movieIdSchema}, 'params'), async (req, res, next) => {
|
||||
const { id } = req.params;
|
||||
try {
|
||||
const movie = await moviesService.get(id);
|
||||
res.status(200).json({
|
||||
data: movie,
|
||||
message: 'Movie retrieved',
|
||||
});
|
||||
} catch (err) {
|
||||
next(err);
|
||||
}
|
||||
});
|
||||
|
||||
router.post('/', validationHandler(createMovieSchema, 'body'), async (req, res, next) => {
|
||||
const { body: movie } = req;
|
||||
try {
|
||||
const createdMovie = await moviesService.create(movie);
|
||||
res.status(201).json({
|
||||
data: createdMovie,
|
||||
message: 'Movie created',
|
||||
});
|
||||
} catch (err) {
|
||||
next(err);
|
||||
}
|
||||
});
|
||||
|
||||
router.put(
|
||||
'/:id', validationHandler({id:movieIdSchema}, 'params'), validationHandler(updateMovieSchema, 'body'),
|
||||
async (req, res, next) => {
|
||||
const { body: movie } = req;
|
||||
const { id } = req.params;
|
||||
try {
|
||||
const updatedMovie = await moviesService.update(id, movie);
|
||||
res.status(200).json({
|
||||
data: updatedMovie,
|
||||
message: 'Movie updated',
|
||||
});
|
||||
} catch (err) {
|
||||
next(err);
|
||||
}
|
||||
});
|
||||
|
||||
router.delete('/:id', validationHandler({id:movieIdSchema}, 'params'), async (req, res, next) => {
|
||||
const { id } = req.params;
|
||||
try {
|
||||
const movie = await moviesService.delete(id);
|
||||
res.status(200).json({
|
||||
data: id,
|
||||
message: 'Movie deleted',
|
||||
});
|
||||
} catch (err) {
|
||||
next(err);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = moviesApi;
|
33
services/movies.js
Normal file
33
services/movies.js
Normal file
@ -0,0 +1,33 @@
|
||||
const MongoLib = require('../lib/mongo');
|
||||
|
||||
class MoviesService {
|
||||
constructor() {
|
||||
this.collection = 'movies';
|
||||
this.mongoDB = new MongoLib();
|
||||
}
|
||||
|
||||
async list({ tags }) {
|
||||
const query = tags && { tags: { $in: tags }};
|
||||
const movies = await this.mongoDB.getAll(this.collection, query);
|
||||
return movies || [];
|
||||
}
|
||||
|
||||
async get(id) {
|
||||
const movie = await this.mongoDB.get(this.collection, id);
|
||||
return movie || {};
|
||||
}
|
||||
|
||||
async create(movie) {
|
||||
return await this.mongoDB.create(this.collection, movie);
|
||||
}
|
||||
|
||||
async update(id, movie = {}) {
|
||||
return await this.mongoDB.update(this.collection, id, movie);
|
||||
}
|
||||
|
||||
async delete(id) {
|
||||
return await this.mongoDB.delete(this.collection, id);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = MoviesService;
|
28
test/route.movies.test.js
Normal file
28
test/route.movies.test.js
Normal file
@ -0,0 +1,28 @@
|
||||
const assert = require('assert');
|
||||
const proxyquire = require('proxyquire');
|
||||
|
||||
const {moviesMock, MoviesServiceMock } = require('../utils/mocks/movies.js')
|
||||
const testServer = require('../utils/testServer');
|
||||
|
||||
describe('routes - movies', function() {
|
||||
const route = proxyquire('../routes/movies', {
|
||||
'../services/movies': MoviesServiceMock,
|
||||
});
|
||||
const request = testServer(route);
|
||||
|
||||
describe('GET /movies', function() {
|
||||
it('should respond with status 200', function(done) {
|
||||
request.get('/api/movies').expect(200, done);
|
||||
});
|
||||
it('should respond with the list of movies', function(done) {
|
||||
request.get('/api/movies').end((err, res) => {
|
||||
assert.deepEqual(res.body, {
|
||||
data: moviesMock,
|
||||
message: 'movies listed'
|
||||
});
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
})
|
||||
});
|
27
test/services.movies.test.js
Normal file
27
test/services.movies.test.js
Normal file
@ -0,0 +1,27 @@
|
||||
const assert = require('assert');
|
||||
const proxyquire = require('proxyquire');
|
||||
|
||||
const { MongoLibMock, getAllStub } = require('../utils/mocks/mongoLib');
|
||||
|
||||
const { moviesMock } = require('../utils/mocks/movies');
|
||||
|
||||
describe('services - movies', function() {
|
||||
const MoviesServices = proxyquire('../services/movies', {
|
||||
'../lib/mongo': MongoLibMock
|
||||
});
|
||||
|
||||
const moviesService = new MoviesServices();
|
||||
|
||||
describe('when getMovies method is called', async function() {
|
||||
it('should call the getall MongoLib method', async function() {
|
||||
await moviesService.list({});
|
||||
assert.strictEqual(getAllStub.called, true);
|
||||
});
|
||||
|
||||
it('should return an array of movies', async function() {
|
||||
const result = await moviesService.list({});
|
||||
const expected = moviesMock;
|
||||
assert.deepEqual(result, expected);
|
||||
});
|
||||
});
|
||||
});
|
38
utils/middleware/errorHandlers.js
Normal file
38
utils/middleware/errorHandlers.js
Normal file
@ -0,0 +1,38 @@
|
||||
const boom = require('@hapi/boom');
|
||||
const { config } = require('../../config');
|
||||
|
||||
function withErrorStack(error, stack) {
|
||||
if (config.dev) {
|
||||
return { ...error, stack }
|
||||
}
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
function wrapErrors(err, req, res, next) {
|
||||
if (!err.isBoom) {
|
||||
next(boom.badImplementation(err));
|
||||
}
|
||||
next(err);
|
||||
}
|
||||
|
||||
function logErrors(err, req, res, next) {
|
||||
console.log(err);
|
||||
next(err);
|
||||
}
|
||||
|
||||
function errorHandler(err, req, res, next) {
|
||||
const {
|
||||
output: {
|
||||
statusCode, payload
|
||||
}
|
||||
} = err;
|
||||
res.status(statusCode || 500);
|
||||
res.json(withErrorStack(payload, err.stack));
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
logErrors,
|
||||
wrapErrors,
|
||||
errorHandler
|
||||
}
|
13
utils/middleware/notFoundHandler.js
Normal file
13
utils/middleware/notFoundHandler.js
Normal file
@ -0,0 +1,13 @@
|
||||
const boom = require('@hapi/boom');
|
||||
|
||||
function notFoundHandler(req, res) {
|
||||
const {
|
||||
output: {
|
||||
statusCode,
|
||||
payload,
|
||||
}
|
||||
} = boom.notFound();
|
||||
res.status(statusCode).json(payload);
|
||||
}
|
||||
|
||||
module.exports = notFoundHandler;
|
16
utils/middleware/validationHandler.js
Normal file
16
utils/middleware/validationHandler.js
Normal file
@ -0,0 +1,16 @@
|
||||
const boom = require('@hapi/boom');
|
||||
const joi = require('@hapi/joi');
|
||||
|
||||
function validate(data, schema) {
|
||||
const { error } = joi.object(schema).validate(data);
|
||||
return error;
|
||||
}
|
||||
|
||||
function validationHandler(schema, check = "") {
|
||||
return function(req, res, next) {
|
||||
const error = validate(req[check], schema);
|
||||
error ? next(boom.badRequest(error)) : next();
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = validationHandler;
|
27
utils/mocks/mongoLib.js
Normal file
27
utils/mocks/mongoLib.js
Normal file
@ -0,0 +1,27 @@
|
||||
const sinon = require('sinon');
|
||||
|
||||
const { moviesMock, filteredMoviesMock } = require('./movies');
|
||||
|
||||
const getAllStub = sinon.stub();
|
||||
getAllStub.withArgs('movies').resolves(moviesMock);
|
||||
|
||||
const tagQuery = { tags: { $in: ['Drama'] } };
|
||||
getAllStub.withArgs('movies', tagQuery).resolves(filteredMoviesMock('Drama'));
|
||||
|
||||
const createStub = sinon.stub().resolves(moviesMock[0].id);
|
||||
|
||||
class MongoLibMock {
|
||||
list(collection, query) {
|
||||
return getAllStub(collection, query);
|
||||
}
|
||||
|
||||
create(collection, data) {
|
||||
return createStub(collection, data);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
getAllStub,
|
||||
createStub,
|
||||
MongoLibMock
|
||||
};
|
161
utils/mocks/movies.js
Normal file
161
utils/mocks/movies.js
Normal file
@ -0,0 +1,161 @@
|
||||
const moviesMock = [
|
||||
{
|
||||
"id": "94156f00-db7d-42e3-b5eb-f26e256fcae1",
|
||||
"title": "CQ",
|
||||
"year": 2001,
|
||||
"cover": "http://dummyimage.com/130x249.png/ff4444/ffffff",
|
||||
"description": "Duis consequat dui nec nisi volutpat eleifend. Donec ut dolor. Morbi vel lectus in quam fringilla rhoncus.",
|
||||
"duration": 1922,
|
||||
"contentRating": "PG-13",
|
||||
"source": "https://cbsnews.com/eleifend/pede.png",
|
||||
"tags": [
|
||||
"Drama|Romance"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "ce16b2c2-1dd0-4e69-8176-09040f11cc2d",
|
||||
"title": "Spanish Earth, The",
|
||||
"year": 2012,
|
||||
"cover": "http://dummyimage.com/143x154.jpg/dddddd/000000",
|
||||
"description": "Etiam vel augue. Vestibulum rutrum rutrum neque. Aenean auctor gravida sem. Praesent id massa id nisl venenatis lacinia. Aenean sit amet justo. Morbi ut odio.",
|
||||
"duration": 1987,
|
||||
"contentRating": "PG-13",
|
||||
"source": "http://census.gov/volutpat.jsp",
|
||||
"tags": [
|
||||
"Horror",
|
||||
"Documentary",
|
||||
"Drama|Romance"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "0c008a69-b8bb-4c96-97b3-2031ad0fc758",
|
||||
"title": "Mean Streets",
|
||||
"year": 1996,
|
||||
"cover": "http://dummyimage.com/176x226.bmp/dddddd/000000",
|
||||
"description": "Aenean lectus. Pellentesque eget nunc. Donec quis orci eget orci vehicula condimentum.",
|
||||
"duration": 1947,
|
||||
"contentRating": "PG",
|
||||
"source": "http://auda.org.au/sapien/sapien/non/mi.png",
|
||||
"tags": [
|
||||
"Comedy|Drama"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "38436953-f828-4000-a1e3-5ed36c633ff2",
|
||||
"title": "Drop Dead Gorgeous",
|
||||
"year": 1968,
|
||||
"cover": "http://dummyimage.com/170x206.jpg/cc0000/ffffff",
|
||||
"description": "Suspendisse potenti. In eleifend quam a odio. In hac habitasse platea dictumst. Maecenas ut massa quis augue luctus tincidunt. Nulla mollis molestie lorem. Quisque ut erat. Curabitur gravida nisi at nibh. In hac habitasse platea dictumst. Aliquam augue quam, sollicitudin vitae, consectetuer eget, rutrum at, lorem.",
|
||||
"duration": 1955,
|
||||
"contentRating": "R",
|
||||
"source": "http://prnewswire.com/aliquam/erat.html",
|
||||
"tags": [
|
||||
"Thriller"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "8ab2f271-5567-4d78-9fed-88ef660451fe",
|
||||
"title": "War and Peace (Voyna i mir)",
|
||||
"year": 2003,
|
||||
"cover": "http://dummyimage.com/240x228.png/ff4444/ffffff",
|
||||
"description": "Proin interdum mauris non ligula pellentesque ultrices. Phasellus id sapien in sapien iaculis congue. Vivamus metus arcu, adipiscing molestie, hendrerit at, vulputate vitae, nisl. Aenean lectus. Pellentesque eget nunc. Donec quis orci eget orci vehicula condimentum. Curabitur in libero ut massa volutpat convallis. Morbi odio odio, elementum eu, interdum eu, tincidunt in, leo. Maecenas pulvinar lobortis est.",
|
||||
"duration": 1953,
|
||||
"contentRating": "G",
|
||||
"source": "https://fda.gov/non/velit/nec/nisi/vulputate/nonummy/maecenas.xml",
|
||||
"tags": [
|
||||
"Crime|Drama|Fantasy|Film-Noir|Mystery|Romance",
|
||||
"Action|Comedy|Crime|Drama"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "1dc54123-8186-4cb9-adbb-a145e235871d",
|
||||
"title": "Thérèse",
|
||||
"year": 2009,
|
||||
"cover": "http://dummyimage.com/173x237.jpg/cc0000/ffffff",
|
||||
"description": "Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Proin risus. Praesent lectus.",
|
||||
"duration": 1912,
|
||||
"contentRating": "NC-17",
|
||||
"source": "https://1688.com/varius/ut.html",
|
||||
"tags": [
|
||||
"Drama|Film-Noir",
|
||||
"Comedy|Romance",
|
||||
"Action|Sci-Fi",
|
||||
"Comedy|Drama",
|
||||
"Comedy|Drama|Romance"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "1cdb6171-8d12-4aea-bd86-c181d6e8cc42",
|
||||
"title": "Hunky Dory",
|
||||
"year": 2012,
|
||||
"cover": "http://dummyimage.com/205x226.bmp/5fa2dd/ffffff",
|
||||
"description": "Sed sagittis. Nam congue, risus semper porta volutpat, quam pede lobortis ligula, sit amet eleifend pede libero quis orci. Nullam molestie nibh in lectus. Pellentesque at nulla. Suspendisse potenti. Cras in purus eu magna vulputate luctus.",
|
||||
"duration": 1968,
|
||||
"contentRating": "NC-17",
|
||||
"source": "http://amazonaws.com/augue/aliquam/erat/volutpat/in/congue/etiam.png",
|
||||
"tags": [
|
||||
"Action|Thriller",
|
||||
"Action|Adventure",
|
||||
"Drama|Romance|Sci-Fi"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "4c1c536a-f323-4feb-80c1-ca93b896ae70",
|
||||
"title": "Five Easy Pieces",
|
||||
"year": 1997,
|
||||
"cover": "http://dummyimage.com/153x246.png/cc0000/ffffff",
|
||||
"description": "Proin eu mi. Nulla ac enim. In tempor, turpis nec euismod scelerisque, quam turpis adipiscing lorem, vitae mattis nibh ligula nec sem.",
|
||||
"duration": 2039,
|
||||
"contentRating": "PG",
|
||||
"source": "http://nhs.uk/maecenas/leo/odio/condimentum.jsp",
|
||||
"tags": [
|
||||
"Adventure|Animation|Children|Drama|Fantasy"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "a8c6c9fc-0107-494f-83ad-9ceadb11dc35",
|
||||
"title": "Other, The",
|
||||
"year": 1994,
|
||||
"cover": "http://dummyimage.com/242x134.jpg/ff4444/ffffff",
|
||||
"description": "Praesent id massa id nisl venenatis lacinia. Aenean sit amet justo. Morbi ut odio.",
|
||||
"duration": 1935,
|
||||
"contentRating": "G",
|
||||
"source": "https://nba.com/sit.png",
|
||||
"tags": [
|
||||
"Crime|Drama|Mystery|Thriller"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "5d448927-7d8e-49a7-a3bc-280938a05581",
|
||||
"title": "Todos eran culpables",
|
||||
"year": 1995,
|
||||
"cover": "http://dummyimage.com/167x148.jpg/ff4444/ffffff",
|
||||
"description": "Duis bibendum. Morbi non quam nec dui luctus rutrum. Nulla tellus.",
|
||||
"duration": 1901,
|
||||
"contentRating": "PG-13",
|
||||
"source": "http://google.cn/at/turpis/donec/posuere/metus/vitae/ipsum.jsp",
|
||||
"tags": [
|
||||
"Action|Adventure|Drama"
|
||||
]
|
||||
}
|
||||
];
|
||||
|
||||
function filteredMoviesMock(tag) {
|
||||
return moviesMock.filter(movie => movie.tags.includes(tag));
|
||||
}
|
||||
|
||||
class MoviesServiceMock {
|
||||
async list() {
|
||||
return Promise.resolve(moviesMock);
|
||||
}
|
||||
|
||||
async create() {
|
||||
return Promise.resolve(moviesMock[0]);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
moviesMock,
|
||||
filteredMoviesMock,
|
||||
MoviesServiceMock,
|
||||
}
|
39
utils/schemas/movies.js
Normal file
39
utils/schemas/movies.js
Normal file
@ -0,0 +1,39 @@
|
||||
const joi = require('@hapi/joi');
|
||||
|
||||
const movieIdSchema = joi.string().regex(/^[0-9a-fA-F]{24}$/);
|
||||
const movieTitleSchema = joi.string().max(80);
|
||||
const movieYearSchema = joi.number().min(1888).max(2077);
|
||||
const movieCoverSchema = joi.string().uri();
|
||||
const movieDescriptionSchema = joi.string().max(300);
|
||||
const movieDurationSchema = joi.number().min(1).max(300);
|
||||
const movieContentRatingSchema = joi.string().max(5);
|
||||
const movieSourcesSchema = joi.string().uri();
|
||||
const movieTagsSchema = joi.array().items(joi.string().max(50));
|
||||
|
||||
const createMovieSchema = {
|
||||
title: movieTitleSchema.required(),
|
||||
year: movieYearSchema.required(),
|
||||
cover: movieCoverSchema.required(),
|
||||
description: movieDescriptionSchema.required(),
|
||||
duration: movieDurationSchema.required(),
|
||||
contentRating: movieContentRatingSchema.required(),
|
||||
source: movieSourcesSchema.required(),
|
||||
tags: movieTagsSchema,
|
||||
};
|
||||
|
||||
const updateMovieSchema = {
|
||||
title: movieTitleSchema,
|
||||
year: movieYearSchema,
|
||||
cover: movieCoverSchema,
|
||||
description: movieDescriptionSchema,
|
||||
duration: movieDurationSchema,
|
||||
contentRating: movieContentRatingSchema,
|
||||
source: movieSourcesSchema,
|
||||
tags: movieTagsSchema,
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
movieIdSchema,
|
||||
createMovieSchema,
|
||||
updateMovieSchema,
|
||||
}
|
10
utils/testServer.js
Normal file
10
utils/testServer.js
Normal file
@ -0,0 +1,10 @@
|
||||
const express = require('express');
|
||||
const supertest = require('supertest');
|
||||
|
||||
function testServer(route) {
|
||||
const app = express();
|
||||
route(app);
|
||||
return supertest(app);
|
||||
}
|
||||
|
||||
module.exports = testServer;
|
Loading…
Reference in New Issue
Block a user