4 - Laboratoire 4


Vous devez développer un service de transformation de données utilisant un des outils proposé. Le serveur doit recevoir un fichier et générer un fichier transformé via l'outil choisi. Analysez les requêtes-réponses pour élaborer les fonctionnalités du serveur.

Démontrez ensuite le fonctionnement du serveur en implémentant des requêtes brutes et affichage pertinent des réponse dans un client Ruby: 1 requête positive, 1 requête alternative différente.

EN BONUS Pour compléter votre exploration des concepts du cours, vous pouvez étoffer votre projet d'un élément qui n'a pas été vu en classe, par exemple :

  • Client(login, ajouter, modifier, supprimer)
  • Tests unitaires RACK exhaustifs
  • Approche orientée-objet/modules en Ruby, Ruby OOP
  • Utilisation d'une base de donnée relationnelle, SQLite
  • Autre fonctionnalité supplémentaire pertinente... ?

Vous devez faire approuver votre choix d'outil(et de bonus)!

Persistence des données

Vous devez implémenter un mécanisme d'authentification HTTP Basic multi-usagers persistente. Vous pouvez créer manuellement les utilisateurs sur le serveur.

Le résultat de chaque transformation effectuée par les utilisateurs doit être enregistré dans un fichier et une base de données permet d'associer chaque fichier à son propriétaire, en plus des informations suivantes

  • name, nom du fichier original téléversé
  • timestamp, date et heure(format "%F %T") de la transformation
  • password, mot de passe optionnel pour protéger le fichier
  • uuid, identifiant unique alphanumérique généré lors de la transformation

Vous devez également effectuer les validations nécessaires pour assurer l'intégrité des données.

Format Requêtes-Réponses

Les routes suivantes sont accessibles sans authentification obligatoire.


GALLERIE affiche ce fichier HTML

GET /

===

... Fichier gallery.html ...


LISTE retourne les informations des fichiers

  • Trier les items en affichant ceux de l'utilisateur en premier si authentifié, puis du plus récent au plus ancien ET nom de fichier croissant.
    • On peut retourner un tableau de la fonction sort_by pour préciser plusieurs conditions de tri [{nom: "charlie", age: 10}, {nom: "alice", age: 10}, {nom: "bob", age: 20}].sort_by {|p| [ p[:age], p[:nom] ]}
    • Pour ajuster le tri ascendant ou descendant, on peut inverser la valeur via un calcul dans le sort_by.
    • Si le résultat du calcul est un booléen, on doit le convertir to_s pour la comparaison dans le tri.
  • Attention la représentation des données dans la réponse est DIFFÉRENTE des données de la base de données. Le serveur doit traiter les données AVANT de les retourner

    • Par exemple, la base de données contient
    Prenom  | Nom     | Naissance
    -----------------------------
    James   | Hoffman | 1990
    Mathieu | St-Yves | 1980
    Lyne    | Amyot   | 1970
    
    • Le serveur peut transformer les données pour produire la réponse suivante
    Nom complet     | Age
    ----------------------
    James Hoffman   | 30
    Mathieu St-Yves | 40
    Lyne Amyot      | 50
    
    • Donc on CALCULE les valeurs pertinentes à partir des données brutes


GET /files

===

200
Content-Type: application/json
[
    { uuid: _string_,  name: _string_,  timestamp: _string_, private: _boolean_, mine: _boolean_ },
    ...
]


RÉCUPÉRATION d'un fichier

  • Si le fichier est protégé, le pass doit correspondre
  • Sinon, si l'utilisateur est authentifié ET qu'il est le propriétaire, le pass est ignoré
GET /files/...uuid...?pass=...

===

200
Content-Type: ...

... Fichier correspondant au uuid ...

~~~ Si uuid invalide

404
File not found

~~~ Si pass incorrecte OU pas propriétaire

403
Access denied


Les routes qui suivent requiert l'authentification de l'usager, et n'affectent que les données qui lui appartiennent

LOGIN permet de s'authentifier

  • Il n'y a pas de log out avec l'authentification basique dans le navigateur, utilisez une fenêtre privée pour tester. Vous serez déconnecté à la fermeture de la fenêtre.
GET /login

===

303 
Location: /

~~~ Si authentification invalide

401
WWW-Authenticate: Basic
Invalid credentials


