auto commit

This commit is contained in:
Benaya Nathanael Yeroham 2025-09-24 16:53:21 +07:00
commit 4183f0740d
10 changed files with 714 additions and 0 deletions

49
cmd/server/main.go Normal file
View File

@ -0,0 +1,49 @@
package main
import (
"5803024008/internal/db"
"5803024008/internal/handlers"
"log"
"net/http"
"github.com/gorilla/mux"
)
func home(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("Hello broskie"))
}
func main() {
// Initialize database connection
database, err := db.InitDB()
if err != nil {
log.Fatal("Failed to connect to database:", err)
}
defer database.Close()
// Set database connection for handlers
handlers.SetDatabase(database)
// Use mux.NewRouter() to initialize Gorilla Mux router
r := mux.NewRouter()
r.HandleFunc("/", home).Methods("GET")
// Group management routes
r.HandleFunc("/groups", handlers.CreateGroupHandler).Methods("POST")
r.HandleFunc("/groups/{groupId}", handlers.GetGroupHandler).Methods("GET")
r.HandleFunc("/groups/{groupId}", handlers.RemoveGroupHandler).Methods("DELETE")
// Task management routes
r.HandleFunc("/groups/{groupId}/tasks", handlers.CreateTaskHandler).Methods("POST")
r.HandleFunc("/groups/{groupId}/tasks", handlers.DisplayTasksByGroupHandler).Methods("GET")
r.HandleFunc("/tasks", handlers.DisplayTasksHandler).Methods("GET")
r.HandleFunc("/tasks/{taskId}", handlers.GetTaskHandler).Methods("GET")
r.HandleFunc("/tasks/{taskId}", handlers.UpdateTaskHandler).Methods("PUT")
r.HandleFunc("/tasks/{taskId}", handlers.RemoveTaskHandler).Methods("DELETE")
r.HandleFunc("/tasks/{taskId}/done", handlers.MarkTaskDoneHandler).Methods("PUT")
// Use the http.ListenAndServe() function to start a new web server.
log.Print("Starting server on :4000")
err = http.ListenAndServe(":4000", r)
log.Fatal(err)
}

7
go.mod Normal file
View File

@ -0,0 +1,7 @@
module 5803024008
go 1.25.0
require github.com/gorilla/mux v1.8.1
require github.com/lib/pq v1.10.9

4
go.sum Normal file
View File

@ -0,0 +1,4 @@
github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY=
github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ=
github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw=
github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=

43
internal/db/connection.go Normal file
View File

@ -0,0 +1,43 @@
package db
import (
"database/sql"
"fmt"
_ "github.com/lib/pq"
)
const (
host = "202.46.28.160"
port = 45432
user = "5803024008"
password = "pw5803024008"
dbname = "tgs01_5803024008"
)
// InitDB returns a database connection
func InitDB() (*sql.DB, error) {
// connection string
psqlconn := fmt.Sprintf("host=%s port=%d user=%s password=%s dbname=%s sslmode=disable", host, port, user, password, dbname)
// open database
db, err := sql.Open("postgres", psqlconn)
if err != nil {
return nil, err
}
// check db
err = db.Ping()
if err != nil {
return nil, err
}
fmt.Println("Connected to database!")
return db, nil
}
func CheckError(err error) {
if err != nil {
panic(err)
}
}

View File

@ -0,0 +1,49 @@
package db
import (
"database/sql"
)
// CreateGroup inserts a new group into the database
func CreateGroup(db *sql.DB, groupName string) error {
query := `INSERT INTO groups (group_name) VALUES ($1)`
_, err := db.Exec(query, groupName)
return err
}
// GetGroupByID retrieves a group by its ID
func GetGroupByID(db *sql.DB, groupID int) (*Groups, error) {
query := `SELECT group_id, group_name FROM groups WHERE group_id = $1`
row := db.QueryRow(query, groupID)
var group Groups
err := row.Scan(&group.GroupId, &group.GroupName)
if err != nil {
return nil, err
}
return &group, nil
}
// RemoveGroup deletes a group and all its associated tasks
func RemoveGroup(db *sql.DB, groupID int) error {
tx, err := db.Begin()
if err != nil {
return err
}
defer tx.Rollback()
// Delete all tasks in the group first
_, err = tx.Exec("DELETE FROM tasks WHERE group_id = $1", groupID)
if err != nil {
return err
}
// Delete the group
_, err = tx.Exec("DELETE FROM groups WHERE group_id = $1", groupID)
if err != nil {
return err
}
return tx.Commit()
}

