diff --git a/Database/os.go b/Database/os.go new file mode 100644 index 0000000..0216bc6 --- /dev/null +++ b/Database/os.go @@ -0,0 +1,128 @@ +package database + +import ( + "database/sql" + "errors" + "log" + + "gopkg.in/guregu/null.v4" +) + +type OS struct { + ID int `json:"id"` + Distribution null.String `json:"distribution"` + Version null.String `json:"version"` + EndOfSupport null.String `json:"EndOfSupport"` +} + +func GetOS(db *sql.DB) ([]OS, error) { + var oses []OS + rows, err := db.Query("SELECT * FROM dashboard_os") + if err != nil { + log.Println("Error querying list of OS", err) + } + defer func(rows *sql.Rows) { + err := rows.Close() + if err != nil { + log.Println("Error closing rows stream") + } + }(rows) + + for rows.Next() { + var os OS + err := rows.Scan(&os.ID, &os.Distribution, &os.Version, &os.EndOfSupport) + if err != nil { + log.Println("Error building list", err) + } + oses = append(oses, os) + } + return oses, err +} + +func GetOSbyID(db *sql.DB, id int64) (OS, error) { + var os OS + if err := db.QueryRow("SELECT * FROM dashboard_os where id = ?", id).Scan(&os.ID, &os.Distribution, &os.Version, &os.EndOfSupport); err != nil { + if errors.Is(err, sql.ErrNoRows) { + return os, nil + } + log.Println("Error getting OS by ID", err) + } + return os, nil +} + +func GetDistributionList(db *sql.DB) ([]null.String, error) { + var list []null.String + rows, err := db.Query("SELECT DISTINCT distribution FROM dashboard_os") + if err != nil { + log.Println("Error getting distribution list", err) + } + for rows.Next() { + var os OS + err := rows.Scan(&os.Distribution) + if err != nil { + log.Println("Error building distribution list", err) + } + list = append(list, os.Distribution) + } + return list, nil +} + +func GetVersionsByDistributionList(db *sql.DB, d string) ([]null.String, error) { + var list []null.String + rows, err := db.Query("SELECT DISTINCT version FROM dashboard_os where distribution = ?", d) + if err != nil { + log.Println("Error getting distribution list", err) + } + for rows.Next() { + var os OS + err := rows.Scan(&os.Distribution) + if err != nil { + log.Println("Error building distribution list", err) + } + list = append(list, os.Distribution) + } + return list, nil +} + +func checkIfOsExists(os OS, db *sql.DB) bool { + row := db.QueryRow("SELECT distribution, version FROM dashboard_os WHERE distribution = ? AND version = ?", os.Distribution, os.Version) + err := row.Scan(&os.Distribution, &os.Version) + return !errors.Is(err, sql.ErrNoRows) +} + +func CreateOS(os OS, db *sql.DB) error { + if checkIfOsExists(os, db) { + log.Println("OS already exists") + return nil + } + q, err := db.Prepare("INSERT INTO `dashboard_os` (`distribution`,`version`,`end_of_support`) VALUES (?,?,?)") + if err != nil { + log.Println("Error creating query", err) + return err + } + _, err = q.Exec(&os.Distribution, &os.Version, &os.EndOfSupport) + if err != nil { + log.Println("Error inserting OS", err) + return err + } + return nil +} + +func DeleteOS(os OS, db *sql.DB) (int, error) { + if !checkIfOsExists(os, db) { + log.Println("OS does not exist, nothing to delete") + return 404, nil + } + q, err := db.Prepare("DELETE FROM dashboard_os WHERE distribution = ? AND version = ?") + if err != nil { + log.Println("Error deleting OS", err) + return 500, err + } + _, err = q.Exec(&os.Distribution, &os.Version) + if err != nil { + log.Println("Error deleting OS", err) + return 500, err + } + log.Println("OS deleted") + return 200, nil +} diff --git a/Database/package.go b/Database/package.go new file mode 100644 index 0000000..d13a00b --- /dev/null +++ b/Database/package.go @@ -0,0 +1,151 @@ +package database + +import ( + "database/sql" + "errors" + "log" + + "gopkg.in/guregu/null.v4" +) + +type Package struct { + ID int `json:"id"` + Name null.String `json:"name"` + Active int `json:"active"` +} + +func GetPackage(db *sql.DB) ([]Package, error) { + var packages []Package + rows, err := db.Query("SELECT * FROM dashboard_package") + if err != nil { + log.Println("Error querying list of OS", err) + } + defer rows.Close() + + for rows.Next() { + var pkg Package + err := rows.Scan(&pkg.ID, &pkg.Name, &pkg.Active) + if err != nil { + log.Println("Error building list", err) + } + packages = append(packages, pkg) + } + return packages, err +} + +func GetPackagebyID(db *sql.DB, id int64) (Package, error) { + var pkg Package + if err := db.QueryRow("SELECT * FROM dashboard_package where id = ?", id).Scan(&pkg.ID, &pkg.Name, &pkg.Active); err != nil { + if errors.Is(err, sql.ErrNoRows) { + return pkg, nil + } + log.Println("Error getting OS by ID", err) + } + return pkg, nil +} + +func GetPackagebyName(db *sql.DB, id int64) (Package, error) { + var pkg Package + if err := db.QueryRow("SELECT * FROM dashboard_package where name = ?", id).Scan(&pkg.ID, &pkg.Name, &pkg.Active); err != nil { + if errors.Is(err, sql.ErrNoRows) { + return pkg, nil + } + log.Println("Error getting OS by ID", err) + } + return pkg, nil +} + +func checkIfPackageExists(pkg Package, db *sql.DB) bool { + if err := db.QueryRow("Select name from dashboard_package where name = ?", pkg.Name).Scan(&pkg.Name); err != nil { + if errors.Is(err, sql.ErrNoRows) { + return false + } + } + return true +} + +func CreatePackage(pkg Package, db *sql.DB) error { + if checkIfPackageExists(pkg, db) { + /* If package exists but is not active we activate it */ + row := db.QueryRow("Select name, active from dashboard_package where name = ? and active = 1", pkg.Name) + err := row.Scan(&pkg.Name, &pkg.Active) + if errors.Is(err, sql.ErrNoRows) { + q, err := db.Prepare("UPDATE dashboard_package set active = 1 where name = ?") + if err != nil { + log.Println("Error creating query", err) + } + _, err = q.Exec(pkg.Name) + if err != nil { + log.Println("Error activating Package", err) + } + log.Println("Activating new package", pkg.Name) + return nil + } + return nil + } + q, err := db.Prepare("INSERT INTO dashboard_package (name, active) values (?, 1)") + if err != nil { + log.Println("Error creating query", err) + return err + } + _, err = q.Exec(pkg.Name) + if err != nil { + log.Println("Error creating Package", err) + return err + } + return nil +} + +func DisablePackage(pkg Package, db *sql.DB) (int, error) { + if !checkIfPackageExists(pkg, db) { + log.Println("Package does not exist", pkg.Name) + return 404, nil + } + q, err := db.Prepare("UPDATE dashboard_package set active = 0 where name = ?") + if err != nil { + log.Println("Error creating query", err) + return 500, err + } + _, err = q.Exec(pkg.Name) + if err != nil { + log.Println("Error disabling Package", err) + return 500, err + } + return 200, nil +} + +func EnablePackage(pkg Package, db *sql.DB) (int, error) { + if !checkIfPackageExists(pkg, db) { + log.Println("Package does not exist", pkg.Name) + return 404, nil + } + q, err := db.Prepare("UPDATE dashboard_package set active = 1 where name = ?") + if err != nil { + log.Println("Error creating query", err) + return 500, err + } + _, err = q.Exec(pkg.Name) + if err != nil { + log.Println("Error disabling Package", err) + return 500, err + } + return 200, nil +} + +func DeletePackage(pkg Package, db *sql.DB) (int, error) { + if !checkIfPackageExists(pkg, db) { + log.Println("Package does not exist", pkg.Name) + return 404, nil + } + q, err := db.Prepare("DELETE FROM dashboard_package where name = ?") + if err != nil { + log.Println("Error creating query", err) + return 500, err + } + _, err = q.Exec(pkg.Name) + if err != nil { + log.Println("Error deleting Package", err) + return 500, err + } + return 200, nil +} diff --git a/Database/server.go b/Database/server.go new file mode 100644 index 0000000..dc64eb6 --- /dev/null +++ b/Database/server.go @@ -0,0 +1,117 @@ +package database + +import ( + "database/sql" + "errors" + "log" + + "gopkg.in/guregu/null.v4" +) + +type Server struct { + ID int `json:"id"` + Hostname null.String `json:"hostname"` + OsId null.String `json:"OsId"` +} + +func GetServersList(db *sql.DB) ([]Server, error) { + var servers []Server + rows, err := db.Query("SELECT * FROM dashboard_server") + if err != nil { + log.Println("Error querying servers list", err) + } + defer func(rows *sql.Rows) { + err := rows.Close() + if err != nil { + log.Println("Error closing rows query", err) + } + }(rows) + + for rows.Next() { + var s Server + err := rows.Scan(&s.ID, &s.Hostname, &s.OsId) + if err != nil { + log.Println("Error building servers list", err) + } + servers = append(servers, s) + } + return servers, err +} + +func GetServersbyID(db *sql.DB, id int64) Server { + var s Server + rows := db.QueryRow("SELECT * FROM dashboard_server WHERE id = ?", id) + err := rows.Scan(&s.ID, &s.Hostname, &s.OsId) + if err != nil { + return Server{} + } + return s +} + +func GetServersbyOS(db *sql.DB, id int64) ([]Server, error) { + var servers []Server + rows, err := db.Query("SELECT * FROM dashboard_server WHERE os_id = ?", id) + if err != nil { + log.Println("Error querying servers list by os_id", err) + return nil, err + } + defer func(rows *sql.Rows) { + err := rows.Close() + if err != nil { + log.Println("Error closing rows query", err) + } + }(rows) + + for rows.Next() { + var s Server + err := rows.Scan(&s.ID, &s.Hostname, &s.OsId) + if err != nil { + log.Println("Error building servers list", err) + } + servers = append(servers, s) + } + return servers, err +} + +func checkIfServerExists(server Server, db *sql.DB) bool { + row := db.QueryRow("SELECT hostname, os_id FROM dashboard_server WHERE hostname = ? AND os_id = ?", server.Hostname, server.OsId) + err := row.Scan(&server.Hostname, &server.OsId) + return !errors.Is(err, sql.ErrNoRows) +} + +func CreateServer(server Server, db *sql.DB) error { + if !checkIfServerExists(server, db) { + log.Println("Server already exists") + return nil + } + q, err := db.Prepare("INSERT INTO dashboard_server (hostname, os_id) VALUES ( ?, ?)") + if err != nil { + log.Println("Error creating query", err) + return err + } + _, err = q.Exec(server.Hostname, server.OsId) + if err != nil { + log.Println("Error inserting Server", err) + return err + } + return nil +} + +func DeleteServer(server Server, db *sql.DB) (int, error) { + if !checkIfServerExists(server, db) { + log.Println("Server does not exist, nothing to delete") + return 404, nil + } + q, err := db.Prepare("DELETE FROM dashboard_server WHERE hostname = ? AND os_id = ?") + if err != nil { + log.Println("Error deleting Server", err) + return 500, err + } + _, err = q.Exec(server.Hostname, server.OsId) + if err != nil { + log.Println("Error deleting Server", err) + return 500, err + } + log.Println("Server deleted") + return 200, nil +} diff --git a/Database/utils.go b/Database/utils.go new file mode 100644 index 0000000..3397392 --- /dev/null +++ b/Database/utils.go @@ -0,0 +1,24 @@ +package database + +import ( + "database/sql" + "log" + "os" + + "github.com/go-sql-driver/mysql" +) + +var GetDatabaseConnection = func() *sql.DB { + cfg := mysql.Config{ + User: os.Getenv("DATABASE_USER"), + Passwd: os.Getenv("DATABASE_PASSWORD"), + Net: "tcp", + Addr: os.Getenv("DATABASE_HOST"), + DBName: os.Getenv("DATABASE_NAME"), + } + DbConn, err := sql.Open("mysql", cfg.FormatDSN()) + if err != nil { + log.Println("Database connection error : ", err) + } + return DbConn +} diff --git a/Http/os.go b/Http/os.go new file mode 100644 index 0000000..fb93586 --- /dev/null +++ b/Http/os.go @@ -0,0 +1,174 @@ +package http + +import ( + "encoding/json" + db "infra-dashboard/Database" + "io" + "log" + "net/http" + "strconv" + "time" + + "github.com/gorilla/mux" + "gopkg.in/guregu/null.v4" +) + +func GetOS(w http.ResponseWriter, r *http.Request) { + var list []db.OS + var err error + + t := time.Now() + status := 200 + + dbConn := db.GetDatabaseConnection() + defer dbConn.Close() + list, err = db.GetOS(dbConn) + if err != nil { + log.Println("Error getting OS list") + status = 500 + } + logRequest(t, r, status) + w.Header().Set("Content-Type", "application/json") + err = json.NewEncoder(w).Encode(list) + if err != nil { + log.Println("Error encoding OS list") + return + } +} + +func GetOSbyID(w http.ResponseWriter, r *http.Request) { + var os db.OS + var err error + + t := time.Now() + status := 200 + + params := mux.Vars(r) + dbConn := db.GetDatabaseConnection() + defer dbConn.Close() + id, err := strconv.ParseInt(params["id"], 10, 64) + if err != nil { + log.Println("Error converting ID", err) + status = 500 + } + os, err = db.GetOSbyID(dbConn, int64(id)) + if err != nil { + log.Println("Error getting OS") + status = 500 + } + logRequest(t, r, status) + w.Header().Set("Content-Type", "application/json") + err = json.NewEncoder(w).Encode(os) + if err != nil { + log.Println("Error getting OS by ID") + return + } +} + +func GetDistributionList(w http.ResponseWriter, r *http.Request) { + var list []null.String + var err error + + t := time.Now() + status := 200 + + dbConn := db.GetDatabaseConnection() + defer dbConn.Close() + list, err = db.GetDistributionList(dbConn) + if err != nil { + log.Println("Error getting distribution list") + status = 500 + } + logRequest(t, r, status) + w.Header().Set("Content-Type", "application/json") + err = json.NewEncoder(w).Encode(list) + if err != nil { + log.Println("Error getting distribution list") + return + } +} + +func GetVersionsByDistributionList(w http.ResponseWriter, r *http.Request) { + var list []null.String + var err error + + t := time.Now() + status := 200 + + params := mux.Vars(r) + dbConn := db.GetDatabaseConnection() + defer dbConn.Close() + list, err = db.GetVersionsByDistributionList(dbConn, params["distribution"]) + if err != nil { + log.Println("Error getting distribution list") + status = 500 + } + logRequest(t, r, status) + w.Header().Set("Content-Type", "application/json") + err = json.NewEncoder(w).Encode(list) + if err != nil { + log.Println("Error getting distribution list") + return + } +} + +func CreateOS(w http.ResponseWriter, r *http.Request) { + var os db.OS + var err error + + t := time.Now() + status := 204 + + body, err := io.ReadAll(r.Body) + if err != nil { + log.Println("Error reading request body", err) + } + + params := make(map[string]null.String) + err = json.Unmarshal(body, ¶ms) + if err != nil { + log.Println("Error unmarshalling request body", err) + return + } + os.Distribution = params["distribution"] + os.Version = params["version"] + os.EndOfSupport = params["end_of_support"] + dbConn := db.GetDatabaseConnection() + defer dbConn.Close() + err = db.CreateOS(os, dbConn) + if err != nil { + log.Println("Error creating new OS", err) + status = 500 + } + logRequest(t, r, status) + w.WriteHeader(status) +} + +func DeleteOS(w http.ResponseWriter, r *http.Request) { + var os db.OS + var err error + var status int + t := time.Now() + + body, err := io.ReadAll(r.Body) + if err != nil { + log.Println(err.Error(), "Error reading request body") + } + + params := make(map[string]null.String) + err = json.Unmarshal(body, ¶ms) + if err != nil { + log.Println("Error unmarshalling request body", err) + return + } + os.Distribution = params["distribution"] + os.Version = params["version"] + dbConn := db.GetDatabaseConnection() + defer dbConn.Close() + status, err = db.DeleteOS(os, dbConn) + if err != nil { + log.Println(err.Error(), "Error deleting OS") + } + logRequest(t, r, status) + w.WriteHeader(status) +} diff --git a/Http/os_test.go b/Http/os_test.go new file mode 100644 index 0000000..9e657aa --- /dev/null +++ b/Http/os_test.go @@ -0,0 +1,230 @@ +package http + +import ( + "database/sql" + "encoding/json" + "github.com/DATA-DOG/go-sqlmock" + "github.com/gorilla/mux" + "gopkg.in/guregu/null.v4" + db "infra-dashboard/Database" + "log" + "net/http" + "net/http/httptest" + "reflect" + "regexp" + "testing" +) + +func TestGetOS(t *testing.T) { + // Créer une connexion à la base de données simulée + dbConn, mock, err := sqlmock.New() + if err != nil { + t.Fatalf("an error '%s' was not expected when opening a stub database connection", err) + } + defer dbConn.Close() + + // Simuler la réponse de la base de données + mockOSList := []db.OS{ + {ID: 1, Distribution: null.StringFrom("Ubuntu"), Version: null.StringFrom("22.04"), EndOfSupport: null.StringFrom("2026-04-01")}, + {ID: 2, Distribution: null.StringFrom("Debian"), Version: null.StringFrom("12"), EndOfSupport: null.StringFrom("2028-04-01")}, + } + + rows := sqlmock.NewRows([]string{"id", "distribution", "version", "end_of_support"}). + AddRow(1, "Ubuntu", "22.04", "2026-04-01"). + AddRow(2, "Debian", "12", "2028-04-01") + + mock.ExpectQuery(regexp.QuoteMeta("SELECT * FROM dashboard_os")).WillReturnRows(rows) + + // Remplacer la fonction GetDatabaseConnection par une version mockée + originalGetDatabaseConnection := db.GetDatabaseConnection + db.GetDatabaseConnection = func() *sql.DB { + return dbConn + } + defer func() { db.GetDatabaseConnection = originalGetDatabaseConnection }() + + // Créer une requête HTTP + req, err := http.NewRequest("GET", "/os", nil) + if err != nil { + t.Fatal(err) + } + + // Créer un ResponseRecorder pour enregistrer la réponse + rr := httptest.NewRecorder() + handler := http.HandlerFunc(GetOS) + + // Appeler la fonction GetOS + handler.ServeHTTP(rr, req) + + // Vérifier le statut de la réponse + if status := rr.Code; status != http.StatusOK { + t.Errorf("handler returned wrong status code: got %v want %v", + status, http.StatusOK) + } + + // Vérifier le type de contenu de la réponse + expectedContentType := "application/json" + if ct := rr.Header().Get("Content-Type"); ct != expectedContentType { + t.Errorf("handler returned wrong content type: got %v want %v", + ct, expectedContentType) + } + + // Vérifier le corps de la réponse + var responseOSList []db.OS + err = json.NewDecoder(rr.Body).Decode(&responseOSList) + if err != nil { + t.Fatal(err) + } + + if !reflect.DeepEqual(responseOSList, mockOSList) { + t.Errorf("handler returned unexpected body: got %v want %v", + responseOSList, mockOSList) + } + + // Vérifier que toutes les attentes ont été satisfaites + if err := mock.ExpectationsWereMet(); err != nil { + t.Errorf("there were unfulfilled expectations: %s", err) + } +} + +func TestGetOSbyID(t *testing.T) { + // Créer une connexion à la base de données simulée + dbConn, mock, err := sqlmock.New() + if err != nil { + t.Fatalf("an error '%s' was not expected when opening a stub database connection", err) + } + defer dbConn.Close() + + // Simuler la réponse de la base de données + mockOS := db.OS{ + ID: 1, Distribution: null.StringFrom("Ubuntu"), Version: null.StringFrom("22.04"), EndOfSupport: null.StringFrom("2026-04-01"), + } + + rows := sqlmock.NewRows([]string{"id", "distribution", "version", "end_of_support"}). + AddRow(1, "Ubuntu", "22.04", "2026-04-01") + + mock.ExpectQuery(regexp.QuoteMeta("SELECT * FROM dashboard_os where id = ?")).WithArgs(sqlmock.AnyArg()).WillReturnRows(rows) + + // Remplacer la fonction GetDatabaseConnection par une version mockée + originalGetDatabaseConnection := db.GetDatabaseConnection + db.GetDatabaseConnection = func() *sql.DB { + return dbConn + } + defer func() { db.GetDatabaseConnection = originalGetDatabaseConnection }() + + // Créer une requête HTTP + req, err := http.NewRequest("GET", "/os/1", nil) + if err != nil { + t.Fatal(err) + } + + // Ajouter les paramètres de la route + req = mux.SetURLVars(req, map[string]string{"id": "1"}) + + // Créer un ResponseRecorder pour enregistrer la réponse + rr := httptest.NewRecorder() + handler := http.HandlerFunc(GetOSbyID) + + // Appeler la fonction GetOS + handler.ServeHTTP(rr, req) + + // Vérifier le statut de la réponse + if status := rr.Code; status != http.StatusOK { + t.Errorf("handler returned wrong status code: got %v want %v", + status, http.StatusOK) + } + + // Vérifier le type de contenu de la réponse + expectedContentType := "application/json" + if ct := rr.Header().Get("Content-Type"); ct != expectedContentType { + t.Errorf("handler returned wrong content type: got %v want %v", + ct, expectedContentType) + } + + // Vérifier le corps de la réponse + var responseOS db.OS + err = json.NewDecoder(rr.Body).Decode(&responseOS) + if err != nil { + t.Fatal("Error decoding json request", err) + } + + if !reflect.DeepEqual(responseOS, mockOS) { + t.Errorf("handler returned unexpected body: got %v want %v", + responseOS, mockOS) + } + + // Vérifier que toutes les attentes ont été satisfaites + if err := mock.ExpectationsWereMet(); err != nil { + t.Errorf("there were unfulfilled expectations: %s", err) + } +} + +func TestGetDistributionList(t *testing.T) { + // Créer une connexion à la base de données simulée + dbConn, mock, err := sqlmock.New() + if err != nil { + t.Fatalf("an error '%s' was not expected when opening a stub database connection", err) + } + defer dbConn.Close() + + // Simuler la réponse de la base de données + mockDistributionList := []null.String{} + mockDistributionList = append(mockDistributionList, null.StringFrom("Ubuntu")) + mockDistributionList = append(mockDistributionList, null.StringFrom("Debian")) + + rows := sqlmock.NewRows([]string{"distribution"}). + AddRow("Ubuntu"). + AddRow("Debian") + + mock.ExpectQuery(regexp.QuoteMeta("SELECT DISTINCT distribution FROM dashboard_os")).WillReturnRows(rows) + + // Remplacer la fonction GetDatabaseConnection par une version mockée + originalGetDatabaseConnection := db.GetDatabaseConnection + db.GetDatabaseConnection = func() *sql.DB { + return dbConn + } + defer func() { db.GetDatabaseConnection = originalGetDatabaseConnection }() + + // Créer une requête HTTP + req, err := http.NewRequest("GET", "/os/distribution", nil) + if err != nil { + t.Fatal(err) + } + + // Créer un ResponseRecorder pour enregistrer la réponse + rr := httptest.NewRecorder() + handler := http.HandlerFunc(GetDistributionList) + + // Appeler la fonction GetOS + handler.ServeHTTP(rr, req) + + // Vérifier le statut de la réponse + if status := rr.Code; status != http.StatusOK { + t.Errorf("handler returned wrong status code: got %v want %v", + status, http.StatusOK) + } + + // Vérifier le type de contenu de la réponse + expectedContentType := "application/json" + if ct := rr.Header().Get("Content-Type"); ct != expectedContentType { + t.Errorf("handler returned wrong content type: got %v want %v", + ct, expectedContentType) + } + + // Vérifier le corps de la réponse + var responseOSList []null.String + err = json.NewDecoder(rr.Body).Decode(&responseOSList) + log.Println(responseOSList) + if err != nil { + t.Fatal(err) + } + + if !reflect.DeepEqual(responseOSList, mockDistributionList) { + t.Errorf("handler returned unexpected body: got %v want %v", + responseOSList, mockDistributionList) + } + + // Vérifier que toutes les attentes ont été satisfaites + if err := mock.ExpectationsWereMet(); err != nil { + t.Errorf("there were unfulfilled expectations: %s", err) + } +} diff --git a/Http/package.go b/Http/package.go new file mode 100644 index 0000000..dbfa4d7 --- /dev/null +++ b/Http/package.go @@ -0,0 +1,179 @@ +package http + +import ( + "encoding/json" + "gopkg.in/guregu/null.v4" + db "infra-dashboard/Database" + "io" + "log" + "net/http" + "strconv" + "time" + + "github.com/gorilla/mux" +) + +func GetAllPackages(w http.ResponseWriter, r *http.Request) { + var list []db.Package + var err error + + t := time.Now() + status := 200 + + db_conn := db.GetDatabaseConnection() + defer db_conn.Close() + list, err = db.GetPackage(db_conn) + if err != nil { + log.Println("Error getting Package list") + status = 500 + } + logRequest(t, r, status) + w.Header().Set("Content-Type", "application/json") + err = json.NewEncoder(w).Encode(list) + if err != nil { + log.Println("Error encoding response") + return + } +} + +func GetPackagebyID(w http.ResponseWriter, r *http.Request) { + var pkg db.Package + var err error + + t := time.Now() + status := 200 + + params := mux.Vars(r) + dbConn := db.GetDatabaseConnection() + defer dbConn.Close() + id, err := strconv.ParseInt(params["id"], 10, 64) + if err != nil { + log.Println("Error converting ID", err) + status = 500 + } + pkg, err = db.GetPackagebyID(dbConn, int64(id)) + if err != nil { + log.Println("Error getting Package") + status = 500 + } + logRequest(t, r, status) + w.Header().Set("Content-Type", "application/json") + err = json.NewEncoder(w).Encode(pkg) + if err != nil { + log.Println("Error encoding response") + return + } +} + +func CreatePackage(w http.ResponseWriter, r *http.Request) { + var pkg db.Package + var err error + t := time.Now() + status := 200 + + body, err := io.ReadAll(r.Body) + if err != nil { + log.Println(err.Error(), "Error reading request body") + } + + params := make(map[string]null.String) + err = json.Unmarshal(body, ¶ms) + if err != nil { + log.Println(err.Error(), "Error reading request body") + return + } + pkg.Name = params["name"] + dbConn := db.GetDatabaseConnection() + defer dbConn.Close() + err = db.CreatePackage(pkg, dbConn) + if err != nil { + log.Println("Error creating package", pkg.Name) + status = 500 + } + logRequest(t, r, status) + w.WriteHeader(status) +} + +func DisablePackage(w http.ResponseWriter, r *http.Request) { + var pkg db.Package + var err error + var status int + t := time.Now() + + body, err := io.ReadAll(r.Body) + if err != nil { + log.Println(err.Error(), "Error reading request body") + } + + params := make(map[string]null.String) + err = json.Unmarshal(body, ¶ms) + if err != nil { + log.Println(err.Error(), "Error reading request body") + return + } + pkg.Name = params["name"] + dbConn := db.GetDatabaseConnection() + defer dbConn.Close() + status, err = db.DisablePackage(pkg, dbConn) + if err != nil { + log.Println(err.Error(), "Error disabling package", pkg.Name) + } + logRequest(t, r, status) + w.WriteHeader(status) +} + +func EnablePackage(w http.ResponseWriter, r *http.Request) { + var pkg db.Package + var err error + var status int + t := time.Now() + + body, err := io.ReadAll(r.Body) + if err != nil { + log.Println(err.Error(), "Error reading request body") + } + + params := make(map[string]null.String) + err = json.Unmarshal(body, ¶ms) + if err != nil { + log.Println(err.Error(), "Error reading request body") + return + } + pkg.Name = params["name"] + dbConn := db.GetDatabaseConnection() + defer dbConn.Close() + status, err = db.EnablePackage(pkg, dbConn) + if err != nil { + log.Println(err.Error(), "Error enabling package", pkg.Name) + } + logRequest(t, r, status) + w.WriteHeader(status) +} + +func DeletePackage(w http.ResponseWriter, r *http.Request) { + var pkg db.Package + var err error + var status int + t := time.Now() + + body, err := io.ReadAll(r.Body) + if err != nil { + log.Println(err.Error(), "Error reading request body") + } + + params := make(map[string]null.String) + err = json.Unmarshal(body, ¶ms) + if err != nil { + log.Println(err.Error(), "Error reading request body") + return + } + pkg.Name = params["name"] + dbConn := db.GetDatabaseConnection() + defer dbConn.Close() + status, err = db.DeletePackage(pkg, dbConn) + if err != nil { + log.Println(err.Error(), "Error deleting package", pkg.Name) + } + logRequest(t, r, status) + w.WriteHeader(status) +} diff --git a/Http/package_test.go b/Http/package_test.go new file mode 100644 index 0000000..41af354 --- /dev/null +++ b/Http/package_test.go @@ -0,0 +1,159 @@ +package http + +import ( + "database/sql" + "encoding/json" + db "infra-dashboard/Database" + "net/http" + "net/http/httptest" + "reflect" + "regexp" + "testing" + + "github.com/DATA-DOG/go-sqlmock" + "github.com/gorilla/mux" + "gopkg.in/guregu/null.v4" +) + +func TestGetPackage(t *testing.T) { + // Créer une connexion à la base de données simulée + dbConn, mock, err := sqlmock.New() + if err != nil { + t.Fatalf("an error '%s' was not expected when opening a stub database connection", err) + } + defer dbConn.Close() + + // Simuler la réponse de la base de données + mockPackageList := []db.Package{ + {ID: 1, Name: null.StringFrom("Redis"), Active: 1}, + {ID: 2, Name: null.StringFrom("Rabbit"), Active: 0}, + } + + rows := sqlmock.NewRows([]string{"id", "name", "active"}). + AddRow(1, "Redis", "1"). + AddRow(2, "Rabbit", "0") + + mock.ExpectQuery(regexp.QuoteMeta("SELECT * FROM dashboard_package")).WillReturnRows(rows) + + // Remplacer la fonction GetDatabaseConnection par une version mockée + originalGetDatabaseConnection := db.GetDatabaseConnection + db.GetDatabaseConnection = func() *sql.DB { + return dbConn + } + defer func() { db.GetDatabaseConnection = originalGetDatabaseConnection }() + + // Créer une requête HTTP + req, err := http.NewRequest("GET", "/package", nil) + if err != nil { + t.Fatal(err) + } + + // Créer un ResponseRecorder pour enregistrer la réponse + rr := httptest.NewRecorder() + handler := http.HandlerFunc(GetAllPackages) + + // Appeler la fonction GetPackage + handler.ServeHTTP(rr, req) + + // Vérifier le statut de la réponse + if status := rr.Code; status != http.StatusOK { + t.Errorf("handler returned wrong status code: got %v want %v", + status, http.StatusOK) + } + + // Vérifier le type de contenu de la réponse + expectedContentType := "application/json" + if ct := rr.Header().Get("Content-Type"); ct != expectedContentType { + t.Errorf("handler returned wrong content type: got %v want %v", + ct, expectedContentType) + } + + // Vérifier le corps de la réponse + var responsePackageList []db.Package + err = json.NewDecoder(rr.Body).Decode(&responsePackageList) + if err != nil { + t.Fatal(err) + } + + if !reflect.DeepEqual(responsePackageList, mockPackageList) { + t.Errorf("handler returned unexpected body: got %v want %v", + responsePackageList, mockPackageList) + } + + // Vérifier que toutes les attentes ont été satisfaites + if err := mock.ExpectationsWereMet(); err != nil { + t.Errorf("there were unfulfilled expectations: %s", err) + } +} + +func TestGetPackagebyID(t *testing.T) { + // Créer une connexion à la base de données simulée + dbConn, mock, err := sqlmock.New() + if err != nil { + t.Fatalf("an error '%s' was not expected when opening a stub database connection", err) + } + defer dbConn.Close() + + // Simuler la réponse de la base de données + mockPackage := db.Package{ + ID: 1, Name: null.StringFrom("Redis"), Active: 1, + } + + rows := sqlmock.NewRows([]string{"id", "name", "active"}). + AddRow(1, "Redis", "1") + + mock.ExpectQuery(regexp.QuoteMeta("SELECT * FROM dashboard_package where id = ?")).WithArgs(sqlmock.AnyArg()).WillReturnRows(rows) + + // Remplacer la fonction GetDatabaseConnection par une version mockée + originalGetDatabaseConnection := db.GetDatabaseConnection + db.GetDatabaseConnection = func() *sql.DB { + return dbConn + } + defer func() { db.GetDatabaseConnection = originalGetDatabaseConnection }() + + // Créer une requête HTTP + req, err := http.NewRequest("GET", "/package/1", nil) + if err != nil { + t.Fatal(err) + } + + // Ajouter les paramètres de la route + req = mux.SetURLVars(req, map[string]string{"id": "1"}) + + // Créer un ResponseRecorder pour enregistrer la réponse + rr := httptest.NewRecorder() + handler := http.HandlerFunc(GetPackagebyID) + + // Appeler la fonction GetPackage + handler.ServeHTTP(rr, req) + + // Vérifier le statut de la réponse + if status := rr.Code; status != http.StatusOK { + t.Errorf("handler returned wrong status code: got %v want %v", + status, http.StatusOK) + } + + // Vérifier le type de contenu de la réponse + expectedContentType := "application/json" + if ct := rr.Header().Get("Content-Type"); ct != expectedContentType { + t.Errorf("handler returned wrong content type: got %v want %v", + ct, expectedContentType) + } + + // Vérifier le corps de la réponse + var responsePackage db.Package + err = json.NewDecoder(rr.Body).Decode(&responsePackage) + if err != nil { + t.Fatal("Error decoding json request", err) + } + + if !reflect.DeepEqual(responsePackage, mockPackage) { + t.Errorf("handler returned unexpected body: got %v want %v", + responsePackage, mockPackage) + } + + // Vérifier que toutes les attentes ont été satisfaites + if err := mock.ExpectationsWereMet(); err != nil { + t.Errorf("there were unfulfilled expectations: %s", err) + } +} diff --git a/Http/server.go b/Http/server.go new file mode 100644 index 0000000..5293f4d --- /dev/null +++ b/Http/server.go @@ -0,0 +1,161 @@ +package http + +import ( + "encoding/json" + db "infra-dashboard/Database" + "io" + "log" + "net/http" + "strconv" + "time" + + "gopkg.in/guregu/null.v4" + + "github.com/gorilla/mux" +) + +func GetServersList(w http.ResponseWriter, r *http.Request) { + var list []db.Server + var err error + + t := time.Now() + status := 200 + + dbConn := db.GetDatabaseConnection() + defer dbConn.Close() + list, err = db.GetServersList(dbConn) + if err != nil { + log.Println("Error getting OS list") + status = 500 + } + logRequest(t, r, status) + w.Header().Set("Content-Type", "application/json") + err = json.NewEncoder(w).Encode(list) + if err != nil { + log.Println("Error getting OS list") + return + } +} + +func GetServersbyID(w http.ResponseWriter, r *http.Request) { + var server db.Server + var err error + + t := time.Now() + status := 200 + + params := mux.Vars(r) + id, err := strconv.ParseInt(params["id"], 10, 32) + if err != nil { + log.Println("Error converting ID", err) + status = 500 + } + dbConn := db.GetDatabaseConnection() + defer dbConn.Close() + server = db.GetServersbyID(dbConn, int64(id)) + logRequest(t, r, status) + w.Header().Set("Content-Type", "application/json") + err = json.NewEncoder(w).Encode(server) + if err != nil { + log.Println("Error getting OS list") + return + } +} + +func GetServersbyOS(w http.ResponseWriter, r *http.Request) { + var servers []db.Server + var err error + + t := time.Now() + status := 200 + + params := mux.Vars(r) + osId, err := strconv.ParseInt(params["os_id"], 10, 32) + if err != nil { + log.Println("Error converting ID", err) + status = 500 + logRequest(t, r, status) + w.WriteHeader(status) + return + } + dbConn := db.GetDatabaseConnection() + defer dbConn.Close() + servers, err = db.GetServersbyOS(dbConn, int64(osId)) + if err != nil { + log.Println("Error getting servers by OS", err) + status = 500 + logRequest(t, r, status) + w.WriteHeader(status) + return + } + logRequest(t, r, status) + w.Header().Set("Content-Type", "application/json") + err = json.NewEncoder(w).Encode(servers) + if err != nil { + log.Println("Error getting OS list") + status = 500 + logRequest(t, r, status) + w.WriteHeader(status) + return + } +} + +func CreateServer(w http.ResponseWriter, r *http.Request) { + var server db.Server + var err error + + t := time.Now() + status := 204 + + body, err := io.ReadAll(r.Body) + if err != nil { + log.Println(err.Error(), "Error reading request body") + } + + params := make(map[string]null.String) + err = json.Unmarshal(body, ¶ms) + if err != nil { + log.Println(err.Error(), "Error parsing request body") + return + } + server.Hostname = params["hostname"] + server.OsId = params["os_id"] + dbConn := db.GetDatabaseConnection() + defer dbConn.Close() + err = db.CreateServer(server, dbConn) + if err != nil { + log.Println(err.Error(), "Error creating server") + status = 500 + } + logRequest(t, r, status) + w.WriteHeader(status) +} + +func DeleteServer(w http.ResponseWriter, r *http.Request) { + var server db.Server + var err error + var status int + t := time.Now() + + body, err := io.ReadAll(r.Body) + if err != nil { + log.Println(err.Error(), "Error reading request body") + } + + params := make(map[string]null.String) + err = json.Unmarshal(body, ¶ms) + if err != nil { + log.Println(err.Error(), "Error parsing request body") + return + } + server.Hostname = params["hostname"] + server.OsId = params["os_id"] + dbConn := db.GetDatabaseConnection() + defer dbConn.Close() + status, err = db.DeleteServer(server, dbConn) + if err != nil { + log.Println(err.Error(), "Error deleting server") + } + logRequest(t, r, status) + w.WriteHeader(status) +} diff --git a/Http/server_test.go b/Http/server_test.go new file mode 100644 index 0000000..b5ee64c --- /dev/null +++ b/Http/server_test.go @@ -0,0 +1,380 @@ +package http + +import ( + "bytes" + "database/sql" + "encoding/json" + db "infra-dashboard/Database" + "net/http" + "net/http/httptest" + "reflect" + "regexp" + "testing" + + "github.com/DATA-DOG/go-sqlmock" + "github.com/gorilla/mux" + "gopkg.in/guregu/null.v4" +) + +func TestGetServerList(t *testing.T) { + // Créer une connexion à la base de données simulée + dbConn, mock, err := sqlmock.New() + if err != nil { + t.Fatalf("an error '%s' was not expected when opening a stub database connection", err) + } + defer dbConn.Close() + + // Simuler la réponse de la base de données + mockServerList := []db.Server{ + {ID: 1, Hostname: null.StringFrom("testsla"), OsId: null.StringFrom("8")}, + {ID: 2, Hostname: null.StringFrom("Rabbit"), OsId: null.StringFrom("7")}, + } + + rows := sqlmock.NewRows([]string{"id", "name", "active"}). + AddRow(1, "testsla", "8"). + AddRow(2, "Rabbit", "7") + + mock.ExpectQuery(regexp.QuoteMeta("SELECT * FROM dashboard_server")).WillReturnRows(rows) + + // Remplacer la fonction GetDatabaseConnection par une version mockée + originalGetDatabaseConnection := db.GetDatabaseConnection + db.GetDatabaseConnection = func() *sql.DB { + return dbConn + } + defer func() { db.GetDatabaseConnection = originalGetDatabaseConnection }() + + // Créer une requête HTTP + req, err := http.NewRequest("GET", "/servers", nil) + if err != nil { + t.Fatal(err) + } + + // Créer un ResponseRecorder pour enregistrer la réponse + rr := httptest.NewRecorder() + handler := http.HandlerFunc(GetServersList) + + // Appeler la fonction GetServer + handler.ServeHTTP(rr, req) + + // Vérifier le statut de la réponse + if status := rr.Code; status != http.StatusOK { + t.Errorf("handler returned wrong status code: got %v want %v", + status, http.StatusOK) + } + + // Vérifier le type de contenu de la réponse + expectedContentType := "application/json" + if ct := rr.Header().Get("Content-Type"); ct != expectedContentType { + t.Errorf("handler returned wrong content type: got %v want %v", + ct, expectedContentType) + } + + // Vérifier le corps de la réponse + var responseServerList []db.Server + err = json.NewDecoder(rr.Body).Decode(&responseServerList) + if err != nil { + t.Fatal(err) + } + + if !reflect.DeepEqual(responseServerList, mockServerList) { + t.Errorf("handler returned unexpected body: got %v want %v", + responseServerList, mockServerList) + } + + // Vérifier que toutes les attentes ont été satisfaites + if err := mock.ExpectationsWereMet(); err != nil { + t.Errorf("there were unfulfilled expectations: %s", err) + } +} + +func TestGetServerbyID(t *testing.T) { + // Créer une connexion à la base de données simulée + dbConn, mock, err := sqlmock.New() + if err != nil { + t.Fatalf("an error '%s' was not expected when opening a stub database connection", err) + } + defer dbConn.Close() + + // Simuler la réponse de la base de données + mockServer := db.Server{ + ID: 1, Hostname: null.StringFrom("testsla"), OsId: null.StringFrom("8"), + } + + rows := sqlmock.NewRows([]string{"id", "hostname", "os_id"}). + AddRow(1, "testsla", "8") + + mock.ExpectQuery(regexp.QuoteMeta("SELECT * FROM dashboard_server WHERE id = ?")).WithArgs(sqlmock.AnyArg()).WillReturnRows(rows) + + // Remplacer la fonction GetDatabaseConnection par une version mockée + originalGetDatabaseConnection := db.GetDatabaseConnection + db.GetDatabaseConnection = func() *sql.DB { + return dbConn + } + defer func() { db.GetDatabaseConnection = originalGetDatabaseConnection }() + + // Créer une requête HTTP + req, err := http.NewRequest("GET", "/server/1", nil) + if err != nil { + t.Fatal(err) + } + + // Ajouter les paramètres de la route + req = mux.SetURLVars(req, map[string]string{"id": "1"}) + + // Créer un ResponseRecorder pour enregistrer la réponse + rr := httptest.NewRecorder() + handler := http.HandlerFunc(GetServersbyID) + + // Appeler la fonction GetServer + handler.ServeHTTP(rr, req) + + // Vérifier le statut de la réponse + if status := rr.Code; status != http.StatusOK { + t.Errorf("handler returned wrong status code: got %v want %v", + status, http.StatusOK) + } + + // Vérifier le type de contenu de la réponse + expectedContentType := "application/json" + if ct := rr.Header().Get("Content-Type"); ct != expectedContentType { + t.Errorf("handler returned wrong content type: got %v want %v", + ct, expectedContentType) + } + + // Vérifier le corps de la réponse + var responseServer db.Server + err = json.NewDecoder(rr.Body).Decode(&responseServer) + if err != nil { + t.Fatal("Error decoding json request", err) + } + + if !reflect.DeepEqual(responseServer, mockServer) { + t.Errorf("handler returned unexpected body: got %v want %v", + responseServer, mockServer) + } + + // Vérifier que toutes les attentes ont été satisfaites + if err := mock.ExpectationsWereMet(); err != nil { + t.Errorf("there were unfulfilled expectations: %s", err) + } +} + +func TestGetServerbyOS(t *testing.T) { + mockDB, mock, err := sqlmock.New(sqlmock.QueryMatcherOption(sqlmock.QueryMatcherEqual)) + if err != nil { + t.Fatalf("an error '%s' was not expected when opening a stub database connection", err) + } + defer mockDB.Close() + db.GetDatabaseConnection = func() *sql.DB { + return mockDB + } + + mockServer := []db.Server{ + {ID: 1, Hostname: null.StringFrom("testsla"), OsId: null.StringFrom("8")}, + } + + tests := []struct { + name string + requestParam string + mockBehavior func() + expectedStatus int + expectedContentType string + }{ + { + name: "Successfully get Server by OS ID", + requestParam: "8", + mockBehavior: func() { + rows := sqlmock.NewRows([]string{"id", "hostname", "os_id"}).AddRow(1, "testsla", "8") + mock.ExpectQuery("SELECT * FROM dashboard_server WHERE os_id = ?").WithArgs(sqlmock.AnyArg()).WillReturnRows(rows) + }, + expectedStatus: http.StatusOK, + expectedContentType: "application/json", + }, + { + name: "Error getting Server by OS ID", + requestParam: "test", + mockBehavior: func() {}, + expectedStatus: http.StatusInternalServerError, + expectedContentType: "", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + tt.mockBehavior() + + req, err := http.NewRequest("GET", "/server/os/", nil) + if err != nil { + t.Fatal(err) + } + req = mux.SetURLVars(req, map[string]string{"os_id": tt.requestParam}) + + rr := httptest.NewRecorder() + handler := http.HandlerFunc(GetServersbyOS) + handler.ServeHTTP(rr, req) + + if status := rr.Code; status != tt.expectedStatus { + t.Errorf("handler returned wrong status code: got %v want %v", + status, tt.expectedStatus) + } + + if ct := rr.Header().Get("Content-Type"); ct != tt.expectedContentType { + t.Errorf("handler returned wrong content type: got %v want %v", + ct, tt.expectedContentType) + } + + var responseServer []db.Server + if len(responseServer) > 0 { + err = json.NewDecoder(rr.Body).Decode(&responseServer) + if err != nil { + t.Fatal("Error decoding json request", err) + } + + if !reflect.DeepEqual(responseServer, mockServer) { + t.Errorf("handler returned unexpected body: got %v want %v", + responseServer, mockServer) + } + + } + if err := mock.ExpectationsWereMet(); err != nil { + t.Errorf("there were unfulfilled expectations: %s", err) + } + }) + } +} + +func TestCreateServer(t *testing.T) { + mockDB, mock, err := sqlmock.New(sqlmock.QueryMatcherOption(sqlmock.QueryMatcherEqual)) + if err != nil { + t.Fatalf("an error '%s' was not expected when opening a stub database connection", err) + } + defer mockDB.Close() + db.GetDatabaseConnection = func() *sql.DB { + return mockDB + } + + // Define the test cases + tests := []struct { + name string + requestBody string + mockBehavior func() + expectedStatus int + }{ + { + name: "Successful server creation", + requestBody: `{"hostname": "test-host", "os_id": "1"}`, + mockBehavior: func() { + mock.ExpectPrepare("INSERT INTO dashboard_server (hostname, os_id) VALUES ( ?, ?)").ExpectExec().WithArgs("test-host", "1").WillReturnResult(sqlmock.NewResult(1, 1)) + }, + expectedStatus: http.StatusNoContent, + }, + { + name: "Error creating server", + requestBody: `{"hostname": "test-host", "os_id": "1"}`, + mockBehavior: func() {}, + expectedStatus: http.StatusInternalServerError, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + // Set up the mock behavior + tt.mockBehavior() + + // Create a request with the test body + req, err := http.NewRequest("POST", "/server/create", bytes.NewBuffer([]byte(tt.requestBody))) + if err != nil { + t.Fatalf("could not create request: %v", err) + } + req.Header.Set("Content-Type", "application/json") + + // Create a ResponseRecorder to record the response + rr := httptest.NewRecorder() + handler := http.HandlerFunc(CreateServer) + + // Call the handler + handler.ServeHTTP(rr, req) + + // Check the status code + if status := rr.Code; status != tt.expectedStatus { + t.Errorf("handler returned wrong status code: got %v want %v", + status, tt.expectedStatus) + } + + // Verify that all expectations were met + if err := mock.ExpectationsWereMet(); err != nil { + t.Errorf("there were unmet expectations: %s", err) + } + }) + } +} + +func TestDeleteServer(t *testing.T) { + // Create a mock database connection + mockDB, mock, err := sqlmock.New(sqlmock.QueryMatcherOption(sqlmock.QueryMatcherEqual)) + if err != nil { + t.Fatalf("an error '%s' was not expected when opening a stub database connection", err) + } + defer mockDB.Close() + + // Mock the GetDatabaseConnection function + db.GetDatabaseConnection = func() *sql.DB { + return mockDB + } + + // Define the test cases + tests := []struct { + name string + requestBody string + mockBehavior func() + expectedStatus int + }{ + { + name: "Successful server deletion", + requestBody: `{"hostname": "test-host", "os_id": "1"}`, + mockBehavior: func() { + mock.ExpectPrepare("DELETE FROM dashboard_server WHERE hostname = ? AND os_id = ?").ExpectExec().WithArgs("test-host", "1").WillReturnResult(sqlmock.NewResult(1, 1)) + }, + expectedStatus: http.StatusOK, + }, + { + name: "Error deleting server", + requestBody: `{"hostname": "test-host", "os_id": "1"}`, + mockBehavior: func() { + }, + expectedStatus: http.StatusInternalServerError, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + // Set up the mock behavior + tt.mockBehavior() + + // Create a request with the test body + req, err := http.NewRequest("POST", "/server/delete", bytes.NewBuffer([]byte(tt.requestBody))) + if err != nil { + t.Fatalf("could not create request: %v", err) + } + req.Header.Set("Content-Type", "application/json") + + // Create a ResponseRecorder to record the response + rr := httptest.NewRecorder() + handler := http.HandlerFunc(DeleteServer) + + // Call the handler + handler.ServeHTTP(rr, req) + + // Check the status code + if status := rr.Code; status != tt.expectedStatus { + t.Errorf("handler returned wrong status code: got %v want %v", + status, tt.expectedStatus) + } + + // Verify that all expectations were met + if err := mock.ExpectationsWereMet(); err != nil { + t.Errorf("there were unmet expectations: %s", err) + } + }) + } +} diff --git a/Http/utils.go b/Http/utils.go new file mode 100644 index 0000000..d6abfa3 --- /dev/null +++ b/Http/utils.go @@ -0,0 +1,43 @@ +package http + +import ( + "fmt" + "io" + "log" + "net/http" + "time" +) + +func RequestHandler(w http.ResponseWriter, r *http.Request) { + t := time.Now() + logRequest(t, r, 418) + w.WriteHeader(http.StatusTeapot) + _, err := fmt.Fprint(w, "Bip Boop I'm a teapot") + if err != nil { + log.Println("Something went wrong with the teapot") + } +} + +func HealthHandler(w http.ResponseWriter, _ *http.Request) { + _, _ = io.WriteString(w, "OK") +} + +func NotFound(w http.ResponseWriter, r *http.Request) { + t := time.Now() + logRequest(t, r, 404) + w.WriteHeader(http.StatusNotFound) +} + +func logRequest(t time.Time, r *http.Request, s int) { + log.Printf("%s - - %s \"%s %s %s\" %d %d \"-\" \"%s\" %d\n", + r.Host, + t.Format("[02/Jan/2006:15:04:05 -0700]"), + r.Method, + r.URL.Path, + r.Proto, + s, + r.ContentLength, + r.UserAgent(), + time.Since(t).Milliseconds(), + ) +} diff --git a/Http/utils_test.go b/Http/utils_test.go new file mode 100644 index 0000000..2da12ce --- /dev/null +++ b/Http/utils_test.go @@ -0,0 +1,59 @@ +package http + +import ( + "io" + "net/http" + "net/http/httptest" + "testing" +) + +func TestHealthHandler(t *testing.T) { + req := httptest.NewRequest(http.MethodGet, "/healthcheck", nil) + w := httptest.NewRecorder() + HealthHandler(w, req) + res := w.Result() + defer res.Body.Close() + + data, err := io.ReadAll(res.Body) + if err != nil { + t.Errorf("Expected error to be nil, got %v", err) + } + if res.StatusCode != http.StatusOK { + t.Errorf("Expected status code 200, got %v", res.StatusCode) + } + if string(data) != "OK" { + t.Errorf("Expected 'OK', got %v", string(data)) + } +} + +func TestRequestHandler(t *testing.T) { + req := httptest.NewRequest(http.MethodGet, "/", nil) + w := httptest.NewRecorder() + RequestHandler(w, req) + res := w.Result() + defer res.Body.Close() + + _, err := io.ReadAll(res.Body) + if err != nil { + t.Errorf("Expected error to be nil, got %v", err) + } + if res.StatusCode != http.StatusTeapot { + t.Errorf("Expected status code 418, got %v", res.StatusCode) + } +} + +func TestNotFound(t *testing.T) { + req := httptest.NewRequest(http.MethodGet, "/metrics", nil) + w := httptest.NewRecorder() + NotFound(w, req) + res := w.Result() + defer res.Body.Close() + + _, err := io.ReadAll(res.Body) + if err != nil { + t.Errorf("Expected error to be nil, got %v", err) + } + if res.StatusCode != http.StatusNotFound { + t.Errorf("Expected status code 404, got %v", res.StatusCode) + } +} diff --git a/Tools/utils.go b/Tools/utils.go new file mode 100644 index 0000000..4fa4092 --- /dev/null +++ b/Tools/utils.go @@ -0,0 +1,12 @@ +package Tools + +import ( + "log" + "os" +) + +// InitLog ensure log file exists and set appropriate flags (remove timestamp at start of line). +func InitLog() { + log.SetOutput(os.Stdout) + log.SetFlags(log.Flags() &^ (log.Ldate | log.Ltime)) +} diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..ef68d01 --- /dev/null +++ b/go.mod @@ -0,0 +1,12 @@ +module infra-dashboard + +go 1.24.2 + +require ( + github.com/DATA-DOG/go-sqlmock v1.5.2 + github.com/go-sql-driver/mysql v1.8.1 + github.com/gorilla/mux v1.8.1 + gopkg.in/guregu/null.v4 v4.0.0 +) + +require filippo.io/edwards25519 v1.1.0 // indirect diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..b39f9ac --- /dev/null +++ b/go.sum @@ -0,0 +1,11 @@ +filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA= +filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4= +github.com/DATA-DOG/go-sqlmock v1.5.2 h1:OcvFkGmslmlZibjAjaHm3L//6LiuBgolP7OputlJIzU= +github.com/DATA-DOG/go-sqlmock v1.5.2/go.mod h1:88MAG/4G7SMwSE3CeA0ZKzrT5CiOU3OJ+JlNzwDqpNU= +github.com/go-sql-driver/mysql v1.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpvNJ1Y= +github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg= +github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY= +github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ= +github.com/kisielk/sqlstruct v0.0.0-20201105191214-5f3e10d3ab46/go.mod h1:yyMNCyc/Ib3bDTKd379tNMpB/7/H5TjM2Y9QJ5THLbE= +gopkg.in/guregu/null.v4 v4.0.0 h1:1Wm3S1WEA2I26Kq+6vcW+w0gcDo44YKYD7YIEJNHDjg= +gopkg.in/guregu/null.v4 v4.0.0/go.mod h1:YoQhUrADuG3i9WqesrCmpNRwm1ypAgSHYqoOcTu/JrI= diff --git a/main.go b/main.go new file mode 100644 index 0000000..71986c8 --- /dev/null +++ b/main.go @@ -0,0 +1,43 @@ +// Receive and send information about servers +package main + +import ( + h "infra-dashboard/Http" + tools "infra-dashboard/Tools" + "log" + "net/http" + + "github.com/gorilla/mux" +) + +func main() { + tools.InitLog() + + router := mux.NewRouter() + + router.NotFoundHandler = http.HandlerFunc(h.NotFound) + + router.HandleFunc("/", h.RequestHandler).Methods("GET") + router.HandleFunc("/healthcheck", h.HealthHandler) + router.HandleFunc("/os", h.GetOS).Methods("GET") + router.HandleFunc("/os/{id:[0-9]+}", h.GetOSbyID).Methods("GET") + router.HandleFunc("/os/create", h.CreateOS).Methods("POST") + router.HandleFunc("/os/distribution", h.GetDistributionList).Methods("GET") + router.HandleFunc("/os/distribution/{distribution:[a-zA-Z]+}/versions", h.GetVersionsByDistributionList).Methods("GET") + router.HandleFunc("/os/delete", h.DeleteOS).Methods("DELETE") + + router.HandleFunc("/servers", h.GetServersList).Methods("GET") + router.HandleFunc("/server/{id:[0-9]+}", h.GetServersbyID).Methods("GET") + router.HandleFunc("/server/os/{os_id:[0-9]+}", h.GetServersbyOS).Methods("GET") + router.HandleFunc("/server/create", h.CreateServer).Methods("POST") + router.HandleFunc("/server/delete", h.DeleteServer).Methods("DELETE") + + router.HandleFunc("/packages", h.GetAllPackages).Methods("GET") + router.HandleFunc("/packages/create", h.CreatePackage).Methods("POST") + router.HandleFunc("/packages/disable", h.DisablePackage).Methods("POST") + router.HandleFunc("/packages/enable", h.EnablePackage).Methods("POST") + router.HandleFunc("/packages/delete", h.DeletePackage).Methods("DELETE") + router.HandleFunc("/packages/{id:[0-9]+}", h.GetPackagebyID).Methods("GET") + + log.Fatal(http.ListenAndServe(":8080", router)) +}