Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(GIST-38): added deletion to organizations #12

Merged
merged 2 commits into from
Sep 1, 2024
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion docs/openapi.json

Large diffs are not rendered by default.

15 changes: 15 additions & 0 deletions organizations/controller.go
Original file line number Diff line number Diff line change
@@ -52,3 +52,18 @@ func (c *OrganizationControllerImpl) GetByID() fiber.Handler {
return c.JSON(org)
}
}

func (c *OrganizationControllerImpl) Delete() fiber.Handler {
return func(c *fiber.Ctx) error {
org_id := c.Params("id")
user_id := c.Locals("pub").(string)
err := OrganizationService.Delete(org_id, user_id)
if err != nil {
if err == ErrUserNotOwner {
return c.Status(401).SendString(err.Error())
}
return c.Status(400).SendString(err.Error())
}
return c.SendStatus(204)
}
}
43 changes: 43 additions & 0 deletions organizations/member_model.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package organizations

import (
"database/sql"

"github.com/gistapp/api/storage"
"github.com/gofiber/fiber/v2/log"
)

type MemberSQL struct {
MemberID sql.NullInt32
OrgID sql.NullInt32
UserID sql.NullInt32
Role sql.NullString
}

type Member struct {
MemberID int `json:"member_id"`
OrgID int `json:"org_id"`
UserID int `json:"user_id"`
Role Role `json:"role"`
}

func (m *MemberSQL) Get() (*Member, error) {
row, err := storage.Database.Query("SELECT member_id, org_id, user_id, role FROM member WHERE org_id = $1 AND user_id = $2", m.OrgID.Int32, m.UserID.Int32)
if err != nil {
log.Error(err)
return nil, err
}

row.Next()

var member Member

err = row.Scan(&member.MemberID, &member.OrgID, &member.UserID, &member.Role)

if err != nil {
log.Error(err)
return nil, err
}

return &member, nil
}
23 changes: 23 additions & 0 deletions organizations/model.go
Original file line number Diff line number Diff line change
@@ -129,3 +129,26 @@ func (o *OrganizationSQL) GetByID(user_id string, org_id string) (*Organization,
Gists: gists_ids,
}, nil
}

func (o *OrganizationSQL) Get() (*Organization, error) {
query := "SELECT org_id, name FROM organization WHERE org_id = $1"

row, err := storage.Database.Query(query, o.ID.Int32)

if err != nil {
return nil, errors.New("couldn't find organization")
}

row.Next()

var organization Organization

err = row.Scan(&organization.ID, &organization.Name)

if err != nil {
return nil, errors.New("couldn't find organization")
}

return &organization, nil

}
1 change: 1 addition & 0 deletions organizations/router.go
Original file line number Diff line number Diff line change
@@ -15,4 +15,5 @@ func (r *OrganizationRouter) SubscribeRoutes(app *fiber.Router) {
organizations_router.Post("/", r.Controller.Save())
organizations_router.Get("/", r.Controller.GetAsMember())
organizations_router.Get("/:id", r.Controller.GetByID())
organizations_router.Delete("/:id", r.Controller.Delete())
}
59 changes: 59 additions & 0 deletions organizations/service.go
Original file line number Diff line number Diff line change
@@ -3,6 +3,8 @@ package organizations
import (
"database/sql"
"errors"
"fmt"
"strconv"

"github.com/gofiber/fiber/v2/log"
)
@@ -30,6 +32,26 @@ func (g *OrganizationServiceImpl) Save(name string, owner_id string) (*Organizat
return organization, nil
}

func (g *OrganizationServiceImpl) IsOwner(org_id int, user_id int) bool {
member_sql := MemberSQL{
OrgID: sql.NullInt32{
Int32: int32(org_id),
Valid: true,
},
UserID: sql.NullInt32{
Int32: int32(user_id),
Valid: true,
},
}

member, err := member_sql.Get()
if err != nil {
return false
}

return member.Role == Owner
}

