Skip to content

Commit 0a26b7d

Browse files
committed
Validate courses request
1 parent d164cc1 commit 0a26b7d

File tree

7 files changed

+116
-17
lines changed

7 files changed

+116
-17
lines changed

package-lock.json

Lines changed: 21 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@
4646
"copy": "^0.3.2",
4747
"errorhandler": "^1.5.1",
4848
"express": "^4.17.1",
49+
"express-validator": "^6.6.1",
4950
"glob": "^7.1.6",
5051
"helmet": "^3.22.0",
5152
"http-status": "^1.4.2",

src/apps/backoffice/frontend/controllers/CoursesGetController.ts

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,19 +3,21 @@ import { Uuid } from '../../../../Contexts/Shared/domain/value-object/Uuid';
33
import { QueryBus } from '../../../../Contexts/Shared/domain/QueryBus';
44
import { FindCoursesCounterResponse } from '../../../../Contexts/Mooc/CoursesCounter/application/Find/FindCoursesCounterResponse';
55
import { FindCoursesCounterQuery } from '../../../../Contexts/Mooc/CoursesCounter/application/Find/FindCoursesCounterQuery';
6+
import { WebController } from './WebController';
67

7-
export class CoursesGetController {
8-
constructor(private queryBus: QueryBus) {}
8+
export class CoursesGetController extends WebController {
9+
constructor(private queryBus: QueryBus) {
10+
super();
11+
}
912

1013
async run(req: Request, res: Response) {
1114
const courses = await this.queryBus.ask<FindCoursesCounterResponse>(new FindCoursesCounterQuery());
1215

13-
res.render('pages/courses/courses', {
16+
this.render(req, res, 'pages/courses/courses', {
1417
title: 'Welcome',
1518
description: 'CodelyTV - Backoffice',
1619
courses_counter: courses.total,
17-
new_course_id: Uuid.random(),
18-
flash: req.flash()
20+
id: Uuid.random().value
1921
});
2022
}
2123
}
Lines changed: 22 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,29 @@
11
import { Request, Response } from 'express';
22
import { CommandBus } from '../../../../Contexts/Shared/domain/CommandBus';
33
import { CreateCourseCommand } from '../../../../Contexts/Mooc/Courses/application/CreateCourseCommand';
4+
import { body, ValidationChain } from 'express-validator';
5+
import { WebController } from './WebController';
46

5-
export class CoursesPostController {
6-
constructor(private commandBus: CommandBus) {}
7+
export class CoursesPostController extends WebController {
8+
constructor(private commandBus: CommandBus) {
9+
super();
10+
}
711

8-
async run(req: Request, res: Response) {
9-
// TODO: validation
10-
// req.flash('errors.id', 'Flash Message Added');
12+
static validator(): ValidationChain[] {
13+
return [
14+
body('id').isUUID(),
15+
body('name').isLength({ min: 1, max: 255 }),
16+
body('duration').isLength({ min: 4, max: 100 })
17+
];
18+
}
1119

12-
await this.createCourse(req, res);
20+
async run(req: Request, res: Response) {
21+
const errors = this.validateRequest(req);
22+
if (errors.length === 0) {
23+
await this.createCourse(req, res);
24+
} else {
25+
this.redirectWithErrors(req, res, errors);
26+
}
1327
}
1428

1529
private async createCourse(req: Request, res: Response) {
@@ -18,9 +32,9 @@ export class CoursesPostController {
1832
name: req.body.name,
1933
duration: req.body.duration
2034
});
35+
2136
await this.commandBus.dispatch(createCourseCommand);
2237

23-
req.flash('message', `Felicidades, el curso ${req.body.name} ha sido creado!`);
24-
res.redirect('/courses');
38+
this.redirectWithMessage(req, res, '/courses', `Felicidades, el curso ${req.body.name} ha sido creado!`);
2539
}
2640
}
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
import { Request, Response } from 'express';
2+
import { validationResult, ValidationError, matchedData } from 'express-validator';
3+
4+
export abstract class WebController {
5+
protected validateRequest(req: Request): Array<ValidationError> {
6+
const errors = validationResult(req);
7+
return errors.array();
8+
}
9+
10+
private setFlashMessage(req: Request, message: { key: string; value: string }) {
11+
req.flash(message.key, message.value);
12+
}
13+
14+
protected redirectWithErrors(req: Request, res: Response, errors: ValidationError[]) {
15+
const validFields = this.getValidFields(req);
16+
17+
errors.forEach(e => {
18+
this.setFlashMessage(req, { key: `errors.${e.param}`, value: e.msg });
19+
});
20+
21+
validFields.forEach(field => {
22+
this.setFlashMessage(req, { key: `inputs.${field.param}`, value: field.value });
23+
});
24+
25+
res.redirect(req.originalUrl);
26+
}
27+
28+
protected redirectWithMessage(req: Request, res: Response, route: string, message: string) {
29+
this.setFlashMessage(req, { key: 'message', value: message });
30+
res.redirect(route);
31+
}
32+
33+
private getValidFields(req: Request): Array<{ value: any; param: string }> {
34+
const validData = matchedData(req, { onlyValidData: false });
35+
return Object.keys(validData).map(key => ({
36+
param: key,
37+
value: validData[key]
38+
}));
39+
}
40+
41+
protected render(req: Request, res: Response, template: string, data: { [key: string]: any }) {
42+
const flash = this.feedFlash(req, data);
43+
res.render('pages/courses/courses', {
44+
...data,
45+
...flash
46+
});
47+
}
48+
49+
private feedFlash(req: Request, data: { [key: string]: any }) {
50+
const rawFlash = req.flash();
51+
const flashResponse = Object.keys(data).reduce((flash, key) => {
52+
flash[`inputs.${key}`] = flash[`inputs.${key}`] || data[key];
53+
return flash;
54+
}, rawFlash);
55+
return {
56+
flash: flashResponse
57+
};
58+
}
59+
60+
abstract run(req: Request, res: Response): Promise<void>;
61+
}

src/apps/backoffice/frontend/routes/courses.route.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,5 +12,5 @@ export const register = (app: Express) => {
1212
);
1313

1414
app.get('/courses', coursesGetController.run.bind(coursesGetController));
15-
app.post('/courses', coursesPostController.run.bind(coursesPostController));
15+
app.post('/courses', CoursesPostController.validator(), coursesPostController.run.bind(coursesPostController));
1616
};

src/apps/backoffice/frontend/templates/pages/courses/partials/new_course_form.html

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ <h2 class="block uppercase tracking-wide text-gray-700 text-3xl font-bold mb-2">
77
</label>
88
<input class="{% if flash['errors.id'] %}border border-red-500{% endif %} appearance-none block w-full bg-gray-200 text-gray-700 border border-gray-200 rounded py-3 px-4 leading-tight focus:outline-none focus:bg-white focus:border-gray-500"
99
id="grid-first-name" type="text" name="id" placeholder="En formado UUID"
10-
value="{{new_course_id}}">
10+
value="{{flash['inputs.id']}}">
1111

1212
{% if flash['errors.id'] %}
1313
<p class="text-red-500 text-xs italic">{{ flash['errors.id'] }}</p>
@@ -21,7 +21,7 @@ <h2 class="block uppercase tracking-wide text-gray-700 text-3xl font-bold mb-2">
2121
</label>
2222
<input class="{% if flash['errors.name'] %}border border-red-500{% endif %} appearance-none block w-full bg-gray-200 text-gray-700 border border-gray-200 rounded py-3 px-4 leading-tight focus:outline-none focus:bg-white focus:border-gray-500"
2323
id="grid-first-name" type="text" name="name" placeholder="DDD en PHP"
24-
value="">
24+
value="{{flash['inputs.name']}}">
2525

2626
{% if flash['errors.name'] %}
2727
<p class="text-red-500 text-xs italic">{{ flash['errors.name'] }}</p>
@@ -33,7 +33,7 @@ <h2 class="block uppercase tracking-wide text-gray-700 text-3xl font-bold mb-2">
3333
</label>
3434
<input class="{% if flash['errors.duration'] %}border border-red-500{% endif %} appearance-none block w-full bg-gray-200 text-gray-700 border border-gray-200 rounded py-3 px-4 leading-tight focus:outline-none focus:bg-white focus:border-gray-500"
3535
id="grid-last-name" type="text" name="duration" placeholder="8 days"
36-
value="xxx">
36+
value="{{flash['inputs.duration']}}">
3737
{% if flash['errors.duration'] %}
3838
<p class="text-red-500 text-xs italic">{{ flash['errors.duration'] }}</p>
3939
{% endif %}

0 commit comments

Comments
 (0)