Brijesh's Git Server — watchman @ 4e2b931a2481f38d8de8cc6e6025d60c6ac9a3d9

observability tool, needs to be rewritten once identity is stable

fix(logs): update API endpoints to follow REST standard

Now that the batch insert and other operations work, I organised the endpoints to follow REST standard, also removed some unnecessary handlers for logs.
Brijesh ops@brijesh.dev
Thu, 04 Jul 2024 07:11:08 +0530
commit

4e2b931a2481f38d8de8cc6e6025d60c6ac9a3d9

parent

c4703486eca9ca9390314db3e85650a3cb6457d9

3 files changed, 55 insertions(+), 399 deletions(-)

jump to
M README.mdREADME.md

@@ -52,66 +52,36 @@ ### Log Management API

#### TOC -- [1. Create a New Log](#1-create-a-new-log) -- [2. Get All Logs](#2-get-all-logs) -- [3. Get Logs by Project ID](#3-get-logs-by-project-id) -- [4. Get Logs by User ID](#4-get-logs-by-user-id) -- [5. Get Logs in Time Range](#5-get-logs-in-time-range) -- [6. Get Logs by Level](#6-get-logs-by-level) -- [7. Delete Logs by Project ID](#7-delete-logs-by-project-id) -- [8. Delete Logs by User ID](#8-delete-logs-by-user-id) -- [9. Delete Logs by Time Range](#9-delete-logs-by-time-range) - -#### 1. Create a New Log - -```sh -curl -X POST http://127.0.0.1:4000/logs -H "Content-Type: application/json" -d '{"level": "INFO", "message": "Log message", "subject": "Log subject", "user_id": "user1", "project_id": "project1"}' -``` - -#### 2. Get All Logs - -```sh -curl -X GET http://127.0.0.1:4000/logs -``` - -#### 3. Get Logs by Project ID - -```sh -curl -X GET 'http://127.0.0.1:4000/logs/project?project_id=project1' -``` - -#### 4. Get Logs by User ID - -```sh -curl -X GET 'http://127.0.0.1:4000/logs/user?user_id=user1' -``` +- [1. Create Logs (Bulk)](#1-create-logs-bulk) +- [2. List Logs](#2-list-logs) +- [3. Delete Logs](#3-delete-logs) -#### 5. Get Logs in Time Range +#### 1. Create Logs (Bulk) ```sh -curl -X GET 'http://127.0.0.1:4000/logs/time?start_time=TIMESTAMP&end_time=TIMESTAMP' +curl -X POST http://127.0.0.1:4000/logs -d '[{"time": 1615760000, "level": "info", "message": "this is a test log", "subject": "test", "user_id": "1", "project_id": "1"}]' ``` -#### 6. Get Logs by Level +#### 2. List Logs ```sh -curl -X GET 'http://127.0.0.1:4000/logs/level?level=INFO' +curl http://127.0.0.1:4000/logs?project_id=1 ``` -#### 7. Delete Logs by Project ID +Or you add more query params to filter the logs ```sh -curl -X DELETE 'http://127.0.0.1:4000/logs/project?project_id=project1' +curl http://127.0.0.1:4000/logs?project_id=1&user_id=1&log_level=info&start_time=1615760000&end_time=1615760000 ``` -#### 8. Delete Logs by User ID +#### 3. Delete Logs ```sh -curl -X DELETE 'http://127.0.0.1:4000/logs/user?user_id=user1' +curl -X DELETE http://127.0.0.1:4000/logs?project_id=1 ``` -#### 9. Delete Logs by Time Range +Or you can add more query params to filter the logs ```sh -curl -X DELETE 'http://127.0.0.1:4000/logs/time?start_time=TIMESTAMP&end_time=TIMESTAMP' +curl -X DELETE http://127.0.0.1:4000/logs?project_id=1&user_id=2 ```
M internal/logs.gointernal/logs.go

@@ -9,60 +9,6 @@ "time"

