Skip to content

Commit

Permalink
Agrega areas a clubes
Browse files Browse the repository at this point in the history
  • Loading branch information
fsodano committed Jul 27, 2020
1 parent 39b186f commit 7a17a1e
Show file tree
Hide file tree
Showing 11 changed files with 117 additions and 23 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -102,3 +102,5 @@ dist

# TernJS port file
.tern-port

.vscode
1 change: 1 addition & 0 deletions src/cli/init.db.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ const container = configureDependencyInjection(app);
* @type {import('sequelize').Sequelize} mainDb
*/
const mainDb = container.get('Sequelize');

container.get('AreaModel');
container.get('ClubModel');

Expand Down
6 changes: 6 additions & 0 deletions src/config/associations.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
/**
* @param {import('sequelize').Sequelize} sequelizeInstance
*/
module.exports = async function setupAssociations(ClubModel, AreaModel) {
ClubModel.belongsTo(AreaModel, { foreignKey: 'area_id' });
};
15 changes: 10 additions & 5 deletions src/config/di.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ const SequelizeStore = require('connect-session-sequelize')(session.Store);
const { ClubController, ClubService, ClubRepository, ClubModel } = require('../module/club/module');
const { AreaController, AreaService, AreaRepository, AreaModel } = require('../module/area/module');

const setupSequelizeModelAssociations = require('./associations');

