Vous êtes sur la page 1sur 17

Examen de Fin de Formation BLANC - Session Juin 2023

OFPPT - Option : Full-Stack Web


Réalisé par M. Bahae Eddine Halim

Partie I: Théorie

Dossier 1: Approche Agile


1. Quel est le rôle du Product Owner dans l'approche Agile ?

a. Coordonner les activités de développement

b. Gérer les ressources du projet

c. Définir et prioriser les fonctionnalités du produit

d. Effectuer les tests de qualité du logiciel

2. Qu'est-ce qu'un sprint dans la méthodologie Agile?

a. Une réunion pour discuter des problèmes du projet

b. Une période fixe pendant laquelle les fonctionnalités du produit sont développées et livrées

c. Un document décrivant les besoins du client

d. Une méthode de gestion de projet traditionnelle

3. Quelle est la durée typique d'un sprint dans Scrum?

a. 1 semaine

b. 1 mois

c. 3 mois

d. La durée d'un sprint varie en fonction des besoins du projet

4. Comment l'approche Agile gère-t-elle les changements de scope du projet?

a. En refusant tout changement une fois le projet démarré

b. En planifiant rigoureusement tous les détails du projet à l'avance

c. En acceptant les changements et en les intégrant de manière itérative

d. En facturant des frais supplémentaires pour chaque changement demandé

5. Quel est l'objectif principal de la rétrospective à la fin de chaque sprint dans Scrum?

a. Célébrer les réussites de l'équipe

b. Identifier les erreurs et les problèmes rencontrés pendant le sprint

c. Planifier les tâches pour le prochain sprint

BAHAE EDDINE HALIM 1


Dossier 2: Application Cloud Native
1. La commande pour exécuter le fichier DOCKERFILE :

La commande pour exécuter un fichier Dockerfile est `docker build -t nom_image .`

(le point à la fin indique que le Dockerfile est présent dans le répertoire actuel).

2. La commande pour supprimer une image sous Docker :

La commande pour supprimer une image Docker est `docker image rm nom_image` en remplaçant
"nom_image" par le nom ou l'ID de l'image que vous souhaitez supprimer.

3. La commande pour supprimer un conteneur sous Docker :

La commande pour supprimer un conteneur Docker est `docker container rm nom_conteneur` en


remplaçant "nom_conteneur" par le nom ou l'ID du conteneur que vous souhaitez supprimer.

4. La commande pour exécuter le fichier docker-compose :

La commande pour exécuter un fichier docker-compose est `docker-compose up` dans le répertoire
où se trouve le fichier docker-compose.yml.

5. Créer une connexion, un canal et une file d'attente RabbitMQ en utilisant le module amqplib :

const amqp = require('amqplib');

async function setupRabbitMQ() {


const connection = await amqp.connect('amqp://localhost');
const channel = await connection.createChannel();

const queueName = 'ma_file_attente';

await channel.assertQueue(queueName, { durable: true });

console.log('La file d\'attente RabbitMQ est prête !');

// Effectuer d'autres opérations avec la file d'attente...


}

setupRabbitMQ();

Assurez-vous d'avoir installé le module amqplib en utilisant `npm install amqplib`.

BAHAE EDDINE HALIM 2


6. La fonction pour envoyer un message dans la file d'attente :

async function sendMessageToQueue(message, utilisateur) {


const connection = await amqp.connect('amqp://localhost');
const channel = await connection.createChannel();

const queueName = 'ma_file_attente';

await channel.assertQueue(queueName, { durable: true });

const messageObj = {
message: message,
utilisateur: utilisateur,
};

channel.sendToQueue(queueName, Buffer.from(JSON.stringify(messageObj)), {
persistent: true });

console.log('Le message a été envoyé dans la file d\'attente !');

// Effectuer d'autres opérations avec la file d'attente...


}

sendMessageToQueue(req.body.message, req.body.email);

Assurez-vous d'avoir importé le module amqplib et d'avoir une connexion et un canal déjà établis
avant d'appeler cette fonction.

BAHAE EDDINE HALIM 3


7. La fonction pour consommer un message dans la file d'attente :

async function consumeMessageFromQueue() {


const connection = await amqp.connect('amqp://localhost');
const channel = await connection.createChannel();

const

queueName = 'ma_file_attente';

await channel.assertQueue(queueName, { durable: true });

channel.consume(queueName, (message) => {


if (message !== null) {
const messageObj = JSON.parse(message.content.toString());

console.log('Message reçu :', messageObj.message);


console.log('Utilisateur :', messageObj.utilisateur);

// Traiter le message...

channel.ack(message);
}
});

console.log('Lecture des messages dans la file d\'attente...');


}

