Skip to content

Commit

Permalink
Merge pull request stakwork#1989 from aliraza556/mplement-ticket-crud…
Browse files Browse the repository at this point in the history
…-endpoints-and-util-function

Implement Ticket CRUD Endpoints With Supporting Utilities Functions
  • Loading branch information
elraphty authored Nov 27, 2024
2 parents 4c99272 + 0f8c65d commit 3d51ab7
Show file tree
Hide file tree
Showing 10 changed files with 850 additions and 51 deletions.
4 changes: 4 additions & 0 deletions db/interface.go
Original file line number Diff line number Diff line change
Expand Up @@ -194,4 +194,8 @@ type Database interface {
DeleteProcessingMapByKey(processType, processKey string) error
DeleteProcessingMap(id uint) error
ProcessReversePayments(paymentId uint) error
CreateOrEditTicket(ticket *Tickets) (Tickets, error)
GetTicket(uuid string) (Tickets, error)
UpdateTicket(ticket Tickets) (Tickets, error)
DeleteTicket(uuid string) error
}
3 changes: 2 additions & 1 deletion db/structs.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"errors"
"time"

"github.com/google/uuid"
"github.com/gorilla/websocket"
"github.com/lib/pq"
"gorm.io/gorm"
Expand Down Expand Up @@ -952,7 +953,7 @@ const (
)

