Skip to content

Commit fcb0e66

Browse files
authored
Add roles to users' batch creation (#102)
* Support role in batch creation params * Create handler for user batch creation with roles Adapter from the standard user batch creation handler. * Update router endpoint to new handler * Add FIXME * Update tests
1 parent be33923 commit fcb0e66

File tree

4 files changed

+130
-1
lines changed

4 files changed

+130
-1
lines changed

controller/usergroups/create.go

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
package usergroups
2+
3+
import (
4+
"encoding/json"
5+
"fmt"
6+
"net/http"
7+
8+
"github.com/sirupsen/logrus"
9+
"github.com/source-academy/stories-backend/controller"
10+
"github.com/source-academy/stories-backend/internal/auth"
11+
"github.com/source-academy/stories-backend/internal/database"
12+
apierrors "github.com/source-academy/stories-backend/internal/errors"
13+
userpermissiongroups "github.com/source-academy/stories-backend/internal/permissiongroups/users"
14+
"github.com/source-academy/stories-backend/internal/usergroups"
15+
"github.com/source-academy/stories-backend/model"
16+
userparams "github.com/source-academy/stories-backend/params/users"
17+
userviews "github.com/source-academy/stories-backend/view/users"
18+
)
19+
20+
func HandleBatchCreate(w http.ResponseWriter, r *http.Request) error {
21+
err := auth.CheckPermissions(r, userpermissiongroups.Create())
22+
if err != nil {
23+
logrus.Error(err)
24+
return apierrors.ClientForbiddenError{
25+
Message: fmt.Sprintf("Error batch creating users: %v", err),
26+
}
27+
}
28+
29+
var usersparams userparams.BatchCreate
30+
if err := json.NewDecoder(r.Body).Decode(&usersparams); err != nil {
31+
e, ok := err.(*json.UnmarshalTypeError)
32+
if !ok {
33+
logrus.Error(err)
34+
return apierrors.ClientBadRequestError{
35+
Message: fmt.Sprintf("Bad JSON parsing: %v", err),
36+
}
37+
}
38+
39+
// TODO: Investigate if we should use errors.Wrap instead
40+
return apierrors.ClientUnprocessableEntityError{
41+
Message: fmt.Sprintf("Invalid JSON format: %s should be a %s.", e.Field, e.Type),
42+
}
43+
}
44+
45+
for _, userparams := range usersparams.Users {
46+
err := userparams.Validate()
47+
if err != nil {
48+
logrus.Error(err)
49+
return apierrors.ClientUnprocessableEntityError{
50+
Message: fmt.Sprintf("JSON validation failed: %v", err),
51+
}
52+
}
53+
}
54+
55+
// Get group id from context
56+
groupID, err := usergroups.GetGroupIDFrom(r)
57+
if err != nil {
58+
logrus.Error(err)
59+
return err
60+
}
61+
62+
// FIXME: MAKE THIS ATOMIC
63+
64+
userModels := make([]*model.User, len(usersparams.Users))
65+
for i, userparams := range usersparams.Users {
66+
userModels[i] = userparams.ToModel()
67+
}
68+
69+
// Get DB instance
70+
db, err := database.GetDBFrom(r)
71+
if err != nil {
72+
logrus.Error(err)
73+
return err
74+
}
75+
76+
numCreated, err := model.CreateUsers(db, &userModels)
77+
if err != nil {
78+
logrus.Error(err)
79+
return err
80+
}
81+
82+
// FIXME: REFACTOR
83+
84+
userGroupModels := make([]*model.UserGroup, len(usersparams.Users))
85+
for i, userModel := range userModels {
86+
userGroupModels[i] = &model.UserGroup{
87+
GroupID: *groupID,
88+
UserID: userModel.ID,
89+
User: *userModel,
90+
Role: usersparams.Users[i].Role,
91+
}
92+
}
93+
94+
// FIXME: Use batch create
95+
for _, toCreate := range userGroupModels {
96+
err := model.CreateUserGroup(db, toCreate)
97+
if err != nil {
98+
logrus.Error(err)
99+
return err
100+
}
101+
}
102+
103+
controller.EncodeJSONResponse(w, userviews.BatchCreateFrom(userModels, numCreated))
104+
w.WriteHeader(http.StatusCreated)
105+
return nil
106+
}