TRAITEMENT de données permet de créer un nouveau fichier et ses informations.

  • On envoit la requête au format multipart pour permettre de joindre des fichiers
  • On peut préciser un mot de passe pour protéger l'accès au fichier
  • On peut préciser les arguments de l'outils de transformation au format JSON
  • Lors d'une réponse positive, on retourne le résultat de la transformation en body et l'URL via un header
POST /files
Authorization: Basic ...
Content-Type: multipart/form-data

-----------------------------
Content-Disposition: form-data; name="password"

shawi123
-----------------------------
Content-Disposition: form-data; name="original_file"; filename="demo.txt"
Content-Type: text/plain

Content of demo.txt
-----------------------------
Content-Disposition: form-data; name="options"

{ some: ..., option: ... }
-----------------------------

===

201
Content-Location: /files/...uuid...
Content-Type: ...

... Fichier traité ...

~~~ Si erreur

400
...Message...

~~~ Si pas authentifié

401
WWW-Authenticate: Basic
Authentication required


MODIFICATION des informations permet d'ajouter/retirer le mot de passe d'un fichier

  • Le mot de passe, ou vide, est envoyé directement dans le body
PATCH /files/...uuid...

... password ...

===

204

~~~ Si uuid invalide, ou pas propriétaire

404
File not found

~~~ Si erreur

400
...Message...

~~~ Si pas authentifié

401
WWW-Authenticate: Basic
Authentication required


SUPPRESSION efface le fichier et ses informations de la base de données

DELETE /files/...uuid...
===

204 

~~~ Si uuid invalide, ou pas propriétaire

404
File not found

~~~ Si erreur

400
...Message...

~~~ Si pas authentifié

401
WWW-Authenticate: Basic
Authentication required

Outils

tokei

wget https://github.com/XAMPPRocky/tokei/releases/download/v12.1.2/tokei-x86_64-unknown-linux-gnu.tar.gz
su root -c "tar -xf  tokei-x86_64-unknown-linux-gnu.tar.gz --directory=/usr/bin"

Usage

tokei

tesseract ocr

su root -c "apt install tesseract-ocr -y"

Usage

tesseract

boxes

su root -c "apt install boxes -y"

Usage

boxes

markmap

su root -c "apt install nodejs npm -y"
su root -c "npm install -g markmap-cli"

Usage

markmap

triangle

wget https://github.com/esimov/triangle/releases/download/v2.0.0/triangle-2.0.0-linux-amd64.tar.gz
su root -c "tar -xvf triangle-2.0.0-linux-amd64.tar.gz triangle-2.0.0-linux-amd64/triangle -O > /usr/bin/triangle; chmod 755 /usr/bin/triangle"

Usage

triangle

wkhtmltopdf / wkhtmltoimage

su root -c "apt install wkhtmltopdf -y"

Usage

wkhtmltopdf
wkhtmltoimage

cowsay

su root -c "apt install cowsay -y"

Usage

cowsay

diff2html

su root -c "apt install nodejs npm -y"
su root -c "npm install -g diff2html-cli"

Usage

diff -u file1.txt file2.txt | diff2html -i stdin

jq

su root -c "apt install jq -y"

Usage

jq

hget

su root -c "apt install nodejs npm -y"
su root -c "npm install -g hget"

Usage

hget

qrencode

su root -c "apt install qrencode -y"

Usage

qrencode

ffmpeg

su root -c "apt install ffmpeg -y"

Usage

ffmpeg

markdown

su root -c "apt install markdown -y"

Usage

markdown

graphicsmagick

su root -c "apt install graphicsmagick -y" 

Usage

magick

Compléments techniques

Téléversement de fichiers

file.txt

 Hello world!!!

server.rb

require "bundler/inline"

gemfile do
    source "http://rubygems.org"

    gem "sinatra-contrib"
    gem "webrick"
end

require "sinatra"
require "sinatra/reloader"

post "/" do 
  return [
    params.to_s,
    "title = " + params["title"],
    params["demo_file"]["filename"] + " = " + params["demo_file"]["tempfile"].read
  ].join("\n\n")
end

client.rb

require "bundler/inline"

gemfile do
    source "http://rubygems.org"
    gem "faraday"

    # https://github.com/lostisland/faraday-multipart
    gem "faraday-multipart"
end

require "faraday"
require "faraday/multipart"

server = Faraday.new("http://localhost:4567") do |f|
  f.request :multipart
end

data = {
  title: "demo file upload",
  demo_file: Faraday::Multipart::FilePart.new("file.txt", "text/plain")
}

response = server.post("/", data) 

puts(response.body)

Réponse