type Tickets struct {
UUID string `gorm:"primaryKey;type:uuid;default:gen_random_uuid()"`
UUID uuid.UUID `gorm:"primaryKey;type:uuid;default:gen_random_uuid()"`
FeatureUUID string `gorm:"type:uuid;not null;index:composite_index" json:"feature_uuid" validate:"required"`
Features WorkspaceFeatures `gorm:"foreignKey:FeatureUUID;references:Uuid"`
PhaseUUID string `gorm:"type:uuid;not null;index:phase_index" json:"phase_uuid" validate:"required"`
Expand Down
56 changes: 54 additions & 2 deletions db/tickets.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,14 @@ import (
"errors"
"fmt"
"time"

"github.com/google/uuid"
"gorm.io/gorm"
)

func (db database) CreateOrEditTicket(ticket *Tickets) (Tickets, error) {

if ticket.UUID == "" || ticket.FeatureUUID == "" || ticket.PhaseUUID == "" || ticket.Name == "" {
if ticket.UUID == uuid.Nil || ticket.FeatureUUID == "" || ticket.PhaseUUID == "" || ticket.Name == "" {
return Tickets{}, errors.New("required fields are missing")
}

Expand Down Expand Up @@ -43,8 +46,57 @@ func (db database) GetTicket(uuid string) (Tickets, error) {
}

if results.RowsAffected == 0 {
return Tickets{}, fmt.Errorf("failed to get ticket: %w", results.Error)
return Tickets{}, fmt.Errorf("ticket not found")
}

return ticket, nil
}

func (db database) UpdateTicket(ticket Tickets) (Tickets, error) {
if ticket.UUID == uuid.Nil {
return Tickets{}, errors.New("ticket UUID is required")
}

if ticket.FeatureUUID == "" || ticket.PhaseUUID == "" || ticket.Name == "" {
return Tickets{}, errors.New("feature_uuid, phase_uuid, and name are required")
}

var existingTicket Tickets
result := db.db.Where("uuid = ?", ticket.UUID).First(&existingTicket)

now := time.Now()
ticket.UpdatedAt = now

if result.Error != nil {
if errors.Is(result.Error, gorm.ErrRecordNotFound) {
ticket.CreatedAt = now
if err := db.db.Create(&ticket).Error; err != nil {
return Tickets{}, fmt.Errorf("failed to create ticket: %w", err)
}
return ticket, nil
}
return Tickets{}, fmt.Errorf("database error: %w", result.Error)
}

if err := db.db.Model(&existingTicket).Updates(ticket).Error; err != nil {
return Tickets{}, fmt.Errorf("failed to update ticket: %w", err)
}

var updatedTicket Tickets
if err := db.db.Where("uuid = ?", ticket.UUID).First(&updatedTicket).Error; err != nil {
return Tickets{}, fmt.Errorf("failed to fetch updated ticket: %w", err)
}

return updatedTicket, nil
}

func (db database) DeleteTicket(uuid string) error {
result := db.db.Where("uuid = ?", uuid).Delete(&Tickets{})
if result.Error != nil {
return fmt.Errorf("failed to delete ticket: %w", result.Error)
}
if result.RowsAffected == 0 {
return errors.New("ticket not found")
}
return nil
}
10 changes: 5 additions & 5 deletions db/tickets_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ func TestCreateOrEditTicket(t *testing.T) {
}

ticket := Tickets{
UUID: uuid.New().String(),
UUID: uuid.New(),
FeatureUUID: workspaceFeatures.Uuid,
PhaseUUID: featurePhase.Uuid,
Name: "test ticket",
Expand All @@ -79,7 +79,7 @@ func TestCreateOrEditTicket(t *testing.T) {
// test that an error is returned if the required fields are missing
t.Run("test that an error is returned if the required fields are missing", func(t *testing.T) {
ticket := Tickets{
UUID: uuid.New().String(),
UUID: uuid.New(),
FeatureUUID: "",
PhaseUUID: "",
Name: "test ticket",
Expand All @@ -96,7 +96,7 @@ func TestCreateOrEditTicket(t *testing.T) {
// test that an error is thrown if the FeatureUUID, and PhaseUUID does not exists
t.Run("test that an error is returned if the required fields are missing", func(t *testing.T) {
ticket := Tickets{
UUID: uuid.New().String(),
UUID: uuid.New(),
FeatureUUID: "testfeatureuuid",
PhaseUUID: "testphaseuuid",
Name: "test ticket",
Expand Down Expand Up @@ -180,7 +180,7 @@ func TestGetTicket(t *testing.T) {
TestDB.CreateOrEditFeaturePhase(featurePhase)

ticket := Tickets{
UUID: uuid.New().String(),
UUID: uuid.New(),
FeatureUUID: workspaceFeatures.Uuid,
PhaseUUID: featurePhase.Uuid,
Name: "test get ticket",
Expand All @@ -201,7 +201,7 @@ func TestGetTicket(t *testing.T) {

// should return a ticket if it exists
t.Run("should return a ticket if it exists", func(t *testing.T) {
result, err := TestDB.GetTicket(ticket.UUID)
result, err := TestDB.GetTicket(ticket.UUID.String())
if err != nil {
t.Errorf("expected no error but got %v", err)
}
Expand Down
File renamed without changes.
108 changes: 108 additions & 0 deletions handlers/ticket.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
package handlers

import (
"encoding/json"
"fmt"
"io"
"net/http"

"github.com/go-chi/chi"
"github.com/google/uuid"
"github.com/stakwork/sphinx-tribes/db"
)

type ticketHandler struct {
db db.Database
}

func NewTicketHandler(database db.Database) *ticketHandler {
return &ticketHandler{
db: database,
}
}

func (th *ticketHandler) GetTicket(w http.ResponseWriter, r *http.Request) {
uuid := chi.URLParam(r, "uuid")
if uuid == "" {
http.Error(w, "UUID is required", http.StatusBadRequest)
return
}

ticket, err := th.db.GetTicket(uuid)
if err != nil {
if err.Error() == "ticket not found" {
http.Error(w, "Ticket not found", http.StatusNotFound)
return
}
http.Error(w, fmt.Sprintf("Failed to get ticket: %v", err), http.StatusInternalServerError)
return
}

respondWithJSON(w, http.StatusOK, ticket)
}

func (th *ticketHandler) UpdateTicket(w http.ResponseWriter, r *http.Request) {
uuidStr := chi.URLParam(r, "uuid")
if uuidStr == "" {
http.Error(w, "UUID is required", http.StatusBadRequest)
return
}

ticketUUID, err := uuid.Parse(uuidStr)
if err != nil {
http.Error(w, "Invalid UUID format", http.StatusBadRequest)
return
}

body, err := io.ReadAll(r.Body)
if err != nil {
http.Error(w, "Error reading request body", http.StatusBadRequest)
return
}

var ticket db.Tickets
if err := json.Unmarshal(body, &ticket); err != nil {
http.Error(w, "Error parsing request body", http.StatusBadRequest)
return
}

ticket.UUID = ticketUUID

updatedTicket, err := th.db.UpdateTicket(ticket)
if err != nil {
if err.Error() == "feature_uuid, phase_uuid, and name are required" {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
http.Error(w, fmt.Sprintf("Failed to update ticket: %v", err), http.StatusInternalServerError)
return
}

respondWithJSON(w, http.StatusOK, updatedTicket)
}

func (th *ticketHandler) DeleteTicket(w http.ResponseWriter, r *http.Request) {
uuid := chi.URLParam(r, "uuid")
if uuid == "" {
http.Error(w, "UUID is required", http.StatusBadRequest)
return
}

err := th.db.DeleteTicket(uuid)
if err != nil {
if err.Error() == "ticket not found" {
http.Error(w, "Ticket not found", http.StatusNotFound)
return
}
http.Error(w, fmt.Sprintf("Failed to delete ticket: %v", err), http.StatusInternalServerError)
return
}

respondWithJSON(w, http.StatusOK, map[string]string{"status": "success"})
}

func respondWithJSON(w http.ResponseWriter, code int, payload interface{}) {
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(code)
json.NewEncoder(w).Encode(payload)
}
Loading

0 comments on commit 3d51ab7

Please sign in to comment.