Skip to content

Commit

Permalink
make ticket fields optional except UUID
Browse files Browse the repository at this point in the history
  • Loading branch information
aliraza556 committed Nov 29, 2024
1 parent 5b3d418 commit a1b442a
Show file tree
Hide file tree
Showing 6 changed files with 102 additions and 91 deletions.
20 changes: 10 additions & 10 deletions db/structs.go
Original file line number Diff line number Diff line change
Expand Up @@ -959,18 +959,18 @@ const (

type Tickets struct {
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"`
FeatureUUID string `gorm:"type:uuid;index:composite_index" json:"feature_uuid"`
Features WorkspaceFeatures `gorm:"foreignKey:FeatureUUID;references:Uuid"`
PhaseUUID string `gorm:"type:uuid;not null;index:phase_index" json:"phase_uuid" validate:"required"`
PhaseUUID string `gorm:"type:uuid;index:phase_index" json:"phase_uuid"`
FeaturePhase FeaturePhase `gorm:"foreignKey:PhaseUUID;references:Uuid"`
Name string `gorm:"type:varchar(255);not null"`
Sequence int `gorm:"type:integer;not null;index:composite_index"`
Dependency []int `gorm:"type:integer[]"`
Description string `gorm:"type:text"`
Status TicketStatus `gorm:"type:varchar(50);not null;default:'DRAFT'"`
Version int `gorm:"type:integer" json:"version"`
CreatedAt time.Time `gorm:"type:timestamp;not null;default:current_timestamp" json:"created_at"`
UpdatedAt time.Time `gorm:"type:timestamp;not null;default:current_timestamp" json:"updated_at"`
Name string `gorm:"type:varchar(255)" json:"name"`
Sequence int `gorm:"type:integer;index:composite_index;default:0" json:"sequence"`
Dependency []int `gorm:"type:integer[]" json:"dependency"`
Description string `gorm:"type:text" json:"description"`
Status TicketStatus `gorm:"type:varchar(50);default:'DRAFT'" json:"status"`
Version int `gorm:"type:integer;default:0" json:"version"`
CreatedAt time.Time `gorm:"type:timestamp;default:current_timestamp" json:"created_at"`
UpdatedAt time.Time `gorm:"type:timestamp;default:current_timestamp" json:"updated_at"`
}

func (Person) TableName() string {
Expand Down
48 changes: 31 additions & 17 deletions db/tickets.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,29 +11,47 @@ import (

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

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

// check if ticket exists and update it
if db.db.Model(&Tickets{}).Where("uuid = ?", ticket.UUID).First(&ticket).RowsAffected != 0 {
now := time.Now()
ticket.UpdatedAt = now
if ticket.Status != "" && !IsValidTicketStatus(ticket.Status) {
return Tickets{}, errors.New("invalid ticket status")
}

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

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

// update ticket
if db.db.Model(&ticket).Where("uuid = ?", ticket.UUID).Updates(&ticket).RowsAffected == 0 {
return Tickets{}, errors.New("failed to update ticket")
if errors.Is(result.Error, gorm.ErrRecordNotFound) {
ticket.CreatedAt = now

if ticket.Status == "" {
ticket.Status = DraftTicket
}

if err := db.db.Create(&ticket).Error; err != nil {
return Tickets{}, fmt.Errorf("failed to create ticket: %w", err)
}
return *ticket, nil
}

// create ticket and return error if it fails
if db.db.Create(&ticket).Error != nil {
return Tickets{}, db.db.Create(&ticket).Error
if result.Error != nil {
return Tickets{}, fmt.Errorf("database error: %w", result.Error)
}

return *ticket, nil
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) GetTicket(uuid string) (Tickets, error) {
Expand Down Expand Up @@ -66,10 +84,6 @@ func (db database) UpdateTicket(ticket Tickets) (Tickets, error) {
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")
}

if ticket.Status != "" && !IsValidTicketStatus(ticket.Status) {
return Tickets{}, errors.New("invalid ticket status")
}
Expand Down
73 changes: 34 additions & 39 deletions handlers/ticket.go
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ func (th *ticketHandler) UpdateTicket(w http.ResponseWriter, r *http.Request) {
return
}

updatedTicket, err := th.db.UpdateTicket(ticket)
updatedTicket, err := th.db.CreateOrEditTicket(&ticket)
if err != nil {
if err.Error() == "feature_uuid, phase_uuid, and name are required" {
w.WriteHeader(http.StatusBadRequest)
Expand Down Expand Up @@ -199,15 +199,6 @@ func (th *ticketHandler) PostTicketDataToStakwork(w http.ResponseWriter, r *http
validationErrors = append(validationErrors, "Invalid UUID format")
}
}
if ticket.FeatureUUID == "" {
validationErrors = append(validationErrors, "FeatureUUID is required")
}
if ticket.PhaseUUID == "" {
validationErrors = append(validationErrors, "PhaseUUID is required")
}
if ticket.Name == "" {
validationErrors = append(validationErrors, "Name is required")
}

if len(validationErrors) > 0 {
w.WriteHeader(http.StatusBadRequest)
Expand All @@ -219,37 +210,41 @@ func (th *ticketHandler) PostTicketDataToStakwork(w http.ResponseWriter, r *http
return
}

feature := th.db.GetFeatureByUuid(ticket.FeatureUUID)
if feature.Uuid == "" {
w.WriteHeader(http.StatusInternalServerError)
json.NewEncoder(w).Encode(TicketResponse{
Success: false,
Message: "Error retrieving feature details",
Errors: []string{"Feature not found with the provided UUID"},
})
return
}
var productBrief, featureBrief string
if ticket.FeatureUUID != "" {
feature := th.db.GetFeatureByUuid(ticket.FeatureUUID)
if feature.Uuid == "" {
w.WriteHeader(http.StatusInternalServerError)
json.NewEncoder(w).Encode(TicketResponse{
Success: false,
Message: "Error retrieving feature details",
Errors: []string{"Feature not found with the provided UUID"},
})
return
}

productBrief, err := th.db.GetProductBrief(feature.WorkspaceUuid)
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
json.NewEncoder(w).Encode(TicketResponse{
Success: false,
Message: "Error retrieving product brief",
Errors: []string{err.Error()},
})
return
}
var err error
productBrief, err = th.db.GetProductBrief(feature.WorkspaceUuid)
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
json.NewEncoder(w).Encode(TicketResponse{
Success: false,
Message: "Error retrieving product brief",
Errors: []string{err.Error()},
})
return
}

featureBrief, err := th.db.GetFeatureBrief(ticket.FeatureUUID)
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
json.NewEncoder(w).Encode(TicketResponse{
Success: false,
Message: "Error retrieving feature brief",
Errors: []string{err.Error()},
})
return
featureBrief, err = th.db.GetFeatureBrief(ticket.FeatureUUID)
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
json.NewEncoder(w).Encode(TicketResponse{
Success: false,
Message: "Error retrieving feature brief",
Errors: []string{err.Error()},
})
return
}
}

host := os.Getenv("HOST")
Expand Down
38 changes: 26 additions & 12 deletions handlers/ticket_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -184,7 +184,7 @@ func TestUpdateTicket(t *testing.T) {
rr := httptest.NewRecorder()
handler := http.HandlerFunc(tHandler.UpdateTicket)

req, err := http.NewRequest(http.MethodPut, "/tickets/", nil)
req, err := http.NewRequest(http.MethodPost, "/tickets/", nil)
if err != nil {
t.Fatal(err)
}
Expand All @@ -197,7 +197,7 @@ func TestUpdateTicket(t *testing.T) {
rr := httptest.NewRecorder()
handler := http.HandlerFunc(tHandler.UpdateTicket)

req, err := http.NewRequest(http.MethodPut, "/tickets/", nil)
req, err := http.NewRequest(http.MethodPost, "/tickets/", nil)
if err != nil {
t.Fatal(err)
}
Expand All @@ -216,7 +216,7 @@ func TestUpdateTicket(t *testing.T) {
rctx := chi.NewRouteContext()
rctx.URLParams.Add("uuid", "invalid-uuid")

req, err := http.NewRequest(http.MethodPut, "/tickets/invalid-uuid", bytes.NewReader([]byte("{}")))
req, err := http.NewRequest(http.MethodPost, "/tickets/invalid-uuid", bytes.NewReader([]byte("{}")))
if err != nil {
t.Fatal(err)
}
Expand All @@ -235,7 +235,7 @@ func TestUpdateTicket(t *testing.T) {
invalidJson := []byte(`{"key": "value"`)
rctx := chi.NewRouteContext()
rctx.URLParams.Add("uuid", createdTicket.UUID.String())
req, err := http.NewRequest(http.MethodPut, "/tickets/"+createdTicket.UUID.String(), bytes.NewReader(invalidJson))
req, err := http.NewRequest(http.MethodPost, "/tickets/"+createdTicket.UUID.String(), bytes.NewReader(invalidJson))
if err != nil {
t.Fatal(err)
}
Expand All @@ -247,19 +247,21 @@ func TestUpdateTicket(t *testing.T) {
assert.Equal(t, http.StatusBadRequest, rr.Code)
})

t.Run("should return 400 if required fields are missing", func(t *testing.T) {
t.Run("should update ticket with only UUID and optional fields", func(t *testing.T) {
rr := httptest.NewRecorder()
handler := http.HandlerFunc(tHandler.UpdateTicket)

invalidTicket := db.Tickets{
UUID: uuid.New(),
// Missing required fields
// Create a ticket with only UUID and some optional fields
updateTicket := db.Tickets{
UUID: createdTicket.UUID,
Description: "Updated description", // Optional field
Status: db.ReadyTicket, // Optional field
}

requestBody, _ := json.Marshal(invalidTicket)
requestBody, _ := json.Marshal(updateTicket)
rctx := chi.NewRouteContext()
rctx.URLParams.Add("uuid", invalidTicket.UUID.String())
req, err := http.NewRequest(http.MethodPut, "/tickets/"+invalidTicket.UUID.String(), bytes.NewReader(requestBody))
rctx.URLParams.Add("uuid", updateTicket.UUID.String())
req, err := http.NewRequest(http.MethodPost, "/tickets/"+updateTicket.UUID.String(), bytes.NewReader(requestBody))
if err != nil {
t.Fatal(err)
}
Expand All @@ -268,7 +270,19 @@ func TestUpdateTicket(t *testing.T) {
req = req.WithContext(context.WithValue(ctx, chi.RouteCtxKey, rctx))

handler.ServeHTTP(rr, req)
assert.Equal(t, http.StatusBadRequest, rr.Code)
assert.Equal(t, http.StatusOK, rr.Code)

var returnedTicket db.Tickets
err = json.Unmarshal(rr.Body.Bytes(), &returnedTicket)
assert.NoError(t, err)

// Verify that only the provided fields were updated
assert.Equal(t, updateTicket.Description, returnedTicket.Description)
assert.Equal(t, updateTicket.Status, returnedTicket.Status)
// Original fields should remain unchanged
assert.Equal(t, createdTicket.FeatureUUID, returnedTicket.FeatureUUID)
assert.Equal(t, createdTicket.PhaseUUID, returnedTicket.PhaseUUID)
assert.Equal(t, createdTicket.Name, returnedTicket.Name)
})

t.Run("should update ticket successfully", func(t *testing.T) {
Expand Down
6 changes: 1 addition & 5 deletions routes/ticket_routes.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,6 @@ func TicketRoutes() chi.Router {
r := chi.NewRouter()
ticketHandler := handlers.NewTicketHandler(http.DefaultClient, db.DB)

r.Options("/*", func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
})

r.Group(func(r chi.Router) {
r.Get("/{uuid}", ticketHandler.GetTicket)
})
Expand All @@ -27,7 +23,7 @@ func TicketRoutes() chi.Router {
r.Post("/review/send", ticketHandler.PostTicketDataToStakwork)
r.Post("/review", ticketHandler.ProcessTicketReview)

r.Put("/{uuid}", ticketHandler.UpdateTicket)
r.Post("/{uuid}", ticketHandler.UpdateTicket)
r.Delete("/{uuid}", ticketHandler.DeleteTicket)
})

Expand Down
8 changes: 0 additions & 8 deletions utils/ticket_processor.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,19 +5,11 @@ import (
)

type TicketReviewRequest struct {
FeatureUUID string `json:"featureUUID" validate:"required"`
PhaseUUID string `json:"phaseUUID" validate:"required"`
TicketUUID string `json:"ticketUUID" validate:"required"`
TicketDescription string `json:"ticketDescription" validate:"required"`
}

func ValidateTicketReviewRequest(req *TicketReviewRequest) error {
if req.FeatureUUID == "" {
return errors.New("featureUUID is required")
}
if req.PhaseUUID == "" {
return errors.New("phaseUUID is required")
}
if req.TicketUUID == "" {
return errors.New("ticketUUID is required")
}
Expand Down

0 comments on commit a1b442a

Please sign in to comment.