Skip to content

Commit

Permalink
satellite/admin/back-office: Add settings endpoint
Browse files Browse the repository at this point in the history
Define and implement a new endpoint to expose services' settings.

At this initial point the endpoint only exposes Satellite Admin features
flags.

Closes #6644

Change-Id: I419f9173e9f53f7f9c267904d210141856ddfb5a
  • Loading branch information
ifraixedes authored and Storj Robot committed Jan 12, 2024
1 parent 80cfa9a commit dca3ede
Show file tree
Hide file tree
Showing 7 changed files with 364 additions and 1 deletion.
69 changes: 69 additions & 0 deletions satellite/admin/back-office/api-docs.gen.md
Expand Up @@ -4,6 +4,8 @@

<h2 id='list-of-endpoints'>List of Endpoints</h2>

* Settings
* [Get settings](#settings-get-settings)
* PlacementManagement
* [Get placements](#placementmanagement-get-placements)
* UserManagement
Expand All @@ -12,6 +14,73 @@
* [Get project](#projectmanagement-get-project)
* [Update project limits](#projectmanagement-update-project-limits)

<h3 id='settings-get-settings'>Get settings (<a href='#list-of-endpoints'>go to full list</a>)</h3>

Gets the settings of the service and relevant Storj services settings

`GET /back-office/api/v1/settings/`

**Response body:**

```typescript
{
admin: {
features: {
account: {
create: boolean
delete: boolean
history: boolean
list: boolean
projects: boolean
suspend: boolean
unsuspend: boolean
resetMFA: boolean
updateInfo: boolean
updateLimits: boolean
updatePlacement: boolean
updateStatus: boolean
updateValueAttribution: boolean
view: boolean
}

project: {
create: boolean
delete: boolean
history: boolean
list: boolean
updateInfo: boolean
updateLimits: boolean
updatePlacement: boolean
updateValueAttribution: boolean
view: boolean
memberList: boolean
memberAdd: boolean
memberRemove: boolean
}

bucket: {
create: boolean
delete: boolean
history: boolean
list: boolean
updateInfo: boolean
updatePlacement: boolean
updateValueAttribution: boolean
view: boolean
}

dashboard: boolean
operator: boolean
signOut: boolean
switchSatellite: boolean
}

}

}

```

<h3 id='placementmanagement-get-placements'>Get placements (<a href='#list-of-endpoints'>go to full list</a>)</h3>

Gets placement rule IDs and their locations
Expand Down
12 changes: 11 additions & 1 deletion satellite/admin/back-office/gen/main.go
Expand Up @@ -27,7 +27,17 @@ func main() {
BasePath: path.Join(backoffice.PathPrefix, "/api"),
}

group := api.Group("PlacementManagement", "placements")
group := api.Group("Settings", "settings")

group.Get("/", &apigen.Endpoint{
Name: "Get settings",
Description: "Gets the settings of the service and relevant Storj services settings",
GoName: "GetSettings",
TypeScriptName: "get",
Response: backoffice.Settings{},
})

group = api.Group("PlacementManagement", "placements")

group.Get("/", &apigen.Endpoint{
Name: "Get placements",
Expand Down
44 changes: 44 additions & 0 deletions satellite/admin/back-office/handlers.gen.go
Expand Up @@ -17,10 +17,15 @@ import (
"storj.io/storj/private/api"
)

var ErrSettingsAPI = errs.Class("admin settings api")
var ErrPlacementsAPI = errs.Class("admin placements api")
var ErrUsersAPI = errs.Class("admin users api")
var ErrProjectsAPI = errs.Class("admin projects api")

type SettingsService interface {
GetSettings(ctx context.Context) (*Settings, api.HTTPError)
}

type PlacementManagementService interface {
GetPlacements(ctx context.Context) ([]PlacementInfo, api.HTTPError)
}
Expand All @@ -34,6 +39,13 @@ type ProjectManagementService interface {
UpdateProjectLimits(ctx context.Context, publicID uuid.UUID, request ProjectLimitsUpdate) api.HTTPError
}

// SettingsHandler is an api handler that implements all Settings API endpoints functionality.
type SettingsHandler struct {
log *zap.Logger
mon *monkit.Scope
service SettingsService
}

// PlacementManagementHandler is an api handler that implements all PlacementManagement API endpoints functionality.
type PlacementManagementHandler struct {
log *zap.Logger
Expand All @@ -57,6 +69,19 @@ type ProjectManagementHandler struct {
auth *Authorizer
}

func NewSettings(log *zap.Logger, mon *monkit.Scope, service SettingsService, router *mux.Router) *SettingsHandler {
handler := &SettingsHandler{
log: log,
mon: mon,
service: service,
}

settingsRouter := router.PathPrefix("/back-office/api/v1/settings").Subrouter()
settingsRouter.HandleFunc("/", handler.handleGetSettings).Methods("GET")

return handler
}

func NewPlacementManagement(log *zap.Logger, mon *monkit.Scope, service PlacementManagementService, router *mux.Router) *PlacementManagementHandler {
handler := &PlacementManagementHandler{
log: log,
Expand Down Expand Up @@ -99,6 +124,25 @@ func NewProjectManagement(log *zap.Logger, mon *monkit.Scope, service ProjectMan
return handler
}

func (h *SettingsHandler) handleGetSettings(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
var err error
defer h.mon.Task()(&ctx)(&err)

w.Header().Set("Content-Type", "application/json")

retVal, httpErr := h.service.GetSettings(ctx)
if httpErr.Err != nil {
api.ServeError(h.log, w, httpErr.Status, httpErr.Err)
return
}

err = json.NewEncoder(w).Encode(retVal)
if err != nil {
h.log.Debug("failed to write json GetSettings response", zap.Error(ErrSettingsAPI.Wrap(err)))
}
}

func (h *PlacementManagementHandler) handleGetPlacements(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
var err error
Expand Down
1 change: 1 addition & 0 deletions satellite/admin/back-office/server.go
Expand Up @@ -85,6 +85,7 @@ func NewServer(
NewPlacementManagement(log, mon, service, root)
NewUserManagement(log, mon, service, root, auth)
NewProjectManagement(log, mon, service, root, auth)
NewSettings(log, mon, service, root)

root = root.PathPrefix(PathPrefix).Subrouter()
// Static assets for the web interface.
Expand Down
139 changes: 139 additions & 0 deletions satellite/admin/back-office/settings.go
@@ -0,0 +1,139 @@
// Copyright (C) 2024 Storj Labs, Inc.
// See LICENSE for copying information.

package admin

import (
"context"

"storj.io/storj/private/api"
)

// Settings contains relevant settings for the consumers of this service. It may contain settings
// of:
//
// - this service.
//
// - the server that exposes the service.
//
// - related Storj services (e.g. Satellite).
type Settings struct {
Admin SettingsAdmin `json:"admin"`
}

// SettingsAdmin are the settings of this service and the server that exposes it.
type SettingsAdmin struct {
Features FeatureFlags `json:"features"`
}

// FeatureFlags indicates what Admin service features are enabled or disabled. The features are
// usually disabled when they are not fully implemented.
type FeatureFlags struct {
Account AccountFlags `json:"account"`
Project ProjectFlags `json:"project"`
Bucket BucketFlags `json:"bucket"`
Dashboard bool `json:"dashboard"`
Operator bool `json:"operator"` // This is the information about the logged operator
SignOut bool `json:"signOut"`
SwitchSatellite bool `json:"switchSatellite"`
}

// AccountFlags are the feature flags related to user's accounts.
type AccountFlags struct {
Create bool `json:"create"`
Delete bool `json:"delete"`
History bool `json:"history"`
List bool `json:"list"`
Projects bool `json:"projects"`
Suspend bool `json:"suspend"`
Unsuspend bool `json:"unsuspend"`
ResetMFA bool `json:"resetMFA"`
UpdateInfo bool `json:"updateInfo"`
UpdateLimits bool `json:"updateLimits"`
UpdatePlacement bool `json:"updatePlacement"`
UpdateStatus bool `json:"updateStatus"`
UpdateValueAttribution bool `json:"updateValueAttribution"`
View bool `json:"view"`
}

// ProjectFlags are the feature flags related to projects.
type ProjectFlags struct {
Create bool `json:"create"`
Delete bool `json:"delete"`
History bool `json:"history"`
List bool `json:"list"`
UpdateInfo bool `json:"updateInfo"`
UpdateLimits bool `json:"updateLimits"`
UpdatePlacement bool `json:"updatePlacement"`
UpdateValueAttribution bool `json:"updateValueAttribution"`
View bool `json:"view"`
MemberList bool `json:"memberList"`
MemberAdd bool `json:"memberAdd"`
MemberRemove bool `json:"memberRemove"`
}

// BucketFlags are the feature flags related to buckets.
type BucketFlags struct {
Create bool `json:"create"`
Delete bool `json:"delete"`
History bool `json:"history"`
List bool `json:"list"`
UpdateInfo bool `json:"updateInfo"`
UpdatePlacement bool `json:"updatePlacement"`
UpdateValueAttribution bool `json:"updateValueAttribution"`
View bool `json:"view"`
}

// GetSettings returns the service settings.
func (s *Service) GetSettings(ctx context.Context) (*Settings, api.HTTPError) {
return &Settings{
Admin: SettingsAdmin{
Features: FeatureFlags{
Account: AccountFlags{
Create: false,
Delete: false,
History: false,
List: false,
Projects: true,
Suspend: false,
Unsuspend: false,
ResetMFA: false,
UpdateInfo: false,
UpdateLimits: false,
UpdatePlacement: false,
UpdateStatus: false,
UpdateValueAttribution: false,
View: true,
},
Project: ProjectFlags{
Create: false,
Delete: false,
History: false,
List: false,
UpdateInfo: false,
UpdateLimits: false,
UpdatePlacement: false,
UpdateValueAttribution: false,
View: false,
MemberList: false,
MemberAdd: false,
MemberRemove: false,
},
Bucket: BucketFlags{
Create: false,
Delete: false,
History: false,
List: false,
UpdateInfo: false,
UpdatePlacement: false,
UpdateValueAttribution: false,
View: false,
},
Dashboard: false,
Operator: false,
SignOut: false,
SwitchSatellite: false,
},
},
}, api.HTTPError{}
}
24 changes: 24 additions & 0 deletions satellite/admin/back-office/settings_test.go
@@ -0,0 +1,24 @@
// Copyright (C) 2024 Storj Labs, Inc.
// See LICENSE for copying information.

package admin_test

import (
"testing"

"github.com/stretchr/testify/require"

"storj.io/common/testcontext"
"storj.io/storj/private/testplanet"
)

func TestGetSettings(t *testing.T) {
testplanet.Run(t, testplanet.Config{
SatelliteCount: 1,
}, func(t *testing.T, ctx *testcontext.Context, planet *testplanet.Planet) {
service := planet.Satellites[0].Admin.Admin.Service

_, apiErr := service.GetSettings(ctx)
require.NoError(t, apiErr.Err)
})
}

0 comments on commit dca3ede

Please sign in to comment.