diff --git a/satellite/admin/ui/src/lib/api.ts b/satellite/admin/ui/src/lib/api.ts index f7ddc8ce0..e0a0ed4f6 100644 --- a/satellite/admin/ui/src/lib/api.ts +++ b/satellite/admin/ui/src/lib/api.ts @@ -378,25 +378,31 @@ Blank fields will not be updated.`, } }, { - name: 'update user and project limits', + name: "update user's project limits", desc: `Update limits for all of user's existing and future projects. Blank fields will not be updated.`, params: [ ["current user's email", new InputText('email', true)], - ['project storage limit (in bytes)', new InputText('number', false)], - ['project bandwidth limit (in bytes)', new InputText('number', false)], + [ + 'project storage limit (in bytes or notations like 1GB, 2tb)', + new InputText('text', false) + ], + [ + 'project bandwidth limit (in bytes or notations like 1GB, 2tb)', + new InputText('text', false) + ], ['project segment limit (max number)', new InputText('number', false)] ], func: async ( currentEmail: string, - projectStorageLimit?: number, - projectBandwidthLimit?: number, - projectSegmentLimit?: number + storage?: number, + bandwidth?: number, + segment?: number ): Promise => { return this.fetch('PUT', `users/${currentEmail}/limits`, null, { - projectStorageLimit, - projectBandwidthLimit, - projectSegmentLimit + storage, + bandwidth, + segment }) as Promise; } }, diff --git a/satellite/admin/user.go b/satellite/admin/user.go index 8cc7e8aad..eb289ad13 100644 --- a/satellite/admin/user.go +++ b/satellite/admin/user.go @@ -15,6 +15,7 @@ import ( "github.com/gorilla/mux" "golang.org/x/crypto/bcrypt" + "storj.io/common/memory" "storj.io/common/uuid" "storj.io/storj/satellite/console" ) @@ -224,7 +225,7 @@ func (server *Server) userLimits(w http.ResponseWriter, r *http.Request) { var limits struct { Storage int64 `json:"storage"` Bandwidth int64 `json:"bandwidth"` - Segment int64 `json:"maxSegments"` + Segment int64 `json:"segment"` } limits.Storage = user.ProjectStorageLimit @@ -362,12 +363,12 @@ func (server *Server) updateLimits(w http.ResponseWriter, r *http.Request) { return } - type User struct { - console.User + var input struct { + Storage memory.Size `json:"storage"` + Bandwidth memory.Size `json:"bandwidth"` + Segment int64 `json:"segment"` } - var input User - err = json.Unmarshal(body, &input) if err != nil { sendJSONError(w, "failed to unmarshal request", @@ -375,38 +376,46 @@ func (server *Server) updateLimits(w http.ResponseWriter, r *http.Request) { return } - updateRequest := console.UpdateUserRequest{} - - if input.ProjectStorageLimit > 0 { - updateRequest.ProjectStorageLimit = &input.ProjectStorageLimit - } - if input.ProjectBandwidthLimit > 0 { - updateRequest.ProjectBandwidthLimit = &input.ProjectBandwidthLimit - } - if input.ProjectSegmentLimit > 0 { - updateRequest.ProjectSegmentLimit = &input.ProjectSegmentLimit + newLimits := console.UsageLimits{ + Storage: user.ProjectStorageLimit, + Bandwidth: user.ProjectBandwidthLimit, + Segment: user.ProjectSegmentLimit, } - userLimits := console.UsageLimits{ - Storage: *updateRequest.ProjectStorageLimit, - Bandwidth: *updateRequest.ProjectBandwidthLimit, - Segment: *updateRequest.ProjectSegmentLimit, + if input.Storage > 0 { + newLimits.Storage = input.Storage.Int64() + } + if input.Bandwidth > 0 { + newLimits.Bandwidth = input.Bandwidth.Int64() + } + if input.Segment > 0 { + newLimits.Segment = input.Segment } - err = server.db.Console().Users().UpdateUserProjectLimits(ctx, user.ID, userLimits) + if newLimits.Storage == user.ProjectStorageLimit && + newLimits.Bandwidth == user.ProjectBandwidthLimit && + newLimits.Segment == user.ProjectSegmentLimit { + sendJSONError(w, "no limits to update", + "new values are equal to old ones", http.StatusBadRequest) + return + } + + err = server.db.Console().Users().UpdateUserProjectLimits(ctx, user.ID, newLimits) if err != nil { sendJSONError(w, "failed to update user limits", err.Error(), http.StatusInternalServerError) + return } userProjects, err := server.db.Console().Projects().GetOwn(ctx, user.ID) if err != nil { sendJSONError(w, "failed to get user's projects", err.Error(), http.StatusInternalServerError) + return } for _, p := range userProjects { - err = server.db.Console().Projects().UpdateUsageLimits(ctx, p.ID, userLimits) + err = server.db.Console().Projects().UpdateUsageLimits(ctx, p.ID, newLimits) if err != nil { sendJSONError(w, "failed to update project limits", err.Error(), http.StatusInternalServerError) diff --git a/satellite/admin/user_test.go b/satellite/admin/user_test.go index f6f647215..6d833d1ce 100644 --- a/satellite/admin/user_test.go +++ b/satellite/admin/user_test.go @@ -14,6 +14,7 @@ import ( "github.com/stretchr/testify/require" "go.uber.org/zap" + "storj.io/common/memory" "storj.io/common/testcontext" "storj.io/storj/private/testplanet" "storj.io/storj/satellite" @@ -204,32 +205,55 @@ func TestUserUpdate(t *testing.T) { require.Equal(t, newUsageLimit, updatedUserStatusAndUsageLimits.ProjectBandwidthLimit) require.Equal(t, newUsageLimit, updatedUserStatusAndUsageLimits.ProjectSegmentLimit) - // Update user limits and project limits (current and existing projects for a user). - link = "http://" + address.String() + "/api/users/alice+2@mail.test/limits" - newStorageLimit := int64(15000) - newBandwidthLimit := int64(25000) - newSegmentLimit := int64(35000) - body2 := fmt.Sprintf(`{"projectStorageLimit":%d, "projectBandwidthLimit":%d, "projectSegmentLimit":%d}`, newStorageLimit, newBandwidthLimit, newSegmentLimit) - responseBody = assertReq(ctx, t, link, http.MethodPut, body2, http.StatusOK, "", planet.Satellites[0].Config.Console.AuthToken) - require.Len(t, responseBody, 0) + var updateLimitsTests = []struct { + newStorageLimit memory.Size + newBandwidthLimit memory.Size + newSegmentLimit int64 + useSizeString bool + }{ + { + 15000, + 25000, + 35000, + false, + }, + { + 50 * memory.KB, + 75 * memory.KB, + 40000, + true, + }, + } - // Get user limits returns new updated limits - link2 := "http://" + address.String() + "/api/users/alice+2@mail.test/limits" - expectedBody := `{` + - fmt.Sprintf(`"storage":%d,"bandwidth":%d,"maxSegments":%d}`, newStorageLimit, newBandwidthLimit, newSegmentLimit) - assertReq(ctx, t, link2, http.MethodGet, "", http.StatusOK, expectedBody, planet.Satellites[0].Config.Console.AuthToken) + for _, tt := range updateLimitsTests { + // Update user limits and project limits (current and existing projects for a user). + link = "http://" + address.String() + "/api/users/alice+2@mail.test/limits" + jsonStr := `{"storage":"%d", "bandwidth":"%d", "segment":%d}` + if tt.useSizeString { + jsonStr = strings.Replace(jsonStr, "%d", "%v", 2) + } + body2 := fmt.Sprintf(jsonStr, tt.newStorageLimit, tt.newBandwidthLimit, tt.newSegmentLimit) + responseBody = assertReq(ctx, t, link, http.MethodPut, body2, http.StatusOK, "", planet.Satellites[0].Config.Console.AuthToken) + require.Len(t, responseBody, 0) - userUpdatedLimits, err := planet.Satellites[0].DB.Console().Users().Get(ctx, user.ID) - require.NoError(t, err) - require.Equal(t, newStorageLimit, userUpdatedLimits.ProjectStorageLimit) - require.Equal(t, newBandwidthLimit, userUpdatedLimits.ProjectBandwidthLimit) - require.Equal(t, newSegmentLimit, userUpdatedLimits.ProjectSegmentLimit) + // Get user limits returns new updated limits + link2 := "http://" + address.String() + "/api/users/alice+2@mail.test/limits" + expectedBody := `{` + + fmt.Sprintf(`"storage":%d,"bandwidth":%d,"segment":%d}`, tt.newStorageLimit, tt.newBandwidthLimit, tt.newSegmentLimit) + assertReq(ctx, t, link2, http.MethodGet, "", http.StatusOK, expectedBody, planet.Satellites[0].Config.Console.AuthToken) - projectUpdatedLimits, err := planet.Satellites[0].DB.Console().Projects().Get(ctx, planet.Uplinks[0].Projects[0].ID) - require.NoError(t, err) - require.Equal(t, newStorageLimit, projectUpdatedLimits.StorageLimit.Int64()) - require.Equal(t, newBandwidthLimit, projectUpdatedLimits.BandwidthLimit.Int64()) - require.Equal(t, newSegmentLimit, *projectUpdatedLimits.SegmentLimit) + userUpdatedLimits, err := planet.Satellites[0].DB.Console().Users().Get(ctx, user.ID) + require.NoError(t, err) + require.Equal(t, tt.newStorageLimit.Int64(), userUpdatedLimits.ProjectStorageLimit) + require.Equal(t, tt.newBandwidthLimit.Int64(), userUpdatedLimits.ProjectBandwidthLimit) + require.Equal(t, tt.newSegmentLimit, userUpdatedLimits.ProjectSegmentLimit) + + projectUpdatedLimits, err := planet.Satellites[0].DB.Console().Projects().Get(ctx, planet.Uplinks[0].Projects[0].ID) + require.NoError(t, err) + require.Equal(t, tt.newStorageLimit, *projectUpdatedLimits.StorageLimit) + require.Equal(t, tt.newBandwidthLimit, *projectUpdatedLimits.BandwidthLimit) + require.Equal(t, tt.newSegmentLimit, *projectUpdatedLimits.SegmentLimit) + } }) t.Run("Not found", func(t *testing.T) {