consumeMessageFromQueue();

Assurez-vous d'avoir importé le module amqplib et d'avoir une connexion et un canal déjà établis
avant d'appeler cette fonction.

BAHAE EDDINE HALIM 4


Partie II: Pratique

Dossier 1: Base de données


Vous serez amené à créer une application en ligne pour gérer un catalogue de livres dans une
bibliothèque.

Le but de cet exercice est de créer un système permettant d'ajouter, de modifier et de supprimer des
livres, ainsi que de les organiser par catégories.

De plus, vous devrez mettre en place des fonctionnalités de validation des données pour assurer

la cohérence des informations saisies par les utilisateurs. Vous aurez également l'opportunité

d'explorer l'utilisation des middlewares pour sécuriser l'accès aux fonctionnalités de gestion des
livres.

Voici un schéma simplifié des tables "Livre" et "Catégorie":

Table "Livre":

- id (clé primaire)

- titre (varchar)

- pages (decimal)

- description (texte)

- image (varchar)

- categorie_id (clé étrangère vers la table "Catégorie")

Table "Catégorie":

- id (clé primaire)

- nom (varchar)

- description (texte)

1. Créez une fonction appelée "calculerTotalLivresParCategorie" qui prend en paramètre l'ID d'une
catégorie et renvoie le nombre total de livres dans cette catégorie.

CREATE FUNCTION calculerTotalLivresParCategorie(categorie_id INT) RETURNS INT


BEGIN
DECLARE total INT;
SELECT COUNT(*) INTO total FROM Livre WHERE categorie_id = categorie_id;
RETURN total;
END;

BAHAE EDDINE HALIM 5


2. Créez une procédure stockée appelée "ajouterLivre" qui permet d'ajouter un nouveau livre à la

table "Livre". La procédure prend en paramètres le titre, le nombre de pages, la description,

l'image et l'ID de la catégorie du livre. La procédure doit également mettre à jour le nombre

total de livres dans la catégorie correspondante en utilisant la fonction


"calculerTotalLivresParCategorie".

CREATE PROCEDURE ajouterLivre(


IN p_titre VARCHAR(255),
IN p_pages DECIMAL(10,2),
IN p_description TEXT,
IN p_image VARCHAR(255),
IN p_categorie_id INT
)
BEGIN
INSERT INTO Livre (titre, pages, description, image, categorie_id)
VALUES (p_titre, p_pages, p_description, p_image, p_categorie_id);

UPDATE Categorie
SET totalLivres = calculerTotalLivresParCategorie(p_categorie_id)
WHERE id = p_categorie_id;
END;

3. Créez un déclencheur (trigger) qui se déclenche après l'insertion d'un nouveau livre dans la

table "Livre". Le déclencheur doit mettre à jour le nombre total de livres dans la

catégorie correspondante en utilisant la fonction "calculerTotalLivresParCategorie".

CREATE TRIGGER apresInsertionLivre AFTER INSERT ON Livre


FOR EACH ROW
BEGIN
UPDATE Categorie
SET totalLivres = calculerTotalLivresParCategorie(NEW.categorie_id)
WHERE id = NEW.categorie_id;
END;

BAHAE EDDINE HALIM 6


Supposons que nous ayons plutôt une base de données NoSQL sous MongoDB.

4. Écrivez une requête MongoDB pour insérer un document représentant une catégorie dans la

collection "Catégories". Assurez-vous d'inclure les champs "nom" et "description".

db.Categories.insertOne({
nom: "Nom de la catégorie",
description: "Description de la catégorie"
});

5. Écrivez une requête MongoDB pour insérer plusieurs documents représentant des livres dans

la collection "Livres". Assurez-vous d'inclure les champs "titre", "pages", "description", "image"

et "categorie_id".

db.Livres.insertMany([
{
titre: "Titre du livre 1",
pages: 300,
description: "Description du livre 1",
image: "image1.jpg",
categorie_id: 1
},
{
titre: "Titre du livre 2",
pages: 250,
description: "Description du livre 2",
image: "image2.jpg",
categorie_id: 2
},
{
titre: "Titre du livre 3",
pages: 400,
description: "Description du livre 3",
image: "image3.jpg",
categorie_id: 1
}
]);

6. Écrivez une requête MongoDB pour récupérer tous les livres avec leurs catégories

correspondantes en utilisant l'opération de jointure (lookup).

BAHAE EDDINE HALIM 7


db.Livres.aggregate([
{
$lookup: {
from: "Categories",
localField: "categorie_id",
foreignField: "id",
as: "categorie"
}
}
]);

