Skip to content

Commit 285fb5a

Browse files
fix(GIST-38): added deletion to organizations (#12)
* wip(GIST-38): added tests + delete orgs * docs(GIST-38): added documentation
1 parent c7c3c93 commit 285fb5a

10 files changed

+209
-16
lines changed

docs/openapi.json

+1-1
Large diffs are not rendered by default.

organizations/controller.go

+15
Original file line numberDiff line numberDiff line change
@@ -52,3 +52,18 @@ func (c *OrganizationControllerImpl) GetByID() fiber.Handler {
5252
return c.JSON(org)
5353
}
5454
}
55+
56+
func (c *OrganizationControllerImpl) Delete() fiber.Handler {
57+
return func(c *fiber.Ctx) error {
58+
org_id := c.Params("id")
59+
user_id := c.Locals("pub").(string)
60+
err := OrganizationService.Delete(org_id, user_id)
61+
if err != nil {
62+
if err == ErrUserNotOwner {
63+
return c.Status(401).SendString(err.Error())
64+
}
65+
return c.Status(400).SendString(err.Error())
66+
}
67+
return c.SendStatus(204)
68+
}
69+
}

organizations/member_model.go

+43
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
package organizations
2+
3+
import (
4+
"database/sql"
5+
6+
"github.com/gistapp/api/storage"
7+
"github.com/gofiber/fiber/v2/log"
8+
)
9+
10+
type MemberSQL struct {
11+
MemberID sql.NullInt32
12+
OrgID sql.NullInt32
13+
UserID sql.NullInt32
14+
Role sql.NullString
15+
}
16+
17+
type Member struct {
18+
MemberID int `json:"member_id"`
19+
OrgID int `json:"org_id"`
20+
UserID int `json:"user_id"`
21+
Role Role `json:"role"`
22+
}
23+
24+
func (m *MemberSQL) Get() (*Member, error) {
25+
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)
26+
if err != nil {
27+
log.Error(err)
28+
return nil, err
29+
}
30+
31+
row.Next()
32+
33+
var member Member
34+
35+
err = row.Scan(&member.MemberID, &member.OrgID, &member.UserID, &member.Role)
36+
37+
if err != nil {
38+
log.Error(err)
39+
return nil, err
40+
}
41+
42+
return &member, nil
43+
}

organizations/model.go

+23
Original file line numberDiff line numberDiff line change
@@ -129,3 +129,26 @@ func (o *OrganizationSQL) GetByID(user_id string, org_id string) (*Organization,
129129
Gists: gists_ids,
130130
}, nil
131131
}
132+
133+
func (o *OrganizationSQL) Get() (*Organization, error) {
134+
query := "SELECT org_id, name FROM organization WHERE org_id = $1"
135+
136+
row, err := storage.Database.Query(query, o.ID.Int32)
137+
138+
if err != nil {
139+
return nil, errors.New("couldn't find organization")
140+
}
141+
142+
row.Next()
143+
144+
var organization Organization
145+
146+
err = row.Scan(&organization.ID, &organization.Name)
147+
148+
if err != nil {
149+
return nil, errors.New("couldn't find organization")
150+
}
151+
152+
return &organization, nil
153+
154+
}

organizations/router.go

+1
Original file line numberDiff line numberDiff line change
@@ -15,4 +15,5 @@ func (r *OrganizationRouter) SubscribeRoutes(app *fiber.Router) {
1515
organizations_router.Post("/", r.Controller.Save())
1616
organizations_router.Get("/", r.Controller.GetAsMember())
1717
organizations_router.Get("/:id", r.Controller.GetByID())
18+
organizations_router.Delete("/:id", r.Controller.Delete())
1819
}

organizations/service.go

