Skip to content

Commit

Permalink
Add User Stories to Features
Browse files Browse the repository at this point in the history
  • Loading branch information
MirzaHanan committed May 13, 2024
1 parent 74c3a0a commit 035b72a
Show file tree
Hide file tree
Showing 8 changed files with 238 additions and 15 deletions.
31 changes: 16 additions & 15 deletions cypress/e2e/04_user_stories.cy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ describe('Modify user story description', () => {
}).its('body').then(body => {
expect(body).to.have.property('uuid').and.equal(UserStories[i].uuid.trim());
expect(body).to.have.property('feature_uuid').and.equal(UserStories[i].feature_uuid.trim());
expect(body).to.have.property('description').and.equal(UserStories[i].description.trim() + "_addtext");
expect(body).to.have.property('description').and.equal(UserStories[i].description.trim() + " _addtext");
expect(body).to.have.property('priority').and.equal(UserStories[i].priority);
});
}
Expand All @@ -56,7 +56,7 @@ describe('Get user stories for feature', () => {
for(let i = 0; i <= 5; i++) {
expect(resp.body[i]).to.have.property('uuid').and.equal(UserStories[i].uuid.trim());
expect(resp.body[i]).to.have.property('feature_uuid').and.equal(UserStories[i].feature_uuid.trim());
expect(resp.body[i]).to.have.property('description').and.equal(UserStories[i].description.trim() + "_addtext");
expect(resp.body[i]).to.have.property('description').and.equal(UserStories[i].description.trim() + " _addtext");
expect(resp.body[i]).to.have.property('priority').and.equal(UserStories[i].priority);
}
})
Expand All @@ -71,19 +71,19 @@ describe('Get story by uuid', () => {
cy.request({
method: 'GET',
url: `${HostName}/features/${UserStories[0].feature_uuid}/story/${UserStories[i].uuid}`,
headers: { 'x-jwt': `${ value }` },
body: {}
headers: { 'x-jwt': `${value}` },
body: {}
}).then((resp) => {
expect(resp.status).to.eq(200)
expect(resp.body[i]).to.have.property('uuid').and.equal(UserStories[i].uuid.trim());
expect(resp.body[i]).to.have.property('feature_uuid').and.equal(UserStories[i].feature_uuid.trim());
expect(resp.body[i]).to.have.property('description').and.equal(UserStories[i].description.trim() + "_addtext");
expect(resp.body[i]).to.have.property('priority').and.equal(UserStories[i].priority);
})
expect(resp.status).to.eq(200);
expect(resp.body).to.have.property('uuid').and.equal(UserStories[i].uuid.trim());
expect(resp.body).to.have.property('feature_uuid').and.equal(UserStories[i].feature_uuid.trim());
expect(resp.body).to.have.property('description').and.equal(UserStories[i].description.trim() + " _addtext");
expect(resp.body).to.have.property('priority').and.equal(UserStories[i].priority);
});
}
})
})
})
});
});
});