// returns a list of organizations that the user is a member of
func (g *OrganizationServiceImpl) GetAsMember(user_id string) ([]Organization, error) {
m := OrganizationSQL{
@@ -67,4 +89,41 @@ func (g *OrganizationServiceImpl) GetByID(org_id string, user_id string) (*Organ
return organization, nil
}

func (g *OrganizationServiceImpl) Delete(org_id string, user_id string) error {
i_org_id, err := strconv.Atoi(org_id)

if err != nil {
return errors.New("organization ID must be an integer")
}

i_user_id, err := strconv.Atoi(user_id)

if err != nil {
return errors.New("user ID must be an integer")
}

if !g.IsOwner(i_org_id, i_user_id) {
return errors.New("user is not the owner of the organization")
}

m := OrganizationSQL{
ID: sql.NullInt32{
Valid: true,
Int32: int32(i_org_id),
},
Name: sql.NullString{
String: "",
Valid: false,
},
}
fmt.Printf("org_id: %d\n", i_org_id)
err = m.Delete()
if err != nil {
fmt.Println(err)
return errors.New("couldn't delete organization")
}
return nil
}

var OrganizationService *OrganizationServiceImpl = &OrganizationServiceImpl{}
var ErrUserNotOwner = errors.New("user is not the owner of the organization")
10 changes: 5 additions & 5 deletions tests/gists_test.go
Original file line number Diff line number Diff line change
@@ -53,20 +53,20 @@ func InitServerGists() *fiber.App {
func TestCreateGists(t *testing.T) {
t.Run("Create a new personal gist", func(t *testing.T) {
app := InitServerGists()
authToken := GetAuthToken(t, app)
auth_token := GetAuthToken(t, app)

body, req := utils.MakeRequest("POST", t, app, "/gists", map[string]string{
_, req := utils.MakeRequest("POST", t, app, "/gists", map[string]string{
"name": "Test Gist",
"content": "Test content",
}, map[string]string{
"Authorization": fmt.Sprintf("Bearer %s", authToken),
"Authorization": fmt.Sprintf("Bearer %s", auth_token),
})

if req.StatusCode != 201 {
t.Fatalf("Expected status code 201, got %d", req.StatusCode)
}

fmt.Println(body)
DeleteAuthUser(t, auth_token)

})

@@ -102,7 +102,7 @@ func TestCreateGists(t *testing.T) {
}

fmt.Println(body)

DeleteOrganization(t, org.ID)
DeleteAuthUser(t, auth_token)

})
60 changes: 53 additions & 7 deletions tests/organization_test.go
Original file line number Diff line number Diff line change
@@ -66,7 +66,6 @@ func TestCreateOrganization(t *testing.T) {
org_payload := map[string]string{
"name": "Test Organization",
}
fmt.Println(org_payload)
//
body, _ := utils.MakeRequest("POST", t, app, "/orgs", org_payload, map[string]string{
"Authorization": "Bearer " + auth_token,
@@ -77,21 +76,68 @@ func TestCreateOrganization(t *testing.T) {
}

// cleanup
id, err := strconv.ParseInt(body["id"], 10, 32)
DeleteOrganization(t, body["id"])
DeleteAuthUser(t, auth_token)
})

}

func DeleteOrganization(t *testing.T, org_id string) {
id, err := strconv.ParseInt(org_id, 10, 32)

if err != nil {
t.Errorf("Failed to parse organization ID: %v", err)
}

org := organizations.OrganizationSQL{
ID: sql.NullInt32{
Int32: int32(id),
Valid: true,
},
}
if err = org.Delete(); err != nil {
t.Errorf("Failed to delete organization: %v", err)
return
}

}

func TestDeleteOrganization(t *testing.T) {
t.Run("Delete organization", func(t *testing.T) {
app := InitServerOrgs()
if app == nil {
t.Fatal("Failed to initialize the application")
}

auth_token := GetAuthToken(t, app)

if err != nil {
t.Errorf("Failed to parse organization ID: %v", err)
org_payload := map[string]string{
"name": "Test Organization",
}

org := organizations.OrganizationSQL{
body, _ := utils.MakeRequest("POST", t, app, "/orgs", org_payload, map[string]string{
"Authorization": "Bearer " + auth_token,
}) //before previous test tests the creation, we should be pretty sure that the creation works

id, _ := strconv.ParseInt(body["id"], 10, 32)

body, _ = utils.MakeRequest("DELETE", t, app, fmt.Sprintf("/orgs/%d", id), nil, map[string]string{
"Authorization": "Bearer " + auth_token,
})

org_dto := organizations.OrganizationSQL{
ID: sql.NullInt32{
Int32: int32(id),
Valid: true,
},
}
if err = org.Delete(); err != nil {
t.Errorf("Failed to delete organization: %v", err)

_, err := org_dto.Get()

if err == nil {
t.Fatal("Organization was not deleted")
}

DeleteAuthUser(t, auth_token)
})
}
2 changes: 2 additions & 0 deletions tests/users_test.go
Original file line number Diff line number Diff line change
@@ -77,5 +77,7 @@ func TestRetreiveUser(t *testing.T) {
}

log.Info(body)

DeleteAuthUser(t, auth_token)
})
}
10 changes: 7 additions & 3 deletions utils/http_testing.go
Original file line number Diff line number Diff line change
@@ -21,7 +21,7 @@ func MakeRequest(method string, t *testing.T, app *fiber.App, url string, payloa
if method == "GET" {
req, err = http.NewRequest("GET", url, nil)
} else {
req, err = http.NewRequest("POST", url, strings.NewReader(string(jsonPayload)))
req, err = http.NewRequest(method, url, strings.NewReader(string(jsonPayload)))
req.Header.Add("Content-Type", "application/json")

}
@@ -39,8 +39,8 @@ func MakeRequest(method string, t *testing.T, app *fiber.App, url string, payloa
t.Fatalf("Failed to execute request: %v", err)
}

if resp.StatusCode != 200 && resp.StatusCode != 201 {
t.Errorf("Expected status code 200 or 201, got %d", resp.StatusCode)
if resp.StatusCode == 204 {
return nil, resp
}

// Decode the response body into a map
@@ -49,5 +49,9 @@ func MakeRequest(method string, t *testing.T, app *fiber.App, url string, payloa
t.Fatalf("Failed to decode response: %v", err)
}

if resp.StatusCode != 200 && resp.StatusCode != 201 {
t.Errorf("Expected status code 200 or 201, got %d with body %v", resp.StatusCode, respBody)
}

return respBody, resp
}