7. Écrivez une requête MongoDB pour récupérer tous les livres dont le nombre de pages est

supérieur à 200.

db.Livres.find({ pages: { $gt: 200 } });

8. Écrivez une requête MongoDB pour mettre à jour le titre d'un livre spécifique en utilisant son

ID.

db.Livres.updateOne(
{ _id: ObjectId("ID_DU_LIVRE") },
{ $set: { titre: "Nouveau titre du livre" } }
);

9. Écrivez une requête MongoDB pour supprimer un livre spécifique en utilisant son ID.

db.Livres.deleteOne({ _id: ObjectId("ID_DU_LIVRE") });

10. Écrivez une requête MongoDB pour supprimer tous les livres d'une catégorie spécifique en

utilisant l'ID de la catégorie.

db.Livres.deleteMany({ categorie_id: ID_DE_LA_CATEGORIE });

BAHAE EDDINE HALIM 8


Dossier 2: Back-end
La table "Livre" est liée à la table "Catégorie" par le biais de la colonne "categorie_id", qui est une clé

étrangère faisant référence à l'ID de la catégorie correspondante dans la table "Catégorie".

Travail à faire:

1. Écrire la commande artisan pour créer les modèles "Livre" et "Catégorie".

php artisan make:model Livre


php artisan make:model Categorie

2. Donnez les lignes de code à ajouter au modèle "Livre" afin que tous les attributs puissent être

affectés lors de la création ou de la mise à jour de plusieurs instances du modèle en même

temps. (Indication : utiliser la propriété "fillable").

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class Livre extends Model


{
protected $fillable = ['attribut1', 'attribut2', 'attribut3']; //
Remplacez les attributs par les attributs réels du modèle
}

BAHAE EDDINE HALIM 9


3. Donnez le code de la méthode "livres" à ajouter au modèle "Catégorie". Grâce à cette méthode,
nous pouvons accéder à la collection des livres associés à une catégorie en accédant à la propriété
"livres".

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class Catégorie extends Model


{
public function livres()
{
return $this->hasMany(Livre::class);
}
}

4. Écrire la commande artisan pour créer le contrôleur "LivreController".

php artisan make:controller LivreController

5. Dans la classe "LivreController", créer les méthodes suivantes :

- "index" qui renvoie la vue "index" avec la liste des livres.

- "edit" qui renvoie la vue "edit" avec les informations du formulaire pour éditer un livre spécifique.

- "store" pour sauvegarder les informations d'un nouveau livre à la table "Livre".

- "create" qui renvoie la vue "create" avec le formulaire pour ajouter un livre.

- "update" pour confirmer la mise à jour d'un livre.

- "destroy" pour supprimer un livre.

<?php

namespace App\Http\Controllers;

use App\Livre;
use Illuminate\Http\Request;

class LivreController extends Controller


{

BAHAE EDDINE HALIM 10


public function index()
{
}

public function edit($id)


{
}

public function store(Request $request)


{
}

public function create()


{
}

public function update(Request $request, $id)


{
}

public function destroy($id)


{

}
}

6. Créer les routes pour toutes les méthodes ci-dessus du contrôleur "LivreController".

<?php

use Illuminate\Support\Facades\Route;
use App\Http\Controllers\LivreController;

Route::get('/livres', [LivreController::class, 'index']);


Route::get('/livres/create', [LivreController::class, 'create']);
Route::post('/livres', [LivreController::class, 'store']);
Route::get('/livres/{id}/edit', [LivreController::class, 'edit']);
Route::put('/livres/{id}', [LivreController::class, 'update']);
Route::delete('/livres/{id}', [LivreController::class, 'destroy']);

BAHAE EDDINE HALIM 11


7. Écrire le code de la vue "index" qui permet :

- D'étendre le layout "layouts.app".

- D'afficher tous les livres dans un tableau dans la section blade : "content".

@extends('layouts.app')

@section('content')
<table>
<thead>
<tr>
<th>Num</th> <!-- Ajout de la colonne "Num" pour les numéros
de questions -->
<th>Titre</th>
<th>Description</th>
<th>Catégorie</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
@foreach($livres as $livre)
<tr>
<td>{{ $loop->index + 1 }}</td> <!-- Affichage du numéro
de la question -->
<td>{{ $livre->titre }}</td>
<td>{{ $livre->description }}</td>
<td>{{ $livre->catégorie->nom }}</td>
<td>
<!-- Ajoutez ici les boutons d'édition et de
suppression -->
</td>
</tr>
@endforeach
</tbody>
</table>
@endsection

BAHAE EDDINE HALIM 12