function configureMainSequelizeDatabase() {
const sequelize = new Sequelize({
dialect: 'sqlite',
Expand Down Expand Up @@ -53,8 +55,6 @@ function configureSession(container) {
saveUninitialized: false,
cookie: { maxAge: ONE_WEEK_IN_SECONDS },
};

sequelize.sync();
return session(sessionOptions);
}

Expand Down Expand Up @@ -90,9 +90,13 @@ function addCommonDefinitions(container) {
*/
function addClubModuleDefinitions(container) {
container.addDefinitions({
ClubController: object(ClubController).construct(get('Multer'), get('ClubService')),
ClubController: object(ClubController).construct(
get('Multer'),
get('ClubService'),
get('AreaService')
),
ClubService: object(ClubService).construct(get('ClubRepository')),
ClubRepository: object(ClubRepository).construct(get('ClubModel')),
ClubRepository: object(ClubRepository).construct(get('ClubModel'), get('AreaModel')),
ClubModel: factory(configureClubModel),
});
}
Expand All @@ -112,7 +116,8 @@ function addAreaModuleDefinitions(container) {
module.exports = function configureDI() {
const container = new DIContainer();
addCommonDefinitions(container);
addClubModuleDefinitions(container);
addAreaModuleDefinitions(container);
addClubModuleDefinitions(container);
setupSequelizeModelAssociations(container.get('ClubModel'), container.get('AreaModel'));
return container;
};
41 changes: 35 additions & 6 deletions src/module/club/controller/__tests__/club.test.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
const ClubController = require('../clubController');
const Club = require('../../entity/club');
const Area = require('../../../area/entity/area');

const serviceMock = {
save: jest.fn(),
delete: jest.fn(() => Promise.resolve(true)),
getById: jest.fn(() => Promise.resolve({})),
getAll: jest.fn(() => Promise.resolve([])),
};
const controller = new ClubController({}, serviceMock);
const controller = new ClubController({}, serviceMock, serviceMock);

test('Index renderea index.html', async () => {
const renderMock = jest.fn();
Expand All @@ -22,19 +23,47 @@ test('Index renderea index.html', async () => {
});
});

test('Create renderea form.html', () => {
const renderMock = jest.fn();
test('Create muestra un error si no hay áreas en el sistema', async () => {
const mockRes = { redirect: jest.fn() };
const mockReq = { session: {} };
await controller.create(mockReq, mockRes);
expect(mockRes.redirect).toHaveBeenCalledTimes(1);
expect(mockReq.session.errors).toEqual(['Para crear un club, primero debe crear un área']);
});

controller.create({}, { render: renderMock });
test('Create renderea form.html', async () => {
const renderMock = jest.fn();
const mockAreasData = [new Area({ id: 1, name: 'Argentina' })];
serviceMock.getAll.mockImplementationOnce(() => mockAreasData);
await controller.create({}, { render: renderMock });

expect(renderMock).toHaveBeenCalledTimes(1);
expect(renderMock).toHaveBeenCalledWith('club/view/form.html');
expect(renderMock).toHaveBeenCalledWith('club/view/form.html', {
data: { areas: mockAreasData },
});
});

test('Save llama al servicio con el body y redirecciona a /club', async () => {
const redirectMock = jest.fn();
const FAKE_CREST_URL = 'ejemplo/escudo.png';
const bodyMock = { id: 1, crestUrl: FAKE_CREST_URL };
const bodyMock = new Club({
Area: {
id: NaN,
name: undefined,
},
address: undefined,
clubColors: undefined,
crestUrl: 'ejemplo/escudo.png',
email: undefined,
founded: undefined,
id: 1,
name: undefined,
phone: undefined,
shortName: undefined,
tla: undefined,
venue: undefined,
website: undefined,
});

await controller.save(
{ body: bodyMock, file: { path: FAKE_CREST_URL }, session: {} },
Expand Down
19 changes: 15 additions & 4 deletions src/module/club/controller/clubController.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,21 @@ const AbstractController = require('../../abstractController');
module.exports = class ClubController extends AbstractController {
/**
* @param {import('../service/clubService')} clubService
* @param {import('../../area/service/areaService')} areaService
*/
constructor(uploadMiddleware, clubService) {
constructor(uploadMiddleware, clubService, areaService) {
super();
this.ROUTE_BASE = '/club';
this.uploadMiddleware = uploadMiddleware;
this.clubService = clubService;
this.areaService = areaService;
}

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

// 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.
Expand Down Expand Up @@ -45,7 +48,14 @@ module.exports = class ClubController extends AbstractController {
* @param {import('express').Response} res
*/
async create(req, res) {
res.render('club/view/form.html');
const areas = await this.areaService.getAll();

if (areas.length > 0) {
res.render('club/view/form.html', { data: { areas } });
} else {
req.session.errors = ['Para crear un club, primero debe crear un área'];
res.redirect(this.ROUTE_BASE);
}
}

/**
Expand All @@ -60,7 +70,8 @@ module.exports = class ClubController extends AbstractController {

try {
const club = await this.clubService.getById(id);
res.render('club/view/form.html', { data: { club } });
const areas = await this.areaService.getAll();
res.render('club/view/form.html', { data: { club, areas } });
} catch (e) {
req.session.errors = [e.message];
res.redirect('/club');
Expand Down
5 changes: 5 additions & 0 deletions src/module/club/entity/club.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ module.exports = class Club {
founded,
clubColors,
venue,
Area,
}) {
this.id = id;
this.name = name;
Expand All @@ -25,5 +26,9 @@ module.exports = class Club {
this.founded = founded;
this.clubColors = clubColors;
this.venue = venue;
/**
* @type {import('../../area/entity/area');} this.Area
*/
this.Area = Area;
}
};
4 changes: 4 additions & 0 deletions src/module/club/mapper/clubMapper.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
// map from Model to Entity

const Club = require('../entity/club');
const Area = require('../../area/entity/area');

/**
*
Expand All @@ -23,6 +24,8 @@ function fromDataToEntity({
founded,
'club-colors': clubColors,
venue,
// eslint-disable-next-line camelcase
area_id,
}) {
return new Club({
id: Number(id),
Expand All @@ -37,6 +40,7 @@ function fromDataToEntity({
founded,
clubColors,
venue,
Area: new Area({ id: Number(area_id) }),
});
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
const { Sequelize } = require('sequelize');
const ClubRepository = require('../clubRepository');
const ClubModel = require('../clubModel');
const AreaModel = require('../../../../area/repository/sqlite/areaModel');
const ClubEntity = require('../../../entity/club');
const ClubNotFoundError = require('../../error/clubNotFoundError');
const ClubIdNotDefinedError = require('../../error/clubIdNotDefinedError');
Expand All @@ -25,10 +26,15 @@ const sampleClub = new ClubEntity({
clubColors: 'Red / White',
venue: 'Emirates Stadium',
lastUpdated: '2020-05-14T02:41:34Z',
Area: { id: 1 },
});

beforeAll(() => {
repository = new ClubRepository(ClubModel.setup(sequelizeInstance));
const club = ClubModel.setup(sequelizeInstance);
const area = AreaModel.setup(sequelizeInstance);
club.belongsTo(area);

repository = new ClubRepository(club, area);
});

beforeEach(async (done) => {
Expand All @@ -48,6 +54,7 @@ test('Actualiza un equipo cuando la entidad tiene un id', async () => {
expect(newClub.id).toEqual(NEW_AUTOGENERATED_ID);

newClub.name = 'Independiente';
console.log(newClub);
const modifiedClub = await repository.save(newClub);
expect(modifiedClub.id).toEqual(NEW_AUTOGENERATED_ID);
expect(modifiedClub.name).toEqual('Independiente');
Expand Down
20 changes: 13 additions & 7 deletions src/module/club/repository/sqlite/clubRepository.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,12 @@ const ClubIdNotDefinedError = require('../error/clubIdNotDefinedError');
module.exports = class ClubRepository extends AbstractClubRepository {
/**
* @param {typeof import('./clubModel')} clubModel
* * @param {typeof import('../../../area/repository/sqlite/areaModel')} areaModel
*/
constructor(clubModel) {
constructor(clubModel, areaModel) {
super();
this.clubModel = clubModel;
this.areaModel = areaModel;
}

/**
Expand All @@ -18,11 +20,12 @@ module.exports = class ClubRepository extends AbstractClubRepository {
*/
async save(club) {
let clubModel;
if (!club.id) {
clubModel = await this.clubModel.create(club);
} else {
clubModel = await this.clubModel.build(club, { isNewRecord: false }).save();
}

const buildOptions = { isNewRecord: !club.id, include: this.areaModel };
clubModel = this.clubModel.build(club, buildOptions);
clubModel.setDataValue('area_id', club.Area.id);
clubModel = await clubModel.save();

return fromModelToEntity(clubModel);
}

Expand All @@ -43,7 +46,10 @@ module.exports = class ClubRepository extends AbstractClubRepository {
* @returns {Promise<import('../../entity/club')>}
*/
async getById(id) {
const clubModel = await this.clubModel.findOne({ where: { id } });
const clubModel = await this.clubModel.findOne({
where: { id },
include: this.areaModel,
});

if (!clubModel) {
throw new ClubNotFoundError(`No se encontró club con id ${id}`);
Expand Down
18 changes: 18 additions & 0 deletions src/module/club/view/form.html
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

{% block body %}
{% set club = data.club %}
{% set areas = data.areas %}

<div class="level-left">
<div class="level-item">
Expand Down Expand Up @@ -62,6 +63,23 @@ <h1 class="title is-1">
</div>
</div>

<div class="field">
<label class="label">Área</label>
<div class="control has-icons-left">
<div class="select">
<select name="area_id">
<option {{"selected" if club.id}}>Seleccione</option>
{% for area in areas %}
<option value="{{area.id}}" {{"selected" if area.id == club.Area.id}}>{{area.name}}</option>
{% endfor %}
</select>
</div>
<span class="icon is-small is-left">
<i class="fas fa-location-arrow"></i>
</span>
</div>
</div>

<div class="field">
<label class="label">Dirección</label>
<div class="control has-icons-left">
Expand Down

0 comments on commit 7a17a1e

Please sign in to comment.