Skip to content

Commit 4d4a7ab

Browse files
feat(server): create
1 parent 4445476 commit 4d4a7ab

File tree

7 files changed

+163
-1
lines changed

7 files changed

+163
-1
lines changed

config.js

+4
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ function isFeatureEnabled(environmentVariable) {
99
function buildConfiguration() {
1010
const config = {
1111
environment: env.NODE_ENV || 'development',
12+
port: env.PORT || 3000,
1213
logging: {
1314
enabled: isFeatureEnabled(env.LOG_ENABLED),
1415
logLevel: env.LOG_LEVEL || 'info',
@@ -39,6 +40,9 @@ function buildConfiguration() {
3940
url: env.NOTIFICATION_URL,
4041
token: env.NOTIFICATION_TOKEN,
4142
},
43+
calendar: {
44+
name: env.CALENDAR_NAME,
45+
},
4246
timeSlotsPreferences: JSON.parse(env.TIME_SLOTS_PREFERENCES),
4347
};
4448
if (config.environment === 'test') {

index.js

+17
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,17 @@ import { CronJob } from 'cron';
22

33
import { config } from './config.js';
44

5+
import { createServer } from './server.js';
56
import { ReservationController } from './src/application/ReservationController.js';
7+
import { CreateReservationEventsUseCase } from './src/domain/usecases/CreateReservationEventsUseCase.js';
68
import { GetActiveReservationsUseCase } from './src/domain/usecases/GetActiveReservationsUseCase.js';
9+
import { GetAllEventsUseCase } from './src/domain/usecases/GetAllEventsUseCase.js';
710
import { HandleNewReservationUseCase } from './src/domain/usecases/HandleNewReservationUseCase.js';
811
import { HandleScheduledReservationUseCase } from './src/domain/usecases/HandleScheduledReservationUseCase.js';
912
import { NotifyUseCase } from './src/domain/usecases/NotifyUseCase.js';
1013
import { SubmitFormUseCase } from './src/domain/usecases/SubmitFormUseCase.js';
1114
import { Browser } from './src/infrastructure/Browser.js';
15+
import { CalendarRepository } from './src/infrastructure/CalendarRepository.js';
1216
import { ImapClient } from './src/infrastructure/ImapClient.js';
1317
import { logger } from './src/infrastructure/logger.js';
1418
import { NotificationClient } from './src/infrastructure/NotificationClient.js';
@@ -31,6 +35,8 @@ async function main() {
3135
start: true,
3236
timeZone: parisTimezone,
3337
});
38+
const server = await createServer({ reservationController });
39+
await server.start();
3440
}
3541

3642
async function getReservationController() {
@@ -71,12 +77,23 @@ async function getReservationController() {
7177
reservationRepository,
7278
});
7379

80+
const calendarRepository = new CalendarRepository(config.calendar.name);
81+
82+
const createReservationEventsUseCase = new CreateReservationEventsUseCase({
83+
reservationRepository,
84+
calendarRepository,
85+
});
86+
87+
const getAllEventsUseCase = new GetAllEventsUseCase({ calendarRepository });
88+
7489
return new ReservationController({
7590
handleNewReservationUseCase,
7691
getActiveReservationsUseCase,
7792
submitFormUseCase,
7893
notifyUseCase,
7994
handleScheduledReservationUseCase,
95+
createReservationEventsUseCase,
96+
getAllEventsUseCase,
8097
logger,
8198
});
8299
}

server.js

+36
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
import Hapi from '@hapi/hapi';
2+
import { config } from './config.js';
3+
import * as pino from './src/infrastructure/pino.js';
4+
import { routes } from './src/infrastructure/routes.js';
5+
6+
const createBareServer = function () {
7+
const serverConfiguration = {
8+
compression: false,
9+
debug: { request: false, log: false },
10+
routes: {
11+
cors: {
12+
origin: ['*'],
13+
additionalHeaders: ['X-Requested-With'],
14+
},
15+
response: {
16+
emptyStatusCode: 204,
17+
},
18+
},
19+
port: config.port,
20+
router: {
21+
isCaseSensitive: false,
22+
stripTrailingSlash: true,
23+
},
24+
};
25+
26+
return Hapi.server(serverConfiguration);
27+
};
28+
29+
async function createServer(controllers) {
30+
const server = createBareServer();
31+
await server.register([pino]);
32+
routes.map(Route => new Route(controllers).register(server));
33+
return server;
34+
}
35+
36+
export { createServer };

src/application/ReservationController.js

+20-1
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,28 @@
11
export class ReservationController {
2-
constructor({ handleNewReservationUseCase, getActiveReservationsUseCase, submitFormUseCase, notifyUseCase, handleScheduledReservationUseCase, logger }) {
2+
constructor({
3+
handleNewReservationUseCase,
4+
getActiveReservationsUseCase,
5+
submitFormUseCase,
6+
notifyUseCase,
7+
handleScheduledReservationUseCase,
8+
createReservationEventsUseCase,
9+
getAllEventsUseCase,
10+
logger,
11+
}) {
312
this.handleNewReservationUseCase = handleNewReservationUseCase;
413
this.getActiveReservationsUseCase = getActiveReservationsUseCase;
514
this.submitFormUseCase = submitFormUseCase;
615
this.notifyUseCase = notifyUseCase;
716
this.handleScheduledReservationUseCase = handleScheduledReservationUseCase;
17+
this.createReservationEventsUseCase = createReservationEventsUseCase;
18+
this.getAllEventsUseCase = getAllEventsUseCase;
819
this.logger = logger;
920
}
1021

22+
async getCalendar() {
23+
return this.getAllEventsUseCase.execute();
24+
}
25+
1126
async handleReservations() {
1227
this.logger.info('Start - HandleNewReservations');
1328
await this.handleNewReservationUseCase.execute();
@@ -34,5 +49,9 @@ export class ReservationController {
3449
this.logger.info('Start - HandleScheduledReservations');
3550
await this.handleScheduledReservationUseCase.execute();
3651
this.logger.info('End - HandleScheduledReservations');
52+
53+
this.logger.info('Start - CreateReservationEventsUseCase');
54+
await this.createReservationEventsUseCase.execute();
55+
this.logger.info('End - CreateReservationEventsUseCase');
3756
}
3857
}
+25
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
export class ReservationInterface {
2+
constructor({ reservationController }) {
3+
this.reservationController = reservationController;
4+
}
5+
6+
register(server) {
7+
server.route([
8+
{
9+
method: 'GET',
10+
path: '/reservations/calendar',
11+
options: {
12+
handler: async (_, h) => {
13+
const calendar = await this.reservationController.getCalendar();
14+
const response = h
15+
.response(calendar)
16+
.type('text/calendar; charset=utf-8');
17+
response.header('Content-Disposition', 'attachment; filename="calendar.ics"');
18+
return response;
19+
},
20+
tags: ['api', 'reservations', 'calendar'],
21+
},
22+
},
23+
]);
24+
}
25+
}

src/infrastructure/pino.js

+58
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
import { logger } from './Logger.js';
2+
3+
const plugin = {
4+
name: 'hapi-pino',
5+
register: async (server, options) => {
6+
const logger = options.instance;
7+
8+
server.ext('onPostStart', async () => {
9+
logger.info(server.info, 'server started');
10+
});
11+
12+
server.ext('onPostStop', async () => {
13+
logger.info(server.info, 'server stopped');
14+
});
15+
16+
server.events.on('log', (event) => {
17+
logger.info({ tags: event.tags, data: event.data });
18+
});
19+
20+
server.events.on('request', (_, event) => {
21+
if (event.channel !== 'error') {
22+
return;
23+
}
24+
if (event.error) {
25+
logger.error(
26+
{
27+
tags: event.tags,
28+
err: event.error,
29+
},
30+
'request error',
31+
);
32+
}
33+
});
34+
35+
server.events.on('response', (request) => {
36+
const info = request.info;
37+
38+
logger.info(
39+
{
40+
queryParams: request.query,
41+
responseTime:
42+
(info.completed !== undefined ? info.completed : info.responded)
43+
- info.received,
44+
payload: request.auth.isAuthenticated ? request.payload : {},
45+
req: request,
46+
res: request.raw.res,
47+
},
48+
'request completed',
49+
);
50+
});
51+
},
52+
};
53+
54+
const options = {
55+
instance: logger,
56+
};
57+
58+
export { options, plugin };

src/infrastructure/routes.js

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
import { ReservationInterface } from './ReservationInterface.js';
2+
3+
export const routes = [ReservationInterface];

0 commit comments

Comments
 (0)