"watchman/schema" ) -// CREATE NEW LOG: INSERT INTO Logs (Time, Level, Message, Subject, UserID, ProjectID) VALUES (?, ?, ?, ?, ?, ?) -// GET ALL LOGS FROM DB: SELECT * FROM Logs -// GET LOGS BY PROJECT ID: SELECT * FROM Logs WHERE ProjectID = ? -// GET LOGS BY USER ID: SELECT * FROM Logs WHERE UserID = ? -// GET LOGS IN TIME RANGE: SELECT * FROM Logs WHERE Time > ? AND Time < ? -// GET LOGS BY LEVEL: SELECT * FROM Logs WHERE Level = ? -// DELETE LOGS BY PROJECT ID: DELETE FROM Logs WHERE ProjectID = ? -// DELETE LOGS BY USER ID: DELETE FROM Logs WHERE UserID = ? -// DELETE LOGS BY TIME RANGE: DELETE FROM Logs WHERE Time > ? AND Time < ? - -func CreateLog(w http.ResponseWriter, r *http.Request, db *sql.DB) { - if r.Method != http.MethodPost { - w.WriteHeader(http.StatusMethodNotAllowed) - fmt.Fprintf(w, "Method %s not allowed", r.Method) - return - } - - var log schema.Log - log.Time = int32(time.Now().Unix()) - decoder := json.NewDecoder(r.Body) - err := decoder.Decode(&log) - if err != nil { - w.WriteHeader(http.StatusBadRequest) - fmt.Fprintf(w, "Error decoding log data: %v", err) - return - } - - stmt, err := db.Prepare("INSERT INTO Logs (Time, Level, Message, Subject, UserID, ProjectID) VALUES (?, ?, ?, ?, ?, ?)") - if err != nil { - w.WriteHeader(http.StatusInternalServerError) - fmt.Fprintf(w, "Error preparing SQL statement: %v", err) - return - } - defer stmt.Close() - - _, err = stmt.Exec(log.Time, log.Level, log.Message, log.Subject, log.UserID, log.ProjectID) - if err != nil { - w.WriteHeader(http.StatusInternalServerError) - fmt.Fprintf(w, "Error inserting log into database: %v", err) - return - } - - response := schema.Response_Type{ - Status: "OK", - Message: "Log created successfully", - RequestID: r.Context().Value(schema.RequestIDKey{}).(string), - } - w.Header().Set("Content-Type", "application/json") - err = json.NewEncoder(w).Encode(response) - if err != nil { - w.WriteHeader(http.StatusInternalServerError) - } -} - func BatchInsertLogs(w http.ResponseWriter, r *http.Request, db *sql.DB) { if r.Method != http.MethodPost { w.WriteHeader(http.StatusMethodNotAllowed)

