Skip to content

Commit

Permalink
Agrega funcionalidad para subir un escudo
Browse files Browse the repository at this point in the history
  • Loading branch information
fsodano committed Jul 26, 2020
1 parent dd2ab0c commit cd74c73
Show file tree
Hide file tree
Showing 11 changed files with 174 additions and 49 deletions.
1 change: 1 addition & 0 deletions .env.dist
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@ API_FOOTBALL_DATA=
SESSION_DB_PATH=./data/session.db
SESSION_SECRET=secreto por default (que puede tener espacios y caracteres no alfanuméricos)
NODE_ENV=development
CRESTS_UPLOAD_DIR=./public/img/crests
117 changes: 100 additions & 17 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
"homepage": "https://github.com/r-argentina-programa/crud-clubes#readme",
"devDependencies": {
"@types/jest": "^26.0.5",
"cypress": "^4.9.0",
"cypress": "^4.11.0",
"eslint": "^7.2.0",
"eslint-config-airbnb-base": "^14.2.0",
"eslint-config-prettier": "^6.11.0",
Expand All @@ -39,6 +39,7 @@
"dotenv": "^8.2.0",
"express": "^4.17.1",
"express-session": "^1.17.1",
"multer": "^1.4.2",
"nunjucks": "^3.2.1",
"rsdi": "^1.0.5",
"sequelize": "^6.3.3",
Expand Down
2 changes: 1 addition & 1 deletion src/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ const app = express();
const port = process.env.PORT || 3000;

app.use(express.urlencoded({ extended: true }));
app.use(express.static('public'));
app.use('/public', express.static('public'));

// https://mozilla.github.io/nunjucks/getting-started.html#when-using-node
nunjucks.configure('src/module', {
Expand Down
31 changes: 26 additions & 5 deletions src/config/di.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
// configure DI container
const path = require('path');
const { default: DIContainer, object, get, factory } = require('rsdi');
const { Sequelize } = require('sequelize');
const multer = require('multer');

const session = require('express-session');
const SequelizeStore = require('connect-session-sequelize')(session.Store);
const { ClubController, ClubService, ClubRepository, ClubModel } = require('../module/club/module');
Expand All @@ -22,8 +25,11 @@ function configureSessionSequelizeDatabase() {
return sequelize;
}

function configureClubModule(di) {
return ClubModel.setup(di.get('Sequelize'));
/**
* @param {DIContainer} container
*/
function configureClubModule(container) {
return ClubModel.setup(container.get('Sequelize'));
}

/**
Expand All @@ -45,17 +51,32 @@ function configureSession(container) {
return session(sessionOptions);
}

module.exports = function configureDI(app) {
function configureMulter() {
const storage = multer.diskStorage({
destination(req, file, cb) {
cb(null, process.env.CRESTS_UPLOAD_DIR);
},
filename(req, file, cb) {
// https://stackoverflow.com/questions/31592726/how-to-store-a-file-with-file-extension-with-multer
// al tener una extensión, el navegador lo sirve en vez de descargarlo
cb(null, Date.now() + path.extname(file.originalname));
},
});

return multer({ storage });
}

module.exports = function configureDI() {
const container = new DIContainer();
container.addDefinitions({
ClubController: object(ClubController).construct(get('ClubService')),
ClubController: object(ClubController).construct(get('Multer'), get('ClubService')),
ClubService: object(ClubService).construct(get('ClubRepository')),
ClubRepository: object(ClubRepository).construct(get('ClubModel')),
ClubModel: factory(configureClubModule),
Sequelize: factory(configureMainSequelizeDatabase),
SessionSequelize: factory(configureSessionSequelizeDatabase),
Session: factory(configureSession),
App: app,
Multer: factory(configureMulter),
});
return container;
};
10 changes: 7 additions & 3 deletions src/module/club/controller/__tests__/club.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ const serviceMock = {
getById: jest.fn(() => Promise.resolve({})),
getAll: jest.fn(() => Promise.resolve([])),
};
const controller = new ClubController(serviceMock);
const controller = new ClubController({}, serviceMock);

test('Index renderea index.html', async () => {
const renderMock = jest.fn();
Expand All @@ -33,9 +33,13 @@ test('Create renderea form.html', () => {

test('Save llama al servicio con el body y redirecciona a /club', async () => {
const redirectMock = jest.fn();
const bodyMock = { id: 1 };
const FAKE_CREST_URL = 'ejemplo/escudo.png';
const bodyMock = { id: 1, crestUrl: FAKE_CREST_URL };

await controller.save({ body: bodyMock, session: {} }, { redirect: redirectMock });
await controller.save(
{ body: bodyMock, file: { path: FAKE_CREST_URL }, session: {} },
{ redirect: redirectMock }
);

expect(serviceMock.save).toHaveBeenCalledTimes(1);
expect(serviceMock.save).toHaveBeenCalledWith(bodyMock);
Expand Down
23 changes: 21 additions & 2 deletions src/module/club/controller/clubController.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,28 @@ module.exports = class ClubController extends AbstractController {
/**
* @param {import('../service/clubService')} clubService
*/
constructor(clubService) {
constructor(uploadMiddleware, clubService) {
super();
this.uploadMiddleware = uploadMiddleware;
this.clubService = clubService;
}

/**
* @param {import('express').Application} app
*/
configureRoutes(app) {
const ROUTE = '/club';

// Nota: el `bind` es necesario porque estamos atando el callback a una función miembro de esta clase
// y no a la clase en si.
// Al hacer `bind` nos aseguramos que "this" dentro de `create` sea el controlador.
app.get(`${ROUTE}/create`, this.create.bind(this));
app.get(`${ROUTE}`, this.index.bind(this));
app.get(`${ROUTE}/view/:id`, this.view.bind(this));
app.post(`${ROUTE}/save`, this.uploadMiddleware.single('crest-url'), this.save.bind(this));
app.get(`${ROUTE}/delete/:id`, this.delete.bind(this));
}

/**
* @param {import('express').Request} req
* @param {import('express').Response} res
Expand All @@ -35,7 +52,7 @@ module.exports = class ClubController extends AbstractController {
* @param {import('express').Request} req
* @param {import('express').Response} res
*/
async edit(req, res) {
async view(req, res) {
const { id } = req.params;
if (!id) {
throw new ClubIdNotDefinedError();
Expand All @@ -57,6 +74,8 @@ module.exports = class ClubController extends AbstractController {
async save(req, res) {
try {
const club = fromDataToEntity(req.body);
const { path } = req.file;
club.crestUrl = path;
const savedClub = await this.clubService.save(club);
if (club.id) {
req.session.messages = [`El club con id ${club.id} se actualizó exitosamente`];
Expand Down
12 changes: 10 additions & 2 deletions src/module/club/module.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,19 @@
const configureRoutes = require('./routes');
const ClubController = require('./controller/clubController');
const ClubRepository = require('./repository/sqlite/clubRepository');
const ClubService = require('./service/clubService');
const ClubModel = require('./repository/sqlite/clubModel');

/**
* @param {import('express').Application} app
* @param {import('rsdi').IDIContainer} container
*/
function init(app, container) {
configureRoutes(container.get('ClubController'), app);
/**
* @type {ClubController} controller;
*/

const controller = container.get('ClubController');
controller.configureRoutes(app);
}

module.exports = {
Expand Down
Loading

0 comments on commit cd74c73

Please sign in to comment.