Skip to content

Commit

Permalink
apigen: endpoint to get user
Browse files Browse the repository at this point in the history
Implemented new GET user by request context endpoint.
Updated docs.

Change-Id: Iebb493e55f9456b89d7dbd234bb0b939b82b0ced
  • Loading branch information
VitaliiShpital authored and Storj Robot committed Jun 6, 2022
1 parent ba58530 commit f0b28d6
Show file tree
Hide file tree
Showing 6 changed files with 168 additions and 27 deletions.
104 changes: 77 additions & 27 deletions satellite/console/consoleweb/consoleapi/api.gen.go
Expand Up @@ -23,6 +23,7 @@ const dateLayout = "2006-01-02T15:04:05.000Z"

var ErrProjectsAPI = errs.Class("consoleapi projects api")
var ErrApikeysAPI = errs.Class("consoleapi apikeys api")
var ErrUsersAPI = errs.Class("consoleapi users api")

type ProjectManagementService interface {
GenGetUsersProjects(context.Context) ([]console.Project, api.HTTPError)
Expand All @@ -36,6 +37,10 @@ type APIKeyManagementService interface {
GenCreateAPIKey(context.Context, console.CreateAPIKeyRequest) (*console.CreateAPIKeyResponse, api.HTTPError)
}

type UserManagementService interface {
GenGetUser(context.Context) (*console.ResponseUser, api.HTTPError)
}

// ProjectManagementHandler is an api handler that exposes all projects related functionality.
type ProjectManagementHandler struct {
log *zap.Logger
Expand All @@ -50,6 +55,13 @@ type APIKeyManagementHandler struct {
auth api.Auth
}

// UserManagementHandler is an api handler that exposes all users related functionality.
type UserManagementHandler struct {
log *zap.Logger
service UserManagementService
auth api.Auth
}

func NewProjectManagement(log *zap.Logger, service ProjectManagementService, router *mux.Router, auth api.Auth) *ProjectManagementHandler {
handler := &ProjectManagementHandler{
log: log,
Expand All @@ -58,11 +70,11 @@ func NewProjectManagement(log *zap.Logger, service ProjectManagementService, rou
}

projectsRouter := router.PathPrefix("/api/v0/projects").Subrouter()
projectsRouter.HandleFunc("/bucket-rollup", handler.handleGenGetSingleBucketUsageRollup).Methods("GET")
projectsRouter.HandleFunc("/bucket-rollups", handler.handleGenGetBucketUsageRollups).Methods("GET")
projectsRouter.HandleFunc("/create", handler.handleGenCreateProject).Methods("POST")
projectsRouter.HandleFunc("/update/{id}", handler.handleGenUpdateProject).Methods("PATCH")
projectsRouter.HandleFunc("/", handler.handleGenGetUsersProjects).Methods("GET")
projectsRouter.HandleFunc("/bucket-rollup", handler.handleGenGetSingleBucketUsageRollup).Methods("GET")

return handler
}
Expand All @@ -80,6 +92,62 @@ func NewAPIKeyManagement(log *zap.Logger, service APIKeyManagementService, route
return handler
}

func NewUserManagement(log *zap.Logger, service UserManagementService, router *mux.Router, auth api.Auth) *UserManagementHandler {
handler := &UserManagementHandler{
log: log,
service: service,
auth: auth,
}

usersRouter := router.PathPrefix("/api/v0/users").Subrouter()
usersRouter.HandleFunc("/", handler.handleGenGetUser).Methods("GET")

return handler
}

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

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

ctx, err = h.auth.IsAuthenticated(ctx, r, true, true)
if err != nil {
api.ServeError(h.log, w, http.StatusUnauthorized, err)
return
}

projectID, err := uuid.FromString(r.URL.Query().Get("projectID"))
if err != nil {
api.ServeError(h.log, w, http.StatusBadRequest, err)
return
}

since, err := time.Parse(dateLayout, r.URL.Query().Get("since"))
if err != nil {
api.ServeError(h.log, w, http.StatusBadRequest, err)
return
}

before, err := time.Parse(dateLayout, r.URL.Query().Get("before"))
if err != nil {
api.ServeError(h.log, w, http.StatusBadRequest, err)
return
}

retVal, httpErr := h.service.GenGetBucketUsageRollups(ctx, projectID, since, before)
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 GenGetBucketUsageRollups response", zap.Error(ErrProjectsAPI.Wrap(err)))
}
}

func (h *ProjectManagementHandler) handleGenCreateProject(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
var err error
Expand Down Expand Up @@ -228,7 +296,7 @@ func (h *ProjectManagementHandler) handleGenGetSingleBucketUsageRollup(w http.Re
}
}

func (h *ProjectManagementHandler) handleGenGetBucketUsageRollups(w http.ResponseWriter, r *http.Request) {
func (h *APIKeyManagementHandler) handleGenCreateAPIKey(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
var err error
defer mon.Task()(&ctx)(&err)
Expand All @@ -241,37 +309,25 @@ func (h *ProjectManagementHandler) handleGenGetBucketUsageRollups(w http.Respons
return
}

projectID, err := uuid.FromString(r.URL.Query().Get("projectID"))
if err != nil {
api.ServeError(h.log, w, http.StatusBadRequest, err)
return
}

since, err := time.Parse(dateLayout, r.URL.Query().Get("since"))
if err != nil {
api.ServeError(h.log, w, http.StatusBadRequest, err)
return
}

before, err := time.Parse(dateLayout, r.URL.Query().Get("before"))
if err != nil {
apikeyInfo := &console.CreateAPIKeyRequest{}
if err = json.NewDecoder(r.Body).Decode(&apikeyInfo); err != nil {
api.ServeError(h.log, w, http.StatusBadRequest, err)
return
}

retVal, httpErr := h.service.GenGetBucketUsageRollups(ctx, projectID, since, before)
retVal, httpErr := h.service.GenCreateAPIKey(ctx, *apikeyInfo)
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 GenGetBucketUsageRollups response", zap.Error(ErrProjectsAPI.Wrap(err)))
h.log.Debug("failed to write json GenCreateAPIKey response", zap.Error(ErrApikeysAPI.Wrap(err)))
}
}

func (h *APIKeyManagementHandler) handleGenCreateAPIKey(w http.ResponseWriter, r *http.Request) {
func (h *UserManagementHandler) handleGenGetUser(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
var err error
defer mon.Task()(&ctx)(&err)
Expand All @@ -284,20 +340,14 @@ func (h *APIKeyManagementHandler) handleGenCreateAPIKey(w http.ResponseWriter, r
return
}

apikeyInfo := &console.CreateAPIKeyRequest{}
if err = json.NewDecoder(r.Body).Decode(&apikeyInfo); err != nil {
api.ServeError(h.log, w, http.StatusBadRequest, err)
return
}

retVal, httpErr := h.service.GenCreateAPIKey(ctx, *apikeyInfo)
retVal, httpErr := h.service.GenGetUser(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 GenCreateAPIKey response", zap.Error(ErrApikeysAPI.Wrap(err)))
h.log.Debug("failed to write json GenGetUser response", zap.Error(ErrUsersAPI.Wrap(err)))
}
}
26 changes: 26 additions & 0 deletions satellite/console/consoleweb/consoleapi/gen/README.md
Expand Up @@ -214,3 +214,29 @@ A successful response body:
}
}
```

### User Endpoints
#### GET /api/v0/users/
Returns User entity by request context.

A successful response body:

```json
{
"id": "00fe2cef-9412-4c9c-bc42-e57cc062bb7d",
"fullName": "Test",
"shortName": "",
"email": "test@test.com",
"partnerId": "00000000-0000-0000-0000-000000000000",
"userAgent": null,
"projectLimit": 1,
"isProfessional": false,
"position": "",
"companyName": "",
"employeeCount": "",
"haveSalesContact": false,
"paidTier": false,
"isMFAEnabled": false,
"mfaRecoveryCodeCount": 0
}
```
11 changes: 11 additions & 0 deletions satellite/console/consoleweb/consoleapi/gen/main.go
Expand Up @@ -93,5 +93,16 @@ func main() {
})
}

{
g := a.Group("UserManagement", "users")

g.Get("/", &apigen.Endpoint{
Name: "Get User",
Description: "Gets User by request context",
MethodName: "GenGetUser",
Response: &console.ResponseUser{},
})
}

a.MustWriteGo("satellite/console/consoleweb/consoleapi/api.gen.go")
}
1 change: 1 addition & 0 deletions satellite/console/consoleweb/server.go
Expand Up @@ -221,6 +221,7 @@ func NewServer(logger *zap.Logger, config Config, service *console.Service, oidc
if server.config.GeneratedAPIEnabled {
consoleapi.NewProjectManagement(logger, server.service, router, server.service)
consoleapi.NewAPIKeyManagement(logger, server.service, router, server.service)
consoleapi.NewUserManagement(logger, server.service, router, server.service)
}

router.HandleFunc("/registrationToken/", server.createRegistrationTokenHandler)
Expand Down
34 changes: 34 additions & 0 deletions satellite/console/service.go
Expand Up @@ -1047,6 +1047,40 @@ func (s *Service) GetUser(ctx context.Context, id uuid.UUID) (u *User, err error
return user, nil
}

// GenGetUser returns ResponseUser by request context for generated api.
func (s *Service) GenGetUser(ctx context.Context) (*ResponseUser, api.HTTPError) {
var err error
defer mon.Task()(&ctx)(&err)

auth, err := s.getAuthAndAuditLog(ctx, "get user")
if err != nil {
return nil, api.HTTPError{
Status: http.StatusUnauthorized,
Err: Error.Wrap(err),
}
}

user := &ResponseUser{
ID: auth.User.ID,
FullName: auth.User.FullName,
ShortName: auth.User.ShortName,
Email: auth.User.Email,
PartnerID: auth.User.PartnerID,
UserAgent: auth.User.UserAgent,
ProjectLimit: auth.User.ProjectLimit,
IsProfessional: auth.User.IsProfessional,
Position: auth.User.Position,
CompanyName: auth.User.CompanyName,
EmployeeCount: auth.User.EmployeeCount,
HaveSalesContact: auth.User.HaveSalesContact,
PaidTier: auth.User.PaidTier,
MFAEnabled: auth.User.MFAEnabled,
MFARecoveryCodeCount: len(auth.User.MFARecoveryCodes),
}

return user, api.HTTPError{}
}

// GetUserID returns the User ID from the session.
func (s *Service) GetUserID(ctx context.Context) (id uuid.UUID, err error) {
defer mon.Task()(&ctx)(&err)
Expand Down
19 changes: 19 additions & 0 deletions satellite/console/users.go
Expand Up @@ -174,3 +174,22 @@ type User struct {
FailedLoginCount int `json:"failedLoginCount"`
LoginLockoutExpiration time.Time `json:"loginLockoutExpiration"`
}

// ResponseUser is an entity which describes db User and can be sent in response.
type ResponseUser struct {
ID uuid.UUID `json:"id"`
FullName string `json:"fullName"`
ShortName string `json:"shortName"`
Email string `json:"email"`
PartnerID uuid.UUID `json:"partnerId"`
UserAgent []byte `json:"userAgent"`
ProjectLimit int `json:"projectLimit"`
IsProfessional bool `json:"isProfessional"`
Position string `json:"position"`
CompanyName string `json:"companyName"`
EmployeeCount string `json:"employeeCount"`
HaveSalesContact bool `json:"haveSalesContact"`
PaidTier bool `json:"paidTier"`
MFAEnabled bool `json:"isMFAEnabled"`
MFARecoveryCodeCount int `json:"mfaRecoveryCodeCount"`
}

1 comment on commit f0b28d6

@storjrobot
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This commit has been mentioned on Storj Community Forum (official). There might be relevant details there:

https://forum.storj.io/t/release-preparation-v1-57/18810/1

Please sign in to comment.