19
internal/db/models.go Normal file
View File

@ -0,0 +1,19 @@
package db
import (
"time"
)
type Groups struct{
GroupId int `json:"group_id"`
GroupName string `json:"group_name"`
}
type Tasks struct{
TaskID int `json:"task_id"`
TaskName string `json:"task_name"` // Maps to "task" column in SQL
TaskDescription string `json:"task_desc"` // Maps to "task_desc" column in SQL (changed from *int to string)
GroupID int `json:"group_id"`
IsDone bool `json:"is_done"` // Maps to "isdone" column in SQL
CreatedAt time.Time `json:"created_at"`
}

View File

@ -0,0 +1,99 @@
package db
import (
"database/sql"
"time"
)
// CreateTask inserts a new task into the database
func CreateTask(db *sql.DB, taskName string, taskDescription string, groupID int) error {
query := `INSERT INTO tasks (task_name, task_desc, group_id, is_done, created_at)
VALUES ($1, $2, $3, $4, $5)`
_, err := db.Exec(query, taskName, taskDescription, groupID, false, time.Now())
return err
}
// GetAllTasks retrieves all tasks from the database
func GetAllTasks(db *sql.DB) ([]Tasks, error) {
query := `SELECT task_id, task_name, task_desc, group_id, is_done, created_at
FROM tasks ORDER BY created_at DESC`
rows, err := db.Query(query)
if err != nil {
return nil, err
}
defer rows.Close()
var tasks []Tasks
for rows.Next() {
var task Tasks
err := rows.Scan(&task.TaskID, &task.TaskName, &task.TaskDescription,
&task.GroupID, &task.IsDone, &task.CreatedAt)
if err != nil {
return nil, err
}
tasks = append(tasks, task)
}
return tasks, nil
}
// GetTaskByID retrieves a specific task by its ID
func GetTaskByID(db *sql.DB, taskID int) (*Tasks, error) {
query := `SELECT task_id, task_name, task_desc, group_id, is_done, created_at
FROM tasks WHERE task_id = $1`
row := db.QueryRow(query, taskID)
var task Tasks
err := row.Scan(&task.TaskID, &task.TaskName, &task.TaskDescription,
&task.GroupID, &task.IsDone, &task.CreatedAt)
if err != nil {
return nil, err
}
return &task, nil
}
// GetTasksByGroupID retrieves all tasks for a specific group
func GetTasksByGroupID(db *sql.DB, groupID int) ([]Tasks, error) {
query := `SELECT task_id, task_name, task_desc, group_id, is_done, created_at
FROM tasks WHERE group_id = $1`
rows, err := db.Query(query, groupID)
if err != nil {
return nil, err
}
defer rows.Close()
var tasks []Tasks
for rows.Next() {
var task Tasks
err := rows.Scan(&task.TaskID, &task.TaskName, &task.TaskDescription,
&task.GroupID, &task.IsDone, &task.CreatedAt)
if err != nil {
return nil, err
}
tasks = append(tasks, task)
}
return tasks, nil
}
// MarkTaskAsDone updates a task's status to completed
func MarkTaskAsDone(db *sql.DB, taskID int) error {
query := `UPDATE tasks SET is_done = true WHERE task_id = $1`
_, err := db.Exec(query, taskID)
return err
}
// RemoveTask deletes a task from the database
func RemoveTask(db *sql.DB, taskID int) error {
query := `DELETE FROM tasks WHERE task_id = $1`
_, err := db.Exec(query, taskID)
return err
}
// UpdateTask updates task name and description
func UpdateTask(db *sql.DB, taskID int, taskName string, taskDescription string) error {
query := `UPDATE tasks SET task_name = $1, task_desc = $2 WHERE task_id = $3`
_, err := db.Exec(query, taskName, taskDescription, taskID)
return err
}