{"title"=>"demo file upload", "demo_file"=>{"filename"=>"file.txt", "type"=>"text/plain", "name"=>"demo_file", "tempfile"=>#<Tempfile:/tmp/RackMultipart20221119-13201-151pi1f.txt>, "head"=>"Content-Disposition: form-data; name=\"demo_file\"; filename=\"file.txt\"\r\nContent-Length: 16\r\nContent-Type: text/plain\r\nContent-Transfer-Encoding: binary\r\n"}}

title = demo file upload

file.txt =  Hello world!!!

Exécution d'un programme externe

original.txt

3
4
2
6
7
3
4
5
7
3
6

code.rb

# Demonstration de l'utilisation d'un programme externe via Ruby
# https://docs.ruby-lang.org/en/3.1/Kernel.html#method-i-system
# avec les utilitaires standards uniq et sort

filename = "original.txt"
output = "result.txt"

# sort original.txt | uniq > result.txt
system("sort #{filename} | uniq > #{output}")

result.txt

2
3
4
5
6
7

Remise

20 décembre, 8h via LÉA

  • Une pénalité de 10% sera appliquée à la note finale pour chaque jour de retard à la remise.
  • Remettre tous les fichiers de votre projet dans une archive ZIP via la remise de travail LÉA.

Critères d’évaluation

Laboratoire 2 - 15% / 10

Nom: ..............................................................

Suivi 1, Vendredi 1er décembre / 1
GET /login 0 0.33
GET /files, données brutes 0 0.33
GET /, affichage données 0 0.33
Suivi 2, ANNULÉ: pondéré dans la remise finale / 1
POST /files 0 0.5
GET /files, données réelles 0 0.5
Livraison, 20 décembre 8h AM / 62 / 8
Gallerie
GET / 0 0.5 1
Fichier gallery.html 0 0.5
Liste
GET /files 0 0.5 1
200, liste, JSON 0 0.5 1 1.5
Tri propriétaire en premier, si authentifié 0 1
Tri date descendant ET nom ascendant 0 0.5 1 1.5 2
Représentation des données: uuid, name, timestamp, private, mine 0 0.5 1 1.5 2 2.5 3
Récupération
GET /files/...uuid.. 0 0.5 1 1.5
?pass=... doit correspondre sauf si propriétaire authentifié 0 0.5 1 1.5
200, content-type, fichier correspondant 0 0.5 1
404, not found 0 0.5 1 1.5 2
403, access denied 0 0.5 1 1.5 2
Login
GET /login 0 0.5 1
303, redirection / 0 0.5 1
401, invalid 0 0.5 1
Plusieurs utilisateurs 0 0.5
Gestion sécuritaire du mot de passe 0 0.5 1
Traitement
POST /files 0 0.5 1
password, original file, options JSON 0 0.5 1 1.5
201, content-location, content-type, fichier résultat 0 0.5 1 1.5
401, auth required 0 0.5 1
Validations pertinentes 0 0.5 1 1.5 2
400, erreur... 0 0.5 1
Persistence des informations de la transformation 0 1 1.5 2
Génération du fichier résultat de la transformation via l'outil 0 1 1.5 2
Mise en place des options supplémentaires en arguments de l'outil 0 1 1.5 2
Modification
PATCH /files/...uuid... 0 0.5 1 1.5
password 0 0.5
Persistence de l'information, bon item 0 0.5 1
404, not found OU pas propriétaire 0 0.5 1
401, auth required 0 0.5 1 1.5
400, message... 0 0.5 1
Suppression
DELETE /files/...uuid... 0 0.5 1 1.5
Persistence de l'information, bon item 0 0.5 1
Suppression du fichier correspondant 0 0.5 1
404, not found OU pas propriétaire 0 0.5 1
401, auth required 0 0.5 1 1.5
400, message... 0 0.5 1
Client
GET /files positive, alternative, affichage 0 0.5 1 1.5 2
GET /files/...uuid..?pass positive, alternative, affichage 0 0.5 1 1.5 2
GET /login positive, alternative, affichage 0 0.5 1 1.5 2
POST /files positive, alternative, affichage 0 0.5 1 1.5 2
PATCH /files/...uuid... positive, alternative, affichage 0 0.5 1 1.5 2
DELETE /files/...uuid... positive, alternative, affichage 0 0.5 1 1.5 2
Qualité de rédaction
Nomenclature, Organisation, Indentation, Utilisation judicieuse de Ruby OK -0.5 -1 -1.5 -2
BONUS ............................................................. 0 +2