progettoTLW

Introduzione

Questo documento rappresenta la relazione del progetto “Social Network for Music”, sviluppato nel contesto del corso “Programmazione e Linguaggi per il Web” durante l’anno accademico 2023.

Autori

Il progetto è stato realizzato da:

Front-End

Front End: Il Front-End è la parte dell’applicazione che si occupa dell’interfaccia utente e dell’interazione con l’utente. Si concentra sulla progettazione e sull’implementazione dell’aspetto visivo dell’applicazione e sulla gestione delle interazioni utente.

All’interno della directory /src/html/, sono presenti i seguenti elementi principali:

La suddivisione chiara tra file HTML, file CSS e file JavaScript (JScript) consente una gestione efficiente del Front-End e garantisce un’esperienza utente di alta qualità.

Per ulteriori dettagli sull’implementazione del Front-End, si rimanda alle specifiche sezioni dei file e dei componenti menzionati sopra.

Back-End

Back End: Il Back-End è responsabile delle funzionalità e della logica dell’applicazione lato server. Esso comprende una serie di elementi chiave presenti nella nostra struttura di lavoro. Possiamo suddividere il backend in 3 sezioni principali

NODEJS

COS’E’ NODEJS ED EXPRESS

Node.js ed Express costituiscono un binomio potente nell’ambito dello sviluppo web di applicazioni scalabili ed efficienti.
Node.js fornisce un ambiente runtime JavaScript server-side, ottimizzato per l’efficienza e la scalabilità.
Express, un framework web basato su Node.js, semplifica la creazione di applicazioni web, offrendo funzionalità come la gestione delle richieste HTTP e dell’autenticazione. Ulteriori info a questa pagina.

FILE NODEJS

La struttura ben organizzata del Back-End garantisce una gestione efficiente delle funzionalità server-side e contribuisce al corretto funzionamento dell’applicazione.

DATABASE MONGODB

Nel corso di sviluppo della nostra applicazione, abbiamo fatto largo uso del database MongoDB. Qui di seguito, presentiamo le collezioni che abbiamo creato e utilizzato per immagazzinare i dati essenziali dell’applicazione.

MongoDB: MongoDB è un database NoSQL (non relazionale), flessibile e scalabile, noto per la sua struttura orientata ai documenti. Un documento è un record dati in formato BSON (Binary JSON) che può contenere dati di varie forme e dimensioni. Ogni documento è organizzato in collezioni, offrendo flessibilità nella modellazione dei dati.

Per questa applicazione abbiamo deciso di utilizzare le seguenti collections:

Alt text

Di seguito viene riportata una descrizione delle collections, del loro schema di validation JSON e dei tipi di dato

Validazione JSON: La validazione JSON è un processo cruciale per garantire che i dati immagazzinati nei database siano coerenti e rispettino gli standard dell’applicazione. Definendo regole specifiche per la struttura e il formato dei dati, la validazione riduce il rischio di errori e contribuisce all’affidabilità e all’integrità del sistema.

COMMUNITY

DESCRIZIONE

La collezione community ha lo scopo di raccogliere informazioni relative alle comunità all’interno della nostra applicazione.

VALIDAZIONE JSON
{
  "$jsonSchema": {
    "bsonType": "object",
    "required": [
      "_id",
      "creatorId",
      "name"
    ],
    "properties": {
      "_id": {
        "bsonType": "objectId",
        "description": "_id must be an ObjectId and is required"
      },
      "creatorId": {
        "bsonType": "objectId",
        "description": "creatorId must be an ObjectId and is required"
      },
      "name": {
        "bsonType": "string",
        "description": "name must be a string and is required"
      },
      "desc": {
        "bsonType": "string",
        "description": "desc must be a string"
      },
      "members": {
        "bsonType": "array",
        "description": "members must be an array of ObjectIds"
      },
      "playlists": {
        "bsonType": "array",
        "description": "playlists must be an array of ObjectIds"
      }
    }
  }
} 
ATTRIBUTI

PLAYLISTS

DESCRIZIONE
La collezione playlists è stata creata per rappresentare le playlist musicali all’interno della nostra applicazione.