@@ -105,183 +51,43 @@ w.WriteHeader(http.StatusInternalServerError)

} } -func GetAllLogs(w http.ResponseWriter, r *http.Request, db *sql.DB) { +func GetLogs(w http.ResponseWriter, r *http.Request, db *sql.DB) { if r.Method != http.MethodGet { w.WriteHeader(http.StatusMethodNotAllowed) fmt.Fprintf(w, "Method %s not allowed", r.Method) return } - - rows, err := db.Query("SELECT * FROM Logs") - if err != nil { - w.WriteHeader(http.StatusInternalServerError) - fmt.Fprintf(w, "Error querying database: %v", err) - return - } - defer rows.Close() - - var logs []schema.Log - for rows.Next() { - var log schema.Log - err = rows.Scan(&log.Time, &log.Level, &log.Message, &log.Subject, &log.UserID, &log.ProjectID) - if err != nil { - w.WriteHeader(http.StatusInternalServerError) - fmt.Fprintf(w, "Error scanning row: %v", err) - return - } - logs = append(logs, log) - } - - w.Header().Set("Content-Type", "application/json") - response := schema.Response_Type{ - Status: "OK", - Message: "Logs retrieved successfully", - RequestID: r.Context().Value(schema.RequestIDKey{}).(string), - Data: logs, - } - err = json.NewEncoder(w).Encode(response) - if err != nil { - w.WriteHeader(http.StatusInternalServerError) - } -} - -func GetLogsByProjectID(w http.ResponseWriter, r *http.Request, db *sql.DB) { - if r.Method != http.MethodGet { - w.WriteHeader(http.StatusMethodNotAllowed) - fmt.Fprintf(w, "Method %s not allowed", r.Method) - return - } - projectID := r.URL.Query().Get("project_id") - - rows, err := db.Query("SELECT * FROM Logs WHERE ProjectID = ?", projectID) - if err != nil { - w.WriteHeader(http.StatusInternalServerError) - fmt.Fprintf(w, "Error querying database: %v", err) - return - } - defer rows.Close() - - var logs []schema.Log - for rows.Next() { - var log schema.Log - err = rows.Scan(&log.Time, &log.Level, &log.Message, &log.Subject, &log.UserID, &log.ProjectID) - if err != nil { - w.WriteHeader(http.StatusInternalServerError) - fmt.Fprintf(w, "Error scanning row: %v", err) - return - } - logs = append(logs, log) - } - - w.Header().Set("Content-Type", "application/json") - response := schema.Response_Type{ - Status: "OK", - Message: "Logs retrieved successfully", - RequestID: r.Context().Value(schema.RequestIDKey{}).(string), - Data: logs, - } - err = json.NewEncoder(w).Encode(response) - if err != nil { - w.WriteHeader(http.StatusInternalServerError) - } -} - -func GetLogsByUserID(w http.ResponseWriter, r *http.Request, db *sql.DB) { - if r.Method != http.MethodGet { - w.WriteHeader(http.StatusMethodNotAllowed) - fmt.Fprintf(w, "Method %s not allowed", r.Method) - return - } - userID := r.URL.Query().Get("user_id") + startTime := r.URL.Query().Get("start_time") + endTime := r.URL.Query().Get("end_time") + level := r.URL.Query().Get("level") - rows, err := db.Query("SELECT * FROM Logs WHERE UserID = ?", userID) - if err != nil { - w.WriteHeader(http.StatusInternalServerError) - fmt.Fprintf(w, "Error querying database: %v", err) - return - } - defer rows.Close() + query := "SELECT * FROM Logs WHERE " - var logs []schema.Log - for rows.Next() { - var log schema.Log - err = rows.Scan(&log.Time, &log.Level, &log.Message, &log.Subject, &log.UserID, &log.ProjectID) - if err != nil { - w.WriteHeader(http.StatusInternalServerError) - fmt.Fprintf(w, "Error scanning row: %v", err) - return - } - logs = append(logs, log) + if projectID != "" { + query += "ProjectID = '" + projectID + "' AND " } - - w.Header().Set("Content-Type", "application/json") - response := schema.Response_Type{ - Status: "OK", - Message: "Logs retrieved successfully", - RequestID: r.Context().Value(schema.RequestIDKey{}).(string), - Data: logs, + if userID != "" { + query += "UserID = '" + userID + "' AND " } - err = json.NewEncoder(w).Encode(response) - if err != nil { - w.WriteHeader(http.StatusInternalServerError) + if startTime != "" { + query += "Time > '" + startTime + "' AND " } -} - -func GetLogsInTimeRange(w http.ResponseWriter, r *http.Request, db *sql.DB) { - if r.Method != http.MethodGet { - w.WriteHeader(http.StatusMethodNotAllowed) - fmt.Fprintf(w, "Method %s not allowed", r.Method) - return + if endTime != "" { + query += "Time < '" + endTime + "' AND " } - - startTime := r.URL.Query().Get("start_time") - endTime := r.URL.Query().Get("end_time") - - rows, err := db.Query("SELECT * FROM Logs WHERE Time > ? AND Time < ?", startTime, endTime) - if err != nil { - w.WriteHeader(http.StatusInternalServerError) - fmt.Fprintf(w, "Error querying database: %v", err) - return + if level != "" { + query += "Level = '" + level + "' AND " } - defer rows.Close() - var logs []schema.Log - for rows.Next() { - var log schema.Log - err = rows.Scan(&log.Time, &log.Level, &log.Message, &log.Subject, &log.UserID, &log.ProjectID) - if err != nil { - w.WriteHeader(http.StatusInternalServerError) - fmt.Fprintf(w, "Error scanning row: %v", err) - return - } - logs = append(logs, log) + if projectID != "" || userID != "" || startTime != "" || endTime != "" || level != "" { + query = query[:len(query)-5] + } else { + query = query[:len(query)-7] } - w.Header().Set("Content-Type", "application/json") - response := schema.Response_Type{ - Status: "OK", - Message: "Logs retrieved successfully", - RequestID: r.Context().Value(schema.RequestIDKey{}).(string), - Data: logs, - } - err = json.NewEncoder(w).Encode(response) - if err != nil { - w.WriteHeader(http.StatusInternalServerError) - } -} - -func GetLogsByLevel(w http.ResponseWriter, r *http.Request, db *sql.DB) { - if r.Method != http.MethodGet { - w.WriteHeader(http.StatusMethodNotAllowed) - fmt.Fprintf(w, "Method %s not allowed", r.Method) - return - } - - level := r.URL.Query().Get("level") - - rows, err := db.Query("SELECT * FROM Logs WHERE Level = ?", level) + rows, err := db.Query(query) if err != nil { w.WriteHeader(http.StatusInternalServerError) fmt.Fprintf(w, "Error querying database: %v", err)

@@ -314,103 +120,50 @@ w.WriteHeader(http.StatusInternalServerError)

} } -func DeleteLogsByProjectID(w http.ResponseWriter, r *http.Request, db *sql.DB) { +// DeleteLogs function similar to above GetLogs that takes in the same parameters and deletes the logs from the database +func DeleteLogs(w http.ResponseWriter, r *http.Request, db *sql.DB) { if r.Method != http.MethodDelete { w.WriteHeader(http.StatusMethodNotAllowed) fmt.Fprintf(w, "Method %s not allowed", r.Method) return } - projectID := r.URL.Query().Get("project_id") - - stmt, err := db.Prepare("DELETE FROM Logs WHERE ProjectID = ?") - if err != nil { - w.WriteHeader(http.StatusInternalServerError) - fmt.Fprintf(w, "Error preparing SQL statement: %v", err) - return - } - defer stmt.Close() - - _, err = stmt.Exec(projectID) - if err != nil { - w.WriteHeader(http.StatusInternalServerError) - fmt.Fprintf(w, "Error deleting logs from database: %v", err) - return - } - - response := schema.Response_Type{ - Status: "OK", - Message: "Logs deleted successfully", - RequestID: r.Context().Value(schema.RequestIDKey{}).(string), - } - w.Header().Set("Content-Type", "application/json") - err = json.NewEncoder(w).Encode(response) - if err != nil { - w.WriteHeader(http.StatusInternalServerError) - } -} - -func DeleteLogsByUserID(w http.ResponseWriter, r *http.Request, db *sql.DB) { - if r.Method != http.MethodDelete { - w.WriteHeader(http.StatusMethodNotAllowed) - fmt.Fprintf(w, "Method %s not allowed", r.Method) - return - } - userID := r.URL.Query().Get("user_id") + startTime := r.URL.Query().Get("start_time") + endTime := r.URL.Query().Get("end_time") + level := r.URL.Query().Get("level") + query := "DELETE FROM Logs WHERE " - stmt, err := db.Prepare("DELETE FROM Logs WHERE UserID = ?") - if err != nil { - w.WriteHeader(http.StatusInternalServerError) - fmt.Fprintf(w, "Error preparing SQL statement: %v", err) - return + if projectID != "" { + query += "ProjectID = '" + projectID + "' AND " } - defer stmt.Close() - - _, err = stmt.Exec(userID) - if err != nil { - w.WriteHeader(http.StatusInternalServerError) - fmt.Fprintf(w, "Error deleting logs from database: %v", err) - return + if userID != "" { + query += "UserID = '" + userID + "' AND " } - - response := schema.Response_Type{ - Status: "OK", - Message: "Logs deleted successfully", - RequestID: r.Context().Value(schema.RequestIDKey{}).(string), + if startTime != "" { + query += "Time > '" + startTime + "' AND " } - w.Header().Set("Content-Type", "application/json") - err = json.NewEncoder(w).Encode(response) - if err != nil { - w.WriteHeader(http.StatusInternalServerError) + if endTime != "" { + query += "Time < '" + endTime + "' AND " } -} - -func DeleteLogsByTimeRange(w http.ResponseWriter, r *http.Request, db *sql.DB) { - if r.Method != http.MethodDelete { - w.WriteHeader(http.StatusMethodNotAllowed) - fmt.Fprintf(w, "Method %s not allowed", r.Method) - return + if level != "" { + query += "Level = '" + level + "' AND " } - startTime := r.URL.Query().Get("start_time") - endTime := r.URL.Query().Get("end_time") - - stmt, err := db.Prepare("DELETE FROM Logs WHERE Time > ? AND Time < ?") + query = query[:len(query)-5] + stmt, err := db.Prepare(query) if err != nil { w.WriteHeader(http.StatusInternalServerError) fmt.Fprintf(w, "Error preparing SQL statement: %v", err) return } defer stmt.Close() - - _, err = stmt.Exec(startTime, endTime) + _, err = stmt.Exec() if err != nil { w.WriteHeader(http.StatusInternalServerError) fmt.Fprintf(w, "Error deleting logs from database: %v", err) return } - response := schema.Response_Type{ Status: "OK", Message: "Logs deleted successfully",
M main.gomain.go

@@ -1,21 +1,3 @@

-// projects api endpoints should be: -// 1. GET /projects -// 2. POST /projects -// 3. GET /projects/{id} -// 4. PUT /projects/{id} -// 5. DELETE /projects/{id} -// logs api endpoints should be restful but also factor in query parameters -// 1. GET /logs -// 2 GET /logs?project_id={id} -// 3. GET /logs?user_id={id} -// 4. GET /logs?start_time={timestamp}&end_time={timestamp} -// 5. GET /logs?level={level} -// 6. POST /logs (this is for batch insertion of logs) -// 7. DELETE /logs?project_id={id} -// 8. DELETE /logs?user_id={id} -// 9. DELETE /logs?start_time={timestamp}&end_time={timestamp} -// 10. DELETE /logs?level={level} - package main import (

@@ -108,60 +90,11 @@

multiplexer.HandleFunc("/logs", func(w http.ResponseWriter, r *http.Request) { switch r.Method { case http.MethodPost: - internal.CreateLog(w, r, db_connection) - case http.MethodGet: - internal.GetAllLogs(w, r, db_connection) - default: - utils.Method_Not_Allowed_Handler(w, r) - } - }) - - multiplexer.HandleFunc("/logs/batch-insert", func(w http.ResponseWriter, r *http.Request) { - switch r.Method { - case http.MethodPost: internal.BatchInsertLogs(w, r, db_connection) - default: - utils.Method_Not_Allowed_Handler(w, r) - } - }) - - multiplexer.HandleFunc("/logs/project", func(w http.ResponseWriter, r *http.Request) { - switch r.Method { case http.MethodGet: - internal.GetLogsByProjectID(w, r, db_connection) - // case http.MethodDelete: - // internal.DeleteLogsByProjectID(w, r, db_c nnection) - default: - utils.Method_Not_Allowed_Handler(w, r) - } - }) - - multiplexer.HandleFunc("/logs/user", func(w http.ResponseWriter, r *http.Request) { - switch r.Method { - case http.MethodGet: - internal.GetLogsByUserID(w, r, db_connection) - // case http.MethodDelete: - // internal.DeleteLogsByUserID(w, r, db_connection) - default: - utils.Method_Not_Allowed_Handler(w, r) - } - }) - - multiplexer.HandleFunc("/logs/time", func(w http.ResponseWriter, r *http.Request) { - switch r.Method { - case http.MethodGet: - internal.GetLogsInTimeRange(w, r, db_connection) - // case http.MethodDelete: - // internal.DeleteLogsByTimeRange(w, r, db_connection) - default: - utils.Method_Not_Allowed_Handler(w, r) - } - }) - - multiplexer.HandleFunc("/logs/level", func(w http.ResponseWriter, r *http.Request) { - switch r.Method { - case http.MethodGet: - internal.GetLogsByLevel(w, r, db_connection) + internal.GetLogs(w, r, db_connection) + case http.MethodDelete: + internal.DeleteLogs(w, r, db_connection) default: utils.Method_Not_Allowed_Handler(w, r) }