View File

@ -0,0 +1,139 @@
package handlers
import (
"5803024008/internal/db"
"database/sql"
"encoding/json"
"net/http"
"strconv"
"github.com/gorilla/mux"
)
type GroupRequest struct {
GroupName string `json:"group_name"`
}
type GroupResponse struct {
GroupID int `json:"group_id"`
GroupName string `json:"group_name"`
}
type ErrorResponse struct {
Error string `json:"error"`
}
// Database connection - you'll need to inject this or use a global variable
var database *sql.DB
func SetDatabase(db *sql.DB) {
database = db
}
func CreateGroupHandler(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
var req GroupRequest
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
w.WriteHeader(http.StatusBadRequest)
json.NewEncoder(w).Encode(ErrorResponse{Error: "Invalid JSON format"})
return
}
if req.GroupName == "" {
w.WriteHeader(http.StatusBadRequest)
json.NewEncoder(w).Encode(ErrorResponse{Error: "Group name is required"})
return
}
if err := db.CreateGroup(database, req.GroupName); err != nil {
w.WriteHeader(http.StatusInternalServerError)
json.NewEncoder(w).Encode(ErrorResponse{Error: "Failed to create group"})
return
}
w.WriteHeader(http.StatusCreated)
json.NewEncoder(w).Encode(map[string]string{"message": "Group created successfully"})
}
func DisplayTasksByGroupHandler(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
vars := mux.Vars(r)
groupIDStr := vars["groupId"]
groupID, err := strconv.Atoi(groupIDStr)
if err != nil {
w.WriteHeader(http.StatusBadRequest)
json.NewEncoder(w).Encode(ErrorResponse{Error: "Invalid group ID"})
return
}
tasks, err := db.GetTasksByGroupID(database, groupID)
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
json.NewEncoder(w).Encode(ErrorResponse{Error: "Failed to retrieve tasks"})
return
}
w.WriteHeader(http.StatusOK)
json.NewEncoder(w).Encode(tasks)
}
func RemoveGroupHandler(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
vars := mux.Vars(r)
groupIDStr := vars["groupId"]
groupID, err := strconv.Atoi(groupIDStr)
if err != nil {
w.WriteHeader(http.StatusBadRequest)
json.NewEncoder(w).Encode(ErrorResponse{Error: "Invalid group ID"})
return
}
if err := db.RemoveGroup(database, groupID); err != nil {
w.WriteHeader(http.StatusInternalServerError)
json.NewEncoder(w).Encode(ErrorResponse{Error: "Failed to remove group"})
return
}
w.WriteHeader(http.StatusOK)
json.NewEncoder(w).Encode(map[string]string{"message": "Group removed successfully"})
}
func GetGroupHandler(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
vars := mux.Vars(r)
groupIDStr := vars["groupId"]
groupID, err := strconv.Atoi(groupIDStr)
if err != nil {
w.WriteHeader(http.StatusBadRequest)
json.NewEncoder(w).Encode(ErrorResponse{Error: "Invalid group ID"})
return
}
group, err := db.GetGroupByID(database, groupID)
if err == sql.ErrNoRows {
w.WriteHeader(http.StatusNotFound)
json.NewEncoder(w).Encode(ErrorResponse{Error: "Group not found"})
return
}
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
json.NewEncoder(w).Encode(ErrorResponse{Error: "Failed to retrieve group"})
return
}
response := GroupResponse{
GroupID: group.GroupId,
GroupName: group.GroupName,
}
w.WriteHeader(http.StatusOK)
json.NewEncoder(w).Encode(response)
}

View File