+59
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ package organizations
33
import (
44
"database/sql"
55
"errors"
6+
"fmt"
7+
"strconv"
68

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

35+
func (g *OrganizationServiceImpl) IsOwner(org_id int, user_id int) bool {
36+
member_sql := MemberSQL{
37+
OrgID: sql.NullInt32{
38+
Int32: int32(org_id),
39+
Valid: true,
40+
},
41+
UserID: sql.NullInt32{
42+
Int32: int32(user_id),
43+
Valid: true,
44+
},
45+
}
46+
47+
member, err := member_sql.Get()
48+
if err != nil {
49+
return false
50+
}
51+
52+
return member.Role == Owner
53+
}
54+
3355
// returns a list of organizations that the user is a member of
3456
func (g *OrganizationServiceImpl) GetAsMember(user_id string) ([]Organization, error) {
3557
m := OrganizationSQL{
@@ -67,4 +89,41 @@ func (g *OrganizationServiceImpl) GetByID(org_id string, user_id string) (*Organ
6789
return organization, nil
6890
}
6991

92+
func (g *OrganizationServiceImpl) Delete(org_id string, user_id string) error {
93+
i_org_id, err := strconv.Atoi(org_id)
94+
95+
if err != nil {
96+
return errors.New("organization ID must be an integer")
97+
}
98+
99+
i_user_id, err := strconv.Atoi(user_id)
100+
101+
if err != nil {
102+
return errors.New("user ID must be an integer")
103+
}
104+
105+
if !g.IsOwner(i_org_id, i_user_id) {
106+
return errors.New("user is not the owner of the organization")
107+
}
108+
109+
m := OrganizationSQL{
110+
ID: sql.NullInt32{
111+
Valid: true,
112+
Int32: int32(i_org_id),
113+
},
114+
Name: sql.NullString{
115+
String: "",
116+
Valid: false,
117+
},
118+
}
119+
fmt.Printf("org_id: %d\n", i_org_id)
120+
err = m.Delete()
121+
if err != nil {
122+
fmt.Println(err)
123+
return errors.New("couldn't delete organization")
124+
}
125+
return nil
126+
}
127+
70128
var OrganizationService *OrganizationServiceImpl = &OrganizationServiceImpl{}
129+
var ErrUserNotOwner = errors.New("user is not the owner of the organization")

tests/gists_test.go

+5-5
Original file line numberDiff line numberDiff line change
@@ -53,20 +53,20 @@ func InitServerGists() *fiber.App {
5353
func TestCreateGists(t *testing.T) {
5454
t.Run("Create a new personal gist", func(t *testing.T) {
5555
app := InitServerGists()
56-
authToken := GetAuthToken(t, app)
56+
auth_token := GetAuthToken(t, app)
5757

58-
body, req := utils.MakeRequest("POST", t, app, "/gists", map[string]string{
58+
_, req := utils.MakeRequest("POST", t, app, "/gists", map[string]string{
5959
"name": "Test Gist",
6060
"content": "Test content",
6161
}, map[string]string{
62-
"Authorization": fmt.Sprintf("Bearer %s", authToken),
62+
"Authorization": fmt.Sprintf("Bearer %s", auth_token),
6363
})
6464

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

69-
fmt.Println(body)
69+
DeleteAuthUser(t, auth_token)
7070

7171
})
7272

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

104104
fmt.Println(body)
105-
105+
DeleteOrganization(t, org.ID)
106106
DeleteAuthUser(t, auth_token)
107107

108108
})

tests/organization_test.go

+53-7
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,6 @@ func TestCreateOrganization(t *testing.T) {
6666
org_payload := map[string]string{
6767
"name": "Test Organization",
6868
}
69-
fmt.Println(org_payload)
7069
//
7170
body, _ := utils.MakeRequest("POST", t, app, "/orgs", org_payload, map[string]string{
7271
"Authorization": "Bearer " + auth_token,
@@ -77,21 +76,68 @@ func TestCreateOrganization(t *testing.T) {
7776
}
7877

7978
// cleanup
80-
id, err := strconv.ParseInt(body["id"], 10, 32)
79+
DeleteOrganization(t, body["id"])
80+
DeleteAuthUser(t, auth_token)
81+
})
82+
83+
}
84+
85+
func DeleteOrganization(t *testing.T, org_id string) {
86+
id, err := strconv.ParseInt(org_id, 10, 32)
87+
88+
if err != nil {
89+
t.Errorf("Failed to parse organization ID: %v", err)
90+
}
91+
92+
org := organizations.OrganizationSQL{
93+
ID: sql.NullInt32{
94+
Int32: int32(id),
95+
Valid: true,
96+
},
97+
}
98+
if err = org.Delete(); err != nil {
99+
t.Errorf("Failed to delete organization: %v", err)
100+
return
101+
}
102+
103+
}
104+
105+
func TestDeleteOrganization(t *testing.T) {
106+
t.Run("Delete organization", func(t *testing.T) {
107+
app := InitServerOrgs()
108+
if app == nil {
109+
t.Fatal("Failed to initialize the application")
110+
}
111+
112+
auth_token := GetAuthToken(t, app)
81113

82-
if err != nil {
83-
t.Errorf("Failed to parse organization ID: %v", err)
114+
org_payload := map[string]string{
115+
"name": "Test Organization",
84116
}
85117

86-
org := organizations.OrganizationSQL{
118+
body, _ := utils.MakeRequest("POST", t, app, "/orgs", org_payload, map[string]string{
119+
"Authorization": "Bearer " + auth_token,
120+
}) //before previous test tests the creation, we should be pretty sure that the creation works
121+
122+
id, _ := strconv.ParseInt(body["id"], 10, 32)
123+
124+
body, _ = utils.MakeRequest("DELETE", t, app, fmt.Sprintf("/orgs/%d", id), nil, map[string]string{
125+
"Authorization": "Bearer " + auth_token,
126+
})
127+
128+
org_dto := organizations.OrganizationSQL{
87129
ID: sql.NullInt32{
88130
Int32: int32(id),
89131
Valid: true,
90132
},
91133
}
92-
if err = org.Delete(); err != nil {
93-
t.Errorf("Failed to delete organization: %v", err)
134+
135+
_, err := org_dto.Get()
136+
137+
if err == nil {
138+
t.Fatal("Organization was not deleted")
94139
}
140+
95141
DeleteAuthUser(t, auth_token)
96142
})
97143
}

tests/users_test.go

+2
Original file line numberDiff line numberDiff line change
@@ -77,5 +77,7 @@ func TestRetreiveUser(t *testing.T) {
7777
}
7878

7979
log.Info(body)
80+
81+
DeleteAuthUser(t, auth_token)
8082
})
8183
}

utils/http_testing.go

+7-3
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ func MakeRequest(method string, t *testing.T, app *fiber.App, url string, payloa
2121
if method == "GET" {
2222
req, err = http.NewRequest("GET", url, nil)
2323
} else {
24-
req, err = http.NewRequest("POST", url, strings.NewReader(string(jsonPayload)))
24+
req, err = http.NewRequest(method, url, strings.NewReader(string(jsonPayload)))
2525
req.Header.Add("Content-Type", "application/json")
2626

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

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

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

52+
if resp.StatusCode != 200 && resp.StatusCode != 201 {
53+
t.Errorf("Expected status code 200 or 201, got %d with body %v", resp.StatusCode, respBody)
54+
}
55+
5256
return respBody, resp
5357
}

0 commit comments

Comments
 (0)