diff --git a/3layerarch/.DS_Store b/3layerarch/.DS_Store new file mode 100644 index 0000000..74054d4 Binary files /dev/null and b/3layerarch/.DS_Store differ diff --git a/3layerarch/.idea/.gitignore b/3layerarch/.idea/.gitignore new file mode 100644 index 0000000..13566b8 --- /dev/null +++ b/3layerarch/.idea/.gitignore @@ -0,0 +1,8 @@ +# Default ignored files +/shelf/ +/workspace.xml +# Editor-based HTTP Client requests +/httpRequests/ +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml diff --git a/3layerarch/.idea/3layerarch.iml b/3layerarch/.idea/3layerarch.iml new file mode 100644 index 0000000..5e764c4 --- /dev/null +++ b/3layerarch/.idea/3layerarch.iml @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/3layerarch/.idea/modules.xml b/3layerarch/.idea/modules.xml new file mode 100644 index 0000000..88952d1 --- /dev/null +++ b/3layerarch/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/3layerarch/go.mod b/3layerarch/go.mod new file mode 100644 index 0000000..b2aecc5 --- /dev/null +++ b/3layerarch/go.mod @@ -0,0 +1,7 @@ +module 3layerarch + +go 1.24 + +require github.com/go-sql-driver/mysql v1.9.3 + +require filippo.io/edwards25519 v1.1.0 // indirect diff --git a/3layerarch/go.sum b/3layerarch/go.sum new file mode 100644 index 0000000..4bcdcfa --- /dev/null +++ b/3layerarch/go.sum @@ -0,0 +1,4 @@ +filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA= +filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4= +github.com/go-sql-driver/mysql v1.9.3 h1:U/N249h2WzJ3Ukj8SowVFjdtZKfu9vlLZxjPXV1aweo= +github.com/go-sql-driver/mysql v1.9.3/go.mod h1:qn46aNg1333BRMNU69Lq93t8du/dwxI64Gl8i5p1WMU= diff --git a/3layerarch/handler/.DS_Store b/3layerarch/handler/.DS_Store new file mode 100644 index 0000000..2595feb Binary files /dev/null and b/3layerarch/handler/.DS_Store differ diff --git a/3layerarch/handler/task/handler.go b/3layerarch/handler/task/handler.go new file mode 100644 index 0000000..9e8f435 --- /dev/null +++ b/3layerarch/handler/task/handler.go @@ -0,0 +1,115 @@ +package taskhandler + +import ( + "encoding/json" + "fmt" + "io" + "net/http" + "strconv" + + "3layerarch/models" +) + +type TaskService interface { + CreateTask(t models.Task) error + GetTask(id int) (models.Task, error) + ViewTasks() ([]models.Task, error) + UpdateTask(id int) error + DeleteTask(id int) error +} + +type Handler struct { + Service TaskService +} + +func New(service TaskService) *Handler { + return &Handler{Service: service} +} + +func (h *Handler) CreateTask(w http.ResponseWriter, r *http.Request) { + body, err := io.ReadAll(r.Body) + if err != nil || len(body) == 0 { + http.Error(w, "Empty or unreadable body", http.StatusBadRequest) + return + } + var t models.Task + if err := json.Unmarshal(body, &t); err != nil { + http.Error(w, "Invalid JSON input", http.StatusBadRequest) + return + } + if err := h.Service.CreateTask(t); err != nil { + http.Error(w, err.Error(), http.StatusBadRequest) + return + } + w.WriteHeader(http.StatusCreated) +} + +func (h *Handler) GetTask(w http.ResponseWriter, r *http.Request) { + idStr := r.PathValue("id") + if idStr == "" { + http.Error(w, "Missing ID", http.StatusBadRequest) + return + } + id, err := strconv.Atoi(idStr) + if err != nil { + http.Error(w, "Invalid ID format", http.StatusBadRequest) + return + } + t, err := h.Service.GetTask(id) + if err != nil { + http.Error(w, err.Error(), http.StatusBadRequest) + return + } + b, _ := json.Marshal(t) + if _, err := w.Write(b); err != nil { + fmt.Printf("failed to write response: %v\n", err) + } +} + +func (h *Handler) ViewTasks(w http.ResponseWriter, _ *http.Request) { + tasks, err := h.Service.ViewTasks() + if err != nil { + http.Error(w, "Failed to fetch tasks", http.StatusInternalServerError) + return + } + b, _ := json.Marshal(tasks) + if _, err := w.Write(b); err != nil { + fmt.Printf("failed to write response: %v\n", err) + } +} + +func (h *Handler) UpdateTask(w http.ResponseWriter, r *http.Request) { + idStr := r.PathValue("id") + if idStr == "" { + http.Error(w, "Missing ID", http.StatusBadRequest) + return + } + id, err := strconv.Atoi(idStr) + if err != nil { + http.Error(w, "Invalid ID format", http.StatusBadRequest) + return + } + if err := h.Service.UpdateTask(id); err != nil { + http.Error(w, err.Error(), http.StatusBadRequest) + return + } + w.WriteHeader(http.StatusOK) +} + +func (h *Handler) DeleteTask(w http.ResponseWriter, r *http.Request) { + idStr := r.PathValue("id") + if idStr == "" { + http.Error(w, "Missing ID", http.StatusBadRequest) + return + } + id, err := strconv.Atoi(idStr) + if err != nil { + http.Error(w, "Invalid ID format", http.StatusBadRequest) + return + } + if err := h.Service.DeleteTask(id); err != nil { + http.Error(w, err.Error(), http.StatusBadRequest) + return + } + w.WriteHeader(http.StatusOK) +} diff --git a/3layerarch/handler/user/handler.go b/3layerarch/handler/user/handler.go new file mode 100644 index 0000000..14624ca --- /dev/null +++ b/3layerarch/handler/user/handler.go @@ -0,0 +1,64 @@ +package userhandler + +import ( + "encoding/json" + "fmt" + "io" + "net/http" + "strconv" + + "3layerarch/models" +) + +type UserService interface { + CreateUser(u models.User) error + GetUser(id int) (models.User, error) +} + +type Handler struct { + Service UserService +} + +func New(service UserService) *Handler { + return &Handler{Service: service} +} + +func (h *Handler) CreateUser(w http.ResponseWriter, r *http.Request) { + body, err := io.ReadAll(r.Body) + if err != nil || len(body) == 0 { + http.Error(w, "Empty or unreadable body", http.StatusBadRequest) + return + } + var u models.User + if err := json.Unmarshal(body, &u); err != nil { + http.Error(w, "Invalid JSON input", http.StatusBadRequest) + return + } + if err := h.Service.CreateUser(u); err != nil { + http.Error(w, err.Error(), http.StatusBadRequest) + return + } + w.WriteHeader(http.StatusCreated) +} + +func (h *Handler) GetUser(w http.ResponseWriter, r *http.Request) { + idStr := r.PathValue("id") + if idStr == "" { + http.Error(w, "Missing ID", http.StatusBadRequest) + return + } + id, err := strconv.Atoi(idStr) + if err != nil { + http.Error(w, "Invalid ID format", http.StatusBadRequest) + return + } + u, err := h.Service.GetUser(id) + if err != nil { + http.Error(w, err.Error(), http.StatusBadRequest) + return + } + b, _ := json.Marshal(u) + if _, err := w.Write(b); err != nil { + fmt.Printf("failed to write response: %v\n", err) + } +} diff --git a/3layerarch/main.go b/3layerarch/main.go new file mode 100644 index 0000000..1f434a0 --- /dev/null +++ b/3layerarch/main.go @@ -0,0 +1,83 @@ +package main + +import ( + "database/sql" + "log" + "net/http" + "time" + + taskhandler "3layerarch/handler/task" + userhandler "3layerarch/handler/user" + + taskservice "3layerarch/service/task" + userservice "3layerarch/service/user" + + taskstore "3layerarch/store/task" + userstore "3layerarch/store/user" + + _ "github.com/go-sql-driver/mysql" +) + +func main() { + db, err := sql.Open("mysql", "root:root123@tcp(localhost:3306)/test_db") + if err != nil { + log.Fatal("DB connection error:", err) + } + defer func() { + if err := db.Close(); err != nil { + log.Println("Error closing DB:", err) + } + }() + + // Ensure TASKS table exists + _, err = db.Exec(`CREATE TABLE IF NOT EXISTS TASKS ( + id INT AUTO_INCREMENT PRIMARY KEY, + task TEXT, + completed BOOL DEFAULT FALSE, + user_id int + );`) + if err != nil { + log.Fatal("Failed to create TASKS table:", err) + } + + // Ensure USERS table exists + _, err = db.Exec(`CREATE TABLE IF NOT EXISTS USERS ( + id INT AUTO_INCREMENT PRIMARY KEY, + name VARCHAR(100) + );`) + if err != nil { + log.Fatal("Failed to create USERS table:", err) + } + + // User dependency setup + userStore := userstore.New(db) + userService := userservice.New(userStore) + userHandler := userhandler.New(userService) + + // Task dependency setup + taskStore := taskstore.New(db) + taskService := taskservice.New(taskStore, userService) + taskHandler := taskhandler.New(taskService) + + // Task routes + http.HandleFunc("POST /task", taskHandler.CreateTask) + http.HandleFunc("GET /task", taskHandler.ViewTasks) + http.HandleFunc("GET /task/{id}", taskHandler.GetTask) + http.HandleFunc("PUT /task/{id}", taskHandler.UpdateTask) + http.HandleFunc("DELETE /task/{id}", taskHandler.DeleteTask) + + // User routes + http.HandleFunc("POST /user", userHandler.CreateUser) + http.HandleFunc("GET /user/{id}", userHandler.GetUser) + + // Server configuration + srv := &http.Server{ + Addr: ":8080", + ReadTimeout: 5 * time.Second, + WriteTimeout: 10 * time.Second, + IdleTimeout: 120 * time.Second, + } + + //log.Println("Server running on http://localhost:8080") + log.Fatal(srv.ListenAndServe()) +} diff --git a/3layerarch/models/task.go b/3layerarch/models/task.go new file mode 100644 index 0000000..e29c725 --- /dev/null +++ b/3layerarch/models/task.go @@ -0,0 +1,8 @@ +package models + +type Task struct { + ID int `json:"id"` + Task string `json:"task"` + Completed bool `json:"completed"` + UserID int `json:"user_id"` +} diff --git a/3layerarch/models/user.go b/3layerarch/models/user.go new file mode 100644 index 0000000..7f6eacd --- /dev/null +++ b/3layerarch/models/user.go @@ -0,0 +1,6 @@ +package models + +type User struct { + ID int `json:"id"` + Name string `json:"name"` +} diff --git a/3layerarch/service/.DS_Store b/3layerarch/service/.DS_Store new file mode 100644 index 0000000..f69ce9e Binary files /dev/null and b/3layerarch/service/.DS_Store differ diff --git a/3layerarch/service/task/service.go b/3layerarch/service/task/service.go new file mode 100644 index 0000000..31234fa --- /dev/null +++ b/3layerarch/service/task/service.go @@ -0,0 +1,90 @@ +package taskservice + +import ( + //"3layerarch/handler/userhandler" + "3layerarch/models" + "database/sql" + "errors" + "fmt" +) + +type TaskStore interface { + CreateTask(t models.Task) error + GetTask(id int) (models.Task, error) + ViewTasks() ([]models.Task, error) + UpdateTask(id int) error + DeleteTask(id int) error +} + +type UserService interface { + GetUser(id int) (models.User, error) +} + +type Service struct { + TaskStore TaskStore + UserService UserService +} + +func New(ts TaskStore, us UserService) *Service { + return &Service{TaskStore: ts, UserService: us} +} + +func (s *Service) CreateTask(t models.Task) error { + fmt.Println("CreateTask received:", t) + + if t.Task == "" { + return errors.New("task cannot be empty") + } + // Validate user existence before creating task + _, err := s.UserService.GetUser(t.UserID) + if err != nil { + return errors.New("user ID not found") + } + return s.TaskStore.CreateTask(t) +} + +func (s *Service) GetTask(id int) (models.Task, error) { + if id <= 0 { + return models.Task{}, errors.New("invalid task ID") + } + task, err := s.TaskStore.GetTask(id) + if err != nil { + if err == sql.ErrNoRows { + return models.Task{}, errors.New("task not found") + } + return models.Task{}, err + } + return task, nil +} + +func (s *Service) ViewTasks() ([]models.Task, error) { + return s.TaskStore.ViewTasks() +} + +func (s *Service) UpdateTask(id int) error { + if id <= 0 { + return errors.New("invalid task ID") + } + _, err := s.TaskStore.GetTask(id) + if err != nil { + if err == sql.ErrNoRows { + return errors.New("task not found") + } + return err + } + return s.TaskStore.UpdateTask(id) +} + +func (s *Service) DeleteTask(id int) error { + if id <= 0 { + return errors.New("invalid task ID") + } + _, err := s.TaskStore.GetTask(id) + if err != nil { + if err == sql.ErrNoRows { + return errors.New("task not found") + } + return err + } + return s.TaskStore.DeleteTask(id) +} diff --git a/3layerarch/service/user/service.go b/3layerarch/service/user/service.go new file mode 100644 index 0000000..fab65aa --- /dev/null +++ b/3layerarch/service/user/service.go @@ -0,0 +1,41 @@ +package userservice + +import ( + "3layerarch/models" + "database/sql" + "errors" +) + +type UserStore interface { + CreateUser(u models.User) error + GetUser(id int) (models.User, error) +} + +type Service struct { + Store UserStore +} + +func New(store UserStore) *Service { + return &Service{Store: store} +} + +func (s *Service) CreateUser(u models.User) error { + if u.Name == "" { + return errors.New("user name cannot be empty") + } + return s.Store.CreateUser(u) +} + +func (s *Service) GetUser(id int) (models.User, error) { + if id <= 0 { + return models.User{}, errors.New("invalid user ID") + } + u, err := s.Store.GetUser(id) + if err != nil { + if err == sql.ErrNoRows { + return models.User{}, errors.New("user not found") + } + return models.User{}, err + } + return u, nil +} diff --git a/3layerarch/store/.DS_Store b/3layerarch/store/.DS_Store new file mode 100644 index 0000000..090e2e4 Binary files /dev/null and b/3layerarch/store/.DS_Store differ diff --git a/3layerarch/store/task/store.go b/3layerarch/store/task/store.go new file mode 100644 index 0000000..ca2bc15 --- /dev/null +++ b/3layerarch/store/task/store.go @@ -0,0 +1,59 @@ +package taskstore + +import ( + "3layerarch/models" + "database/sql" + "log" +) + +type Store struct { + db *sql.DB +} + +func New(db *sql.DB) *Store { + return &Store{db: db} +} + +func (s *Store) CreateTask(t models.Task) error { + _, err := s.db.Exec("INSERT INTO TASKS (task, completed, user_id) VALUES (?, ?, ?)", t.Task, t.Completed, t.UserID) + return err +} + +func (s *Store) GetTask(id int) (models.Task, error) { + var t models.Task + err := s.db.QueryRow("SELECT id, task, completed, user_id FROM TASKS WHERE id = ?", id). + Scan(&t.ID, &t.Task, &t.Completed, &t.UserID) + return t, err +} + +func (s *Store) ViewTasks() ([]models.Task, error) { + rows, err := s.db.Query("SELECT id, task, completed, user_id FROM TASKS") + if err != nil { + return nil, err + } + defer func() { + if err := rows.Close(); err != nil { + log.Println("Error closing rows:", err) + } + }() + + var tasks []models.Task + for rows.Next() { + var t models.Task + if err := rows.Scan(&t.ID, &t.Task, &t.Completed, &t.UserID); err != nil { + return nil, err + } + tasks = append(tasks, t) + } + return tasks, nil +} + +func (s *Store) UpdateTask(id int) error { + _, err := s.db.Exec("UPDATE TASKS SET completed = true WHERE id = ?", id) + return err +} + +func (s *Store) DeleteTask(id int) error { + _, err := s.db.Exec("DELETE FROM TASKS WHERE id = ?", id) + return err +} diff --git a/3layerarch/store/user/store.go b/3layerarch/store/user/store.go new file mode 100644 index 0000000..bb00c47 --- /dev/null +++ b/3layerarch/store/user/store.go @@ -0,0 +1,25 @@ +package userstore + +import ( + "3layerarch/models" + "database/sql" +) + +type Store struct { + db *sql.DB +} + +func New(db *sql.DB) *Store { + return &Store{db: db} +} + +func (s *Store) CreateUser(u models.User) error { + _, err := s.db.Exec("INSERT INTO USERS (name) VALUES (?)", u.Name) + return err +} + +func (s *Store) GetUser(id int) (models.User, error) { + var u models.User + err := s.db.QueryRow("SELECT id, name FROM USERS WHERE id = ?", id).Scan(&u.ID, &u.Name) + return u, err +}