@ -0,0 +1,194 @@
package handlers
import (
"5803024008/internal/db"
"database/sql"
"encoding/json"
"net/http"
"strconv"
"github.com/gorilla/mux"
)
type TaskRequest struct {
TaskName string `json:"task_name"`
TaskDescription string `json:"task_desc"` // Changed from *string to string to match SQL NOT NULL
}
type TaskUpdateRequest struct {
TaskName string `json:"task_name"`
TaskDescription string `json:"task_desc"` // Changed from *string to string
}
func CreateTaskHandler(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
vars := mux.Vars(r)
groupIDStr := vars["groupId"]
groupID, err := strconv.Atoi(groupIDStr)
if err != nil {
w.WriteHeader(http.StatusBadRequest)
json.NewEncoder(w).Encode(ErrorResponse{Error: "Invalid group ID"})
return
}
var req TaskRequest
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
w.WriteHeader(http.StatusBadRequest)
json.NewEncoder(w).Encode(ErrorResponse{Error: "Invalid JSON format"})
return
}
if req.TaskName == "" {
w.WriteHeader(http.StatusBadRequest)
json.NewEncoder(w).Encode(ErrorResponse{Error: "Task name is required"})
return
}
if req.TaskDescription == "" {
w.WriteHeader(http.StatusBadRequest)
json.NewEncoder(w).Encode(ErrorResponse{Error: "Task description is required"})
return
}
if err := db.CreateTask(database, req.TaskName, req.TaskDescription, groupID); err != nil {
w.WriteHeader(http.StatusInternalServerError)
json.NewEncoder(w).Encode(ErrorResponse{Error: "Failed to create task"})
return
}
w.WriteHeader(http.StatusCreated)
json.NewEncoder(w).Encode(map[string]string{"message": "Task created successfully"})
}
func DisplayTasksHandler(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
tasks, err := db.GetAllTasks(database)
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
json.NewEncoder(w).Encode(ErrorResponse{Error: "Failed to retrieve tasks"})
return
}
w.WriteHeader(http.StatusOK)
json.NewEncoder(w).Encode(tasks)
}
func GetTaskHandler(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
vars := mux.Vars(r)
taskIDStr := vars["taskId"]
taskID, err := strconv.Atoi(taskIDStr)
if err != nil {
w.WriteHeader(http.StatusBadRequest)
json.NewEncoder(w).Encode(ErrorResponse{Error: "Invalid task ID"})
return
}
task, err := db.GetTaskByID(database, taskID)
if err == sql.ErrNoRows {
w.WriteHeader(http.StatusNotFound)
json.NewEncoder(w).Encode(ErrorResponse{Error: "Task not found"})
return
}
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
json.NewEncoder(w).Encode(ErrorResponse{Error: "Failed to retrieve task"})
return
}
w.WriteHeader(http.StatusOK)
json.NewEncoder(w).Encode(task)
}
func MarkTaskDoneHandler(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
vars := mux.Vars(r)
taskIDStr := vars["taskId"]
taskID, err := strconv.Atoi(taskIDStr)
if err != nil {
w.WriteHeader(http.StatusBadRequest)
json.NewEncoder(w).Encode(ErrorResponse{Error: "Invalid task ID"})
return
}
if err := db.MarkTaskAsDone(database, taskID); err != nil {
w.WriteHeader(http.StatusInternalServerError)
json.NewEncoder(w).Encode(ErrorResponse{Error: "Failed to mark task as done"})
return
}
w.WriteHeader(http.StatusOK)
json.NewEncoder(w).Encode(map[string]string{"message": "Task marked as done successfully"})
}
func RemoveTaskHandler(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
vars := mux.Vars(r)
taskIDStr := vars["taskId"]
taskID, err := strconv.Atoi(taskIDStr)
if err != nil {
w.WriteHeader(http.StatusBadRequest)
json.NewEncoder(w).Encode(ErrorResponse{Error: "Invalid task ID"})
return
}
if err := db.RemoveTask(database, taskID); err != nil {
w.WriteHeader(http.StatusInternalServerError)
json.NewEncoder(w).Encode(ErrorResponse{Error: "Failed to remove task"})
return
}
w.WriteHeader(http.StatusOK)
json.NewEncoder(w).Encode(map[string]string{"message": "Task removed successfully"})
}
func UpdateTaskHandler(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
vars := mux.Vars(r)
taskIDStr := vars["taskId"]
taskID, err := strconv.Atoi(taskIDStr)
if err != nil {
w.WriteHeader(http.StatusBadRequest)
json.NewEncoder(w).Encode(ErrorResponse{Error: "Invalid task ID"})
return
}
var req TaskUpdateRequest
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
w.WriteHeader(http.StatusBadRequest)
json.NewEncoder(w).Encode(ErrorResponse{Error: "Invalid JSON format"})
return
}
if req.TaskName == "" {
w.WriteHeader(http.StatusBadRequest)
json.NewEncoder(w).Encode(ErrorResponse{Error: "Task name is required"})
return
}
if req.TaskDescription == "" {
w.WriteHeader(http.StatusBadRequest)
json.NewEncoder(w).Encode(ErrorResponse{Error: "Task description is required"})
return
}
if err := db.UpdateTask(database, taskID, req.TaskName, req.TaskDescription); err != nil {
w.WriteHeader(http.StatusInternalServerError)
json.NewEncoder(w).Encode(ErrorResponse{Error: "Failed to update task"})
return
}
w.WriteHeader(http.StatusOK)
json.NewEncoder(w).Encode(map[string]string{"message": "Task updated successfully"})
}