8. Écrire le code de la colonne "Actions" contenant les boutons "ajouter", "éditer" et "supprimer"

permettant d'appeler les routes correspondantes.

<td>
<a href="{{ route('livres.edit', $livre->id) }}">Éditer</a>
<form action="{{ route('livres.destroy', $livre->id) }}" method="POST">
@csrf
@method('DELETE')
<button type="submit">Supprimer</button>
</form>
</td>

9. Ajouter des règles de validation pour le formulaire de création de livre :

- Le champ "titre" doit être requis et avoir une longueur maximale de 255 caractères.

- Le champ "pages" doit être requis et être un nombre entier positif.

- Le champ "description" doit être requis.

- Le champ "catégorie" doit être requis et correspondre à une catégorie existante.

public function store(Request $request)


{
$validatedData = $request->validate([
'titre' => 'required|max:255',
'pages' => 'required|integer|min:0',
'description' => 'required',
'catégorie' => 'required|exists:catégories,id',
]);

// Le reste de votre code de sauvegarde du livre


}

BAHAE EDDINE HALIM 13


Dossier 3: Front-end
Vous devez créer une application React qui affiche les livres avec leurs catégories correspondantes
dans un tableau.

1. Créez un composant de classe appelé "BookTable" qui représente le tableau de livres. Ce

composant devrait avoir un état initial vide pour stocker les données des livres.

import React, { Component } from 'react';

class BookTable extends Component {


constructor(props) {
super(props);
this.state = {
books: []
};
}

render() {
// Affichage du tableau de livres ici
return (
<div>
// Contenu du tableau
</div>
);
}
}

export default BookTable;

2. Créez un autre composant de classe appelé "BookRow" qui représente une ligne du tableau pour
un livre spécifique. Ce composant devrait recevoir les données du livre en tant que propriétés (props)
et les afficher dans des cellules de tableau.

import React, { Component } from 'react';

class BookRow extends Component {


render() {
const { book } = this.props;
// Affichage des données du livre dans les cellules du tableau ici
return (
<tr>
// Cellules du tableau
</tr>
);

BAHAE EDDINE HALIM 14


}
}

export default BookRow;

3. Dans le composant "BookTable", utilisez le cycle de vie componentDidMount pour effectuer une
requête AJAX (par exemple, à l'aide de la méthode fetch) pour récupérer les données des livres
depuis votre API backend.

import React, { Component } from 'react';

class BookTable extends Component {


// ...

componentDidMount() {
fetch('url_de_votre_api')
.then(response => response.json())
.then(data => {
this.setState({ books: data });
})
.catch(error => {
console.error('Erreur lors de la récupération des données des livres',
error);
});
}

// ...
}

export default BookTable;

4. Une fois que vous avez récupéré les données des livres, mettez à jour l'état du composant
"BookTable" avec ces données.

import React, { Component } from 'react';

class BookTable extends Component {


// ...

componentDidMount() {
fetch('url_de_votre_api')
.then(response => response.json())
.then(data => {
this.setState({ books: data });

BAHAE EDDINE HALIM 15


})
.catch(error => {
console.error('Erreur lors de la récupération des données des livres',
error);
});
}

// ...
}

export default BookTable;

5. Utilisez la méthode map sur les données des livres pour générer dynamiquement des composants
"BookRow" pour chaque livre et affichez-les dans le tableau.

import React, { Component } from 'react';


import BookRow from './BookRow';

class BookTable extends Component {


// ...

render() {
const { books } = this.state;

return (
<div>
<table>
<thead>
// En-tête du tableau
</thead>
<tbody>
{books.map(book => (
<BookRow key={book.id} book={book} />
))}
</tbody>
</table>
</div>
);
}
}

export default BookTable;

6. Importez le composant "BookTable" dans le composant racine de votre application et affichez-le à


l'endroit souhaité.

BAHAE EDDINE HALIM 16


import React from 'react';
import BookTable from './BookTable';

function App() {
return (
<div>
// Autres composants et contenu de l'application
<BookTable />
</div>
);
}

export default App;

7. Stylez le tableau et les lignes du tableau à l'aide de CSS pour les rendre esthétiquement agréables.

.table {
width: 100%;
border-collapse: collapse;
}

.table th, .table td {


border: 1px solid #ccc;
padding: 8px;
}

.table th {
background-color: #f2f2f2;
font-weight: bold;
}

.table tr:nth-child(even) {
background-color: #f9f9f9;
}

.table tr:hover {
background-color: #e6e6e6;
}

BAHAE EDDINE HALIM 17

Vous aimerez peut-être aussi