VALIDAZIONE JSON
{
  "$jsonSchema": {
     "bsonType": "object",
    "required": [
      "_id",
      "owner_id",
      "title"
    ],
    "properties": {
      "_id": {
        "bsonType": "objectId",
        "description": "_id must be a ObjectId and is required"
      },
      "owner_id": {
        "bsonType": "objectId",
        "description": "owner_id must be a ObjectId and is required"
      },
      "title": {
        "bsonType": "string",
        "description": "title must be a string and is required"
      },
      "description": {
        "bsonType": "string",
        "description": "name must be a string"
      },
      "tags": {
        "bsonType": "array",
        "description": "name must be an array of ObjectIds"
      },
      "songs": {
        "bsonType": "array",
        "description": "name must be a array of ObjectIds"
      }
    }
  }
}
ATTRIBUTI

USERS

DESCRIZIONE

La collezione users è destinata a contenere i dati degli utenti all’interno dell’applicazione.

VALIDAZIONE JSON
{
  "$jsonSchema": {
    "bsonType": "object",
    "required": [
      "_id",
      "nickname",
      "email",
      "password"
    ],
    "properties": {
      "_id": {
        "bsonType": "objectId",
        "description": "_id must be an ObjectId and is required"
      },
      "name": {
        "bsonType": "string",
        "description": "name must be a string and is required"
      },
      "nickname": {
        "bsonType": "objectId",
        "description": "nickname must be an ObjectId and is required"
      },
      "email": {
        "bsonType": "string",
        "description": "email must be a string and is required"
      },
      "password": {
        "bsonType": "string",
        "description": "password must be a string"
      },
      "date": {
        "bsonType": "string",
        "description": "date must be a string"
      },
      "genres": {
        "bsonType": "array",
        "description": "genres must be an array of ObjectIds"
      }
    }
  }
}

ATTRIBUTI

SISTEMA DI LOGGING e AUDITING

Il sistema di logging o auditing in un’applicazione web rappresenta un componente fondamentale per la tracciabilità delle operazioni e la gestione degli errori.

Questo sistema registra le attività cruciali e gli errori nell’applicazione, offrendo una visione dettagliata delle interazioni e delle problematiche riscontrate.

In questa applicazione ho realizzato questo meccanismo tramite due funzioni presenti nel file utils.js: log e logonly.

Le operazioni di Log e Auditing funzionano nel seguente modo

CODICE
export function log(message) {
  const timestamp = new Date().toISOString();
  const logMessage = `[${timestamp}]: ${message}\n`;
  var stream = fs.createWriteStream("serverlogs/serverlogs.log", {flags:'a'});
  stream.write(logMessage);
  console.log(logMessage);
}

export function logonly(message) {
  const timestamp = new Date().toISOString();
  const logMessage = `[${timestamp}]: ${message}\n`;
  var stream = fs.createWriteStream("serverlogs/serverlogs.log", {flags:'a'});
  stream.write(logMessage);
}

ESEMPIO DI UTILIZZO
if (!isValidNickname(nickname)) {
    res.status(404).send("invalid nickname");
    utils.log("[REGISTER]> register > ERROR 400: invalid nickname");
    return; 
}
ENTRY NEL FILE SERVERLOGS.LOG
[2023-09-20T17:06:01.826Z]: [REGISTER]> register > ERROR 400: invalid nickname

Configurazione dell’applicazione

Il progetto necessita di un file .env nella directory principale del dove sono contenuti i parametri necessari per il funzionamento. Il file .env è gestito attraverso il pacchetto npm dotenv che si occupa di popolare le relative variabili d’ambienti e renderne semplice l’utilizzo e accesso tramite JavaScript.

Un esempio di file env

# Server HOST and PORT
HOST='localhost'
PORT=3000

# Parametri e Credenziali MongoDB
DATABASE='mongodb-cluster'
DB_NAME='mongodb-name'
DB_URI="mongodb+srv://user:token@DATABASE.server_id.mongodb.net/"

# Parametri e Credenziali Spotify
BASE_URL="https://api.spotify.com/v1"
TOKEN_URL="https://accounts.spotify.com/api/token"
CLIENT_ID='token_client_generated_from_spotify'
CLIENT_SECRET='token_generated_from_spotify'

Scelte implementative e features

Swagger JS

Swagger: è un framework open-source per la progettazione, la creazione e la documentazione di API RESTful. La sua utilità si concentra sulla semplificazione del processo di sviluppo API, consentendo agli sviluppatori di definire chiaramente le specifiche delle API, testarle e generare automaticamente documentazione dettagliata.