describe('Delete story by uuid', () => {
it('passes', () => {
Expand All @@ -104,10 +104,11 @@ describe('Check delete by uuid', () => {
it('passes', () => {
cy.upsertlogin(User).then(value => {
cy.request({
method: 'GET',
method: 'DELETE',
url: `${HostName}/features/${UserStories[0].feature_uuid}/story/${UserStories[0].uuid}`,
headers: { 'x-jwt': `${ value }` },
body: {}
body: {},
failOnStatusCode: false
}).then((resp) => {
expect(resp.status).to.eq(404);
})
Expand Down
1 change: 1 addition & 0 deletions db/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ func InitDB() {
db.AutoMigrate(&BountyRoles{})
db.AutoMigrate(&UserInvoiceData{})
db.AutoMigrate(&WorkspaceFeatures{})
db.AutoMigrate(&FeatureStory{})

DB.MigrateTablesWithOrgUuid()
DB.MigrateOrganizationToWorkspace()
Expand Down
56 changes: 56 additions & 0 deletions db/features.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package db

import (
"errors"
"strings"
"time"
)
Expand Down Expand Up @@ -39,3 +40,58 @@ func (db database) CreateOrEditFeature(m WorkspaceFeatures) (WorkspaceFeatures,

return m, nil
}

func (db database) CreateOrEditFeatureStory(story FeatureStory) (FeatureStory, error) {
story.Description = strings.TrimSpace(story.Description)

now := time.Now()
story.Updated = &now

existingStory := FeatureStory{}
result := db.db.Model(&FeatureStory{}).Where("uuid = ?", story.Uuid).First(&existingStory)

if result.RowsAffected == 0 {

story.Created = &now
db.db.Create(&story)
} else {

db.db.Model(&FeatureStory{}).Where("uuid = ?", story.Uuid).Updates(story)
}

db.db.Model(&FeatureStory{}).Where("uuid = ?", story.Uuid).Find(&story)

return story, nil
}

func (db database) GetFeatureStoriesByFeatureUuid(featureUuid string) ([]FeatureStory, error) {
var stories []FeatureStory
result := db.db.Where("feature_uuid = ?", featureUuid).Find(&stories)
if result.Error != nil {
return nil, result.Error
}

for i := range stories {
stories[i].Description = strings.TrimSpace(stories[i].Description)
}
return stories, nil
}

func (db database) GetFeatureStoryByUuid(uuid string) (FeatureStory, error) {
var story FeatureStory
result := db.db.Where("uuid = ?", uuid).First(&story)
if result.Error != nil {
return FeatureStory{}, result.Error
}

story.Description = strings.TrimSpace(story.Description)
return story, nil
}

func (db database) DeleteFeatureStoryByUuid(featureUuid, storyUuid string) error {
result := db.db.Where("feature_uuid = ? AND uuid = ?", featureUuid, storyUuid).Delete(&FeatureStory{})
if result.RowsAffected == 0 {
return errors.New("no story found to delete")
}
return nil
}
4 changes: 4 additions & 0 deletions db/interface.go
Original file line number Diff line number Diff line change
Expand Up @@ -143,4 +143,8 @@ type Database interface {
CreateOrEditFeature(m WorkspaceFeatures) (WorkspaceFeatures, error)
GetFeaturesByWorkspaceUuid(uuid string) []WorkspaceFeatures
GetFeatureByUuid(uuid string) WorkspaceFeatures
CreateOrEditFeatureStory(story FeatureStory) (FeatureStory, error)
GetFeatureStoriesByFeatureUuid(featureUuid string) ([]FeatureStory, error)
GetFeatureStoryByUuid(uuid string) (FeatureStory, error)
DeleteFeatureStoryByUuid(featureUuid, storyUuid string) error
}
12 changes: 12 additions & 0 deletions db/structs.go
Original file line number Diff line number Diff line change
Expand Up @@ -656,6 +656,18 @@ type BudgetHistory struct {
PaymentType PaymentType `json:"payment_type"`
}

type FeatureStory struct {
ID uint `json:"id"`
Uuid string `json:"uuid"`
FeatureUuid string `json:"feature_uuid"`
Description string `json:"description"`
Priority int `json:"priority"`
Created *time.Time `json:"created"`
Updated *time.Time `json:"updated"`
CreatedBy string `json:"created_by"`
UpdatedBy string `json:"updated_by"`
}

type BudgetHistoryData struct {
BudgetHistory
SenderName string `json:"sender_name"`
Expand Down
70 changes: 70 additions & 0 deletions handlers/features.go
Original file line number Diff line number Diff line change
Expand Up @@ -93,3 +93,73 @@ func (oh *featureHandler) GetFeatureByUuid(w http.ResponseWriter, r *http.Reques
w.WriteHeader(http.StatusOK)
json.NewEncoder(w).Encode(workspaceFeature)
}

func (oh *featureHandler) CreateOrEditStory(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
pubKeyFromAuth, _ := ctx.Value(auth.ContextKey).(string)
if pubKeyFromAuth == "" {
fmt.Println("no pubkey from auth")
w.WriteHeader(http.StatusUnauthorized)
return
}

newStory := db.FeatureStory{}
decoder := json.NewDecoder(r.Body)
err := decoder.Decode(&newStory)
if err != nil {
w.WriteHeader(http.StatusBadRequest)
fmt.Fprintf(w, "Error decoding request body: %v", err)
return
}

newStory.CreatedBy = pubKeyFromAuth

story, err := oh.db.CreateOrEditFeatureStory(newStory)
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
fmt.Fprintf(w, "Error creating feature story: %v", err)
return
}

w.WriteHeader(http.StatusCreated)
json.NewEncoder(w).Encode(story)
}

func (oh *featureHandler) GetStoriesByFeatureUuid(w http.ResponseWriter, r *http.Request) {
featureUuid := chi.URLParam(r, "feature_uuid")
stories, err := oh.db.GetFeatureStoriesByFeatureUuid(featureUuid)
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
return
}

w.WriteHeader(http.StatusOK)
json.NewEncoder(w).Encode(stories)
}

func (oh *featureHandler) GetStoryByUuid(w http.ResponseWriter, r *http.Request) {
storyUuid := chi.URLParam(r, "story_uuid")
story, err := oh.db.GetFeatureStoryByUuid(storyUuid)
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
return
}

w.WriteHeader(http.StatusOK)
json.NewEncoder(w).Encode(story)
}

func (oh *featureHandler) DeleteStory(w http.ResponseWriter, r *http.Request) {
featureUuid := chi.URLParam(r, "feature_uuid")
storyUuid := chi.URLParam(r, "story_uuid")

err := oh.db.DeleteFeatureStoryByUuid(featureUuid, storyUuid)
if err != nil {
w.WriteHeader(http.StatusNotFound)
json.NewEncoder(w).Encode(map[string]string{"error": err.Error()})
return
}

w.WriteHeader(http.StatusOK)
json.NewEncoder(w).Encode(map[string]string{"message": "Story deleted successfully"})
}
75 changes: 75 additions & 0 deletions mocks/Database.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 4 additions & 0 deletions routes/features.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,10 @@ func FeatureRoutes() chi.Router {
r.Post("/", featureHandlers.CreateOrEditFeatures)
r.Get("/forworkspace/{uuid}", featureHandlers.GetFeaturesByWorkspaceUuid)
r.Get("/{uuid}", featureHandlers.GetFeatureByUuid)
r.Post("/story", featureHandlers.CreateOrEditStory)
r.Get("/{feature_uuid}/story", featureHandlers.GetStoriesByFeatureUuid)
r.Get("/{feature_uuid}/story/{story_uuid}", featureHandlers.GetStoryByUuid)
r.Delete("/{feature_uuid}/story/{story_uuid}", featureHandlers.DeleteStory)
})
return r
}

0 comments on commit 035b72a

Please sign in to comment.