111
main.go Normal file
View File

@ -0,0 +1,111 @@
package main
import (
"log"
"net/http"
"github.com/gorilla/mux"
)
func createGroupHandler(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusCreated)
w.Write([]byte("Group Created"))
}
func createTaskHandler(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
groupID := vars["groupId"]
w.WriteHeader(http.StatusCreated)
w.Write([]byte("Task Created in Group ID: " + groupID))
}
func displayTasksHandler(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
w.Write([]byte("Displaying All Tasks"))
}
func displayTasksByGroupHandler(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
groupID := vars["groupId"]
w.WriteHeader(http.StatusOK)
w.Write([]byte("Displaying Tasks for Group ID: " + groupID))
}
func getTaskHandler(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
taskID := vars["taskId"]
w.WriteHeader(http.StatusOK)
w.Write([]byte("Displaying Task ID: " + taskID))
}
func markTaskDoneHandler(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
taskID := vars["taskId"]
w.WriteHeader(http.StatusOK)
w.Write([]byte("Task ID " + taskID + " Marked as Done"))
}
func removeTaskHandler(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
taskID := vars["taskId"]
w.WriteHeader(http.StatusOK)
w.Write([]byte("Task ID " + taskID + " Removed"))
}
func removeGroupHandler(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
groupID := vars["groupId"]
w.WriteHeader(http.StatusOK)
w.Write([]byte("Group ID " + groupID + " Removed"))
}
func updateTaskHandler(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
taskID := vars["taskId"]
w.WriteHeader(http.StatusOK)
w.Write([]byte("Task ID " + taskID + " Updated"))
}
func getGroupHandler(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
groupID := vars["groupId"]
w.WriteHeader(http.StatusOK)
w.Write([]byte("Displaying Group ID: " + groupID))
}
func home(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("Hello from Task Management API"))
}
func main() {
// Use mux.NewRouter() to initialize Gorilla Mux router
r := mux.NewRouter()
r.HandleFunc("/", home).Methods("GET")
// Group management routes
r.HandleFunc("/groups", createGroupHandler).Methods("POST")
r.HandleFunc("/groups/{groupId}", getGroupHandler).Methods("GET")
r.HandleFunc("/groups/{groupId}", removeGroupHandler).Methods("DELETE")
// Task management routes
r.HandleFunc("/groups/{groupId}/tasks", createTaskHandler).Methods("POST")
r.HandleFunc("/groups/{groupId}/tasks", displayTasksByGroupHandler).Methods("GET")
r.HandleFunc("/tasks", displayTasksHandler).Methods("GET")
r.HandleFunc("/tasks/{taskId}", getTaskHandler).Methods("GET")
r.HandleFunc("/tasks/{taskId}", updateTaskHandler).Methods("PUT")
r.HandleFunc("/tasks/{taskId}", removeTaskHandler).Methods("DELETE")
r.HandleFunc("/tasks/{taskId}/done", markTaskDoneHandler).Methods("PUT")
// Use the http.ListenAndServe() function to start a new web server.
log.Print("Starting server on :4000")
err := http.ListenAndServe(":4000", r)
log.Fatal(err)
}