Per la generazione dello swagger ho utilizzato il module swagger-autogen.

Tramite la creazione di un file swagger.js (/src/docs/) con una apposita configurazione e determinati commenti nella sezione degli endpoint, è possibile generare automaticamente una documentazione per gli endpoint.

è possibile visualizzare lo swagger generato all’endpoint /api-docs

FILE SWAGGER.JS

NB: Il codice riportato di seguito non è completo, rappresenta solo un esempio molto vicino a quello utilizzato in questa applicazione! ```javascript const doc = { info: { version: “1.0.0”, title: “SNM API”, description: “Documentation for the APIs of our website: Social Network for Music.” }, host: ${config.host}:${config.port}, basePath: “/”, schemes: [‘http’], consumes: [‘application/json’], produces: [‘application/json’], tags: [ { “name”: “fetch”, “description”: “Endpoints for fetching and searching content.” }, { “name”: “users”, “description”: “…” }, { “name”: “auth”, “description”: “…” }, { “name”: “playlist”, “description”: “…” }, { “name”: “community”, “description”: “…” }, { “name”: “tracks”, “description”: “…” }, { “name”: “misc”, “description”: “…” }, { “name”: “artists”, “description”: “…” }

] , definitions: { … user: { _id: “ObjectId(‘64df73b31e5eda5eb868ddcd’)”, name: “Joe”, nickname: “joedough”, surname: “Joe”, email: “joedough@example.com”, password: “md5 hashed password”, date: “2001-09-11”, genres: { 0: “pop”, 1: “rock”, 2: “metal” } }, playlists: { _id: “ObjectId(‘64e748f0cb18ad90657b9043’)”, owner_id: “ObjectId(‘64df73b31e5eda5eb868ddcd’)”, title: “Example Playlist”, description: “Description of playlist”, public: true, tags: { 0: “chill”, 1: “relax”, 2: “vibes” }, songs: { 0:{ title: “Song 1”, artist: “Artist1, Artist2, Artist3”, duraion: “00:01:11” }, 1:{ title: “Song 2”, artist: “Artist1, Artist2, Artist3”, duraion: “00:02:22” }, 2:{ title: “Song 3”, artist: “Artist1, Artist2, Artist3”, duraion: “00:03:33” } }, private: true }, song: { $_id: “78kf73b31e6yda5eb868dder”, $artist:”[‘artist1’,’artist2’]”, $duration: “00:11:22”, $year: “1984”, $album: “Album Name” } … } }

const generateSwagger = async () => { try { await swaggerAutogen()(outputFile, endpointsFiles,doc); utils.log(‘SWAGGER DOCUMENTATION GENERATED.’); } catch (error) { utils.error(‘ERROR WHILE GENERATING SWAGGER DOCUMENTATION:’, error); } };

generateSwagger();

##### INTERFACCIA GRAFICA SWAGGER
![Alt text](/progettoTLW/docs/assets/image-3.png)
![Alt text](/progettoTLW/docs/assets/image-4.png)
![Alt text](/progettoTLW/docs/assets/image-5.png)
![Alt text](/progettoTLW/docs/assets/image-6.png)


##### INSTALLAZIONE
``` npm install --save-dev swagger-autogen ```<br>
ulteriori informazioni sono presenti al link sopra riportato

## Documentazione JavaScript-Doc
La maggior parte delle funzioni ( principalmente back-end ) in questa applicazione sono state descritte tramite la convenzione **jsdoc**
>La convenzione **JSDoc**, ampiamente utilizzata nella programmazione JavaScript, consiste nell'includere commenti strutturati nel codice per documentare funzioni, classi e metodi. Questi commenti migliorano la chiarezza del codice, facilitano la comprensione e consentono la generazione automatica di documentazione tecnica. Questo standard è cruciale per progetti complessi e la collaborazione tra sviluppatori.

##### Esempio commento JavaDoc
Di seguito un esempio di un commento utilizzando lo standard JavaDoc

```javascript
/**
 * Retrieves a playlist by its ID.
 * 
 * @description This function retrieves a playlist by its unique ID. 
 * It checks the validity of the  provided
 * playlist ID and returns the playlist data if found. 
 * If the playlist does not exist, it returns a 404 Not Found response.
 * In case of any unexpected errors, it sends a 500 Internal Server Error response.
 * @param {Object} res - The HTTP response object.
 * @param {string} playlistid - The ID of the playlist to retrieve.
 * 
 * @returns {void}
 * 
 * @throws {Object} 400 Bad Request if the playlist ID is missing or invalid.
 * @throws {Object} 404 Not Found if the playlist with the provided ID does not exist.
 * @throws {Object} 500 Internal Server Error if any unexpected error occurs during the operation.
 * 
 */