internal/router/router.go

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,9 @@ import (
99
"github.com/source-academy/stories-backend/controller"
1010
"github.com/source-academy/stories-backend/controller/stories"
1111
"github.com/source-academy/stories-backend/controller/users"
12+
13+
// FIXME: Name clash
14+
usergroupscontroller "github.com/source-academy/stories-backend/controller/usergroups"
1215
"github.com/source-academy/stories-backend/internal/auth"
1316
"github.com/source-academy/stories-backend/internal/config"
1417
usergroups "github.com/source-academy/stories-backend/internal/usergroups"
@@ -63,7 +66,7 @@ func Setup(config *config.Config, injectMiddleWares []func(http.Handler) http.Ha
6366
r.Get("/{userID}", handleAPIError(users.HandleRead))
6467
r.Delete("/{userID}", handleAPIError(users.HandleDelete))
6568
r.Post("/", handleAPIError(users.HandleCreate))
66-
r.Post("/batch", handleAPIError(users.HandleBatchCreate))
69+
r.Post("/batch", handleAPIError(usergroupscontroller.HandleBatchCreate))
6770
})
6871
})
6972

params/users/create.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"errors"
55
"fmt"
66

7+
groupenums "github.com/source-academy/stories-backend/internal/enums/groups"
78
userenums "github.com/source-academy/stories-backend/internal/enums/users"
89
"github.com/source-academy/stories-backend/model"
910
)
@@ -12,6 +13,8 @@ type Create struct {
1213
Name string `json:"name"`
1314
Username string `json:"username"`
1415
LoginProvider string `json:"provider"`
16+
// FIXME: Role should be under usergroups create params
17+
Role groupenums.Role `json:"role"`
1518
}
1619

1720
type BatchCreate struct {
@@ -31,6 +34,10 @@ func (params *Create) Validate() error {
3134
return fmt.Errorf("Invalid login provider %s.", params.LoginProvider)
3235
}
3336

37+
if !params.Role.IsValid() {
38+
return fmt.Errorf("Invalid role %s.", params.Role)
39+
}
40+
3441
return nil
3542
}
3643

params/users/create_test.go

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package userparams
33
import (
44
"testing"
55

6+
groupenums "github.com/source-academy/stories-backend/internal/enums/groups"
67
userenums "github.com/source-academy/stories-backend/internal/enums/users"
78
"github.com/stretchr/testify/assert"
89
)
@@ -20,6 +21,7 @@ func TestValidate(t *testing.T) {
2021
params := Create{
2122
Username: "testUsername",
2223
LoginProvider: userenums.LoginProviderNUSNET.ToString(),
24+
Role: groupenums.RoleStandard,
2325
}
2426
err := params.Validate()
2527
assert.Nil(t, err)
@@ -28,6 +30,7 @@ func TestValidate(t *testing.T) {
2830
params := Create{
2931
Username: "testUsername",
3032
LoginProvider: "invalidProvider",
33+
Role: groupenums.RoleStandard,
3134
}
3235
err := params.Validate()
3336
assert.NotNil(t, err)
@@ -36,6 +39,15 @@ func TestValidate(t *testing.T) {
3639
params := Create{
3740
Username: "testUsername",
3841
LoginProvider: userenums.LoginProviderGitHub.ToString(),
42+
Role: groupenums.RoleStandard,
43+
}
44+
err := params.Validate()
45+
assert.NotNil(t, err)
46+
})
47+
t.Run("should ensure role is not empty", func(t *testing.T) {
48+
params := Create{
49+
Username: "testUsername",
50+
LoginProvider: userenums.LoginProviderNUSNET.ToString(),
3951
}
4052
err := params.Validate()
4153
assert.NotNil(t, err)
@@ -47,6 +59,7 @@ func TestToModel(t *testing.T) {
4759
params := Create{
4860
Username: "testUsername",
4961
LoginProvider: userenums.LoginProviderNUSNET.ToString(),
62+
Role: groupenums.RoleStandard,
5063
}
5164
model := params.ToModel()
5265
assert.Equal(t, params.Username, model.Username)

0 commit comments

Comments
 (0)