export async function getPlaylistFromId(res, playlistid) {
   if(playlistid==undefined){
      res.status(400).send("Missing playlist id");
      utils.log("[PLAYLIST]> getPlaylistFromId > ERROR 400: Missing playlist id");
      return;     
   }
   if(!utils.isValidString(playlistid)){
      res.status(400).send("Invalid playlistid");
      utils.log("[PLAYLIST]> getPlaylistFromId > ERROR 400: Invalid playlist id");
      return;     
   }
   try {
      const collection = await dbPlaylistCollection();
      const playlist = await collection.findOne({ _id: new ObjectId(playlistid) });
      if (!playlist) {
         res.status(404).send("Playlist not found");
         utils.log("[PLAYLIST]> getPlaylistFromId > ERROR 404: Playlist not found");
         return; 
      }

      res.json(playlist);
      utils.log("[PLAYLIST]> getPlaylistFromId > SUCCESS: SUCCESFULLY FETCHED PLAYLIST "+playlistid);
      return; 

   } catch (error) {
      res.status(500).send("INTERNAL ERROR");
      utils.log("[PLAYLIST]> getPlaylistFromId > ERROR 500: INTERNAL ERROR "+error);
      return; 
   }
}

Gestione codici HTTP

I codici HTTP sono standard utilizzati per indicare lo stato di una richiesta HTTP effettuata tra un client (spesso un browser web) e un server. Nell’applicazione, vengono ampiamente utilizzati alcuni di questi codici per comunicare lo stato delle richieste e delle risposte:

ESEMPIO DI GESTIONE

La gestione che abbiamo deciso di attuare è stata quella di comunicare al sender il codice che la sua richiesta ha “generato” Nell’esempio di seguito è possibile vedere la gestione dei codici 400,404,500 200

NB: res.json(data) viene percepito dal client come un codice 200 ```javascript export async function getPlaylistFromId(res, playlistid) { if(playlistid==undefined){ res.status(400).send(“Missing playlist id”); utils.log(“[PLAYLIST]> getPlaylistFromId > ERROR 400: Missing playlist id”); return;
} if(!utils.isValidString(playlistid)){ res.status(400).send(“Invalid playlistid”); utils.log(“[PLAYLIST]> getPlaylistFromId > ERROR 400: Invalid playlist id”); return;
} try { const collection = await dbPlaylistCollection(); const playlist = await collection.findOne({ _id: new ObjectId(playlistid) }); if (!playlist) { res.status(404).send(“Playlist not found”); utils.log(“[PLAYLIST]> getPlaylistFromId > ERROR 404: Playlist not found”); return; }

  res.json(playlist);
  utils.log("[PLAYLIST]> getPlaylistFromId > SUCCESS: SUCCESFULLY FETCHED PLAYLIST "+playlistid);
  return; 

} catch (error) { res.status(500).send(“INTERNAL ERROR”); utils.log(“[PLAYLIST]> getPlaylistFromId > ERROR 500: INTERNAL ERROR “+error); return; } } ``` —

Esempi di Utilizo

Pagina Iniziale Home

Profilo Profile

Playlist Personali Personal Playlists

Nuova Playlist New Playlists

Cerca Playlist pubbliche Explore Public Playlists

Esplora e importa Playlist Explore and Import Playlists

Comunità Community

Modifica Comunità EditCommunity


Lingua

La scelta di utilizzare la lingua inglese, come standard di programmazione, è ampiamente diffusa nell’industria del software ed è guidata principalmente dal desiderio di aderire allo standard internazionale. Questo standard è anche noto nella community di programmatori come “English-based programming” .
Adottare questa convenzione ha numerosi vantaggi, in quanto rende il codice più leggibile e comprensibile per un pubblico globale di sviluppatori.