Start creating user resources without ID.
This commit is contained in:
parent
a656d45a6d
commit
6fbde6389d
5 changed files with 159 additions and 33 deletions
|
|
@ -66,6 +66,43 @@ func authenticatedHandlerJSON[T any](f handlerFunctionGet[T]) http.Handler {
|
|||
})
|
||||
}
|
||||
|
||||
type handlerFunctionGetSlice[T any] func(context.Context, *http.Request, platform.User, resource.QueryParams) ([]*T, *nhttp.ErrorWithStatus)
|
||||
|
||||
func authenticatedHandlerJSONSlice[T any](f handlerFunctionGetSlice[T]) http.Handler {
|
||||
return auth.NewEnsureAuth(func(w http.ResponseWriter, r *http.Request, u platform.User) {
|
||||
ctx := r.Context()
|
||||
var body []byte
|
||||
var params resource.QueryParams
|
||||
err := decoder.Decode(¶ms, r.URL.Query())
|
||||
if err != nil {
|
||||
log.Error().Err(err).Msg("decode query failure")
|
||||
http.Error(w, "failed to decode query", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
resp, e := f(ctx, r, u, params)
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
//log.Info().Str("template", template).Err(e).Msg("handler done")
|
||||
if e != nil {
|
||||
log.Warn().Int("status", e.Status).Err(e).Str("user message", e.Message).Msg("Responding with an error from api")
|
||||
body, err = json.Marshal(ErrorAPI{Message: e.Error()})
|
||||
if err != nil {
|
||||
log.Error().Err(err).Msg("failed to marshal error")
|
||||
http.Error(w, "{\"message\": \"boom. I can't even tell you what went wrong\"}", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
http.Error(w, string(body), e.Status)
|
||||
return
|
||||
}
|
||||
body, err = json.Marshal(resp)
|
||||
if err != nil {
|
||||
log.Error().Err(err).Msg("failed to marshal json")
|
||||
http.Error(w, "{\"message\": \"failed to marshal json\"}", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
w.Write(body)
|
||||
})
|
||||
}
|
||||
|
||||
type handlerFunctionPost[ReqType any] func(context.Context, *http.Request, ReqType) (string, *nhttp.ErrorWithStatus)
|
||||
type handlerFunctionPostAuthenticated[ReqType any] func(context.Context, *http.Request, platform.User, ReqType) (string, *nhttp.ErrorWithStatus)
|
||||
|
||||
|
|
|
|||
|
|
@ -50,11 +50,11 @@ func AddRoutes(r *mux.Router) {
|
|||
r.Handle("/upload/{id}/commit", authenticatedHandlerJSONPost(upload.Commit)).Methods("POST")
|
||||
r.Handle("/upload/{id}/discard", authenticatedHandlerJSONPost(upload.Discard)).Methods("POST")
|
||||
|
||||
user := resource.NewUser(r)
|
||||
user := resource.User(r)
|
||||
r.Handle("/user/self", authenticatedHandlerJSON(user.SelfGet)).Methods("GET")
|
||||
r.Handle("/user/suggestion", authenticatedHandlerJSON(user.SuggestionGet)).Methods("GET")
|
||||
r.Handle("/user", authenticatedHandlerJSON(user.List)).Methods("GET")
|
||||
r.Handle("/user/{id}", authenticatedHandlerJSON(user.ByIDGet)).Methods("GET")
|
||||
r.Handle("/user", authenticatedHandlerJSONSlice(user.List)).Methods("GET")
|
||||
r.Handle("/user/{id}", authenticatedHandlerJSON(user.ByIDGet)).Methods("GET").Name("user.ByIDGet")
|
||||
r.Handle("/user/{id}", authenticatedHandlerJSONPut(user.ByIDPut)).Methods("PUT")
|
||||
|
||||
// Unauthenticated endpoints
|
||||
|
|
|
|||
|
|
@ -98,6 +98,21 @@ func OrganizationByID(ctx context.Context, id int) (*Organization, error) {
|
|||
o := newOrganization(org)
|
||||
return &o, nil
|
||||
}
|
||||
func OrganizationList(ctx context.Context, user User) ([]*Organization, error) {
|
||||
if !user.HasRoot() {
|
||||
return []*Organization{&user.Organization}, nil
|
||||
}
|
||||
rows, err := models.Organizations.Query().All(ctx, db.PGInstance.BobDB)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("query orgs: %w", err)
|
||||
}
|
||||
results := make([]*Organization, len(rows))
|
||||
for i, row := range rows {
|
||||
o := newOrganization(row)
|
||||
results[i] = &o
|
||||
}
|
||||
return results, err
|
||||
}
|
||||
func newOrganization(org *models.Organization) Organization {
|
||||
var sa *ServiceArea
|
||||
if org.ServiceAreaXmax.IsValue() &&
|
||||
|
|
|
|||
|
|
@ -37,7 +37,6 @@ type User struct {
|
|||
PasswordHashType string `json:"-"`
|
||||
Role string `json:"role"`
|
||||
Tags []string `json:"tags"`
|
||||
URI string `json:"uri"`
|
||||
Username string `json:"username"`
|
||||
|
||||
model *models.User
|
||||
|
|
@ -67,7 +66,6 @@ func newUser(ctx context.Context, org Organization, user *models.User) User {
|
|||
PasswordHashType: string(user.PasswordHashType),
|
||||
Role: user.Role.String(),
|
||||
Tags: []string{},
|
||||
URI: fmt.Sprintf("/user/%d", user.ID),
|
||||
Username: user.Username,
|
||||
|
||||
model: user,
|
||||
|
|
@ -113,6 +111,39 @@ func UserByID(ctx context.Context, user_id int32) (*User, error) {
|
|||
func UserByUsername(ctx context.Context, username string) (*User, error) {
|
||||
return getUser(ctx, models.SelectWhere.Users.Username.EQ(username))
|
||||
}
|
||||
func UserList(ctx context.Context, user User) ([]*User, error) {
|
||||
var query models.UsersQuery
|
||||
var orgByID map[int32]*Organization
|
||||
if user.HasRoot() {
|
||||
query = models.Users.Query()
|
||||
orgs, err := OrganizationList(ctx, user)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("org list: %w", err)
|
||||
}
|
||||
orgByID = make(map[int32]*Organization, len(orgs))
|
||||
for _, org := range orgs {
|
||||
orgByID[org.ID] = org
|
||||
}
|
||||
} else {
|
||||
query = user.Organization.model.User()
|
||||
orgByID = make(map[int32]*Organization, 1)
|
||||
orgByID[user.model.OrganizationID] = &user.Organization
|
||||
}
|
||||
rows, err := query.All(ctx, db.PGInstance.BobDB)
|
||||
results := make([]*User, len(rows))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("query users: %w", err)
|
||||
}
|
||||
for i, row := range rows {
|
||||
org, ok := orgByID[row.OrganizationID]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("get org %d", row.OrganizationID)
|
||||
}
|
||||
new_user := newUser(ctx, *org, row)
|
||||
results[i] = &new_user
|
||||
}
|
||||
return results, nil
|
||||
}
|
||||
func UsersByOrg(ctx context.Context, org Organization) (map[int32]*User, error) {
|
||||
users, err := org.model.User().All(ctx, db.PGInstance.BobDB)
|
||||
if err != nil {
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ package resource
|
|||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strconv"
|
||||
|
||||
|
|
@ -13,16 +14,56 @@ import (
|
|||
"github.com/rs/zerolog/log"
|
||||
)
|
||||
|
||||
type userR struct {
|
||||
router *mux.Router
|
||||
type userResponse struct {
|
||||
Avatar string `json:"avatar"`
|
||||
DisplayName string `json:"display_name"`
|
||||
Initials string `json:"initials"`
|
||||
IsActive bool `json:"is_active"`
|
||||
//Notifications []Notification `json:"notifications"`
|
||||
//NotificationCounts UserNotificationCounts `json:"notification_counts"`
|
||||
//Organization Organization `json:"organization"`
|
||||
PasswordHash string `json:"-"`
|
||||
PasswordHashType string `json:"-"`
|
||||
Role string `json:"role"`
|
||||
Tags []string `json:"tags"`
|
||||
URI string `json:"uri"`
|
||||
Username string `json:"username"`
|
||||
}
|
||||
|
||||
func NewUser(r *mux.Router) *userR {
|
||||
func User(r *mux.Router) *userR {
|
||||
return &userR{
|
||||
router: r,
|
||||
}
|
||||
}
|
||||
func (res *userR) response(u *platform.User) (*userResponse, error) {
|
||||
if u == nil {
|
||||
return nil, fmt.Errorf("nil user")
|
||||
}
|
||||
log.Info().Int("id", u.ID).Msg("making response from user")
|
||||
i := strconv.FormatInt(int64(u.ID), 10)
|
||||
handler := res.router.Get("user.ByIDGet")
|
||||
if handler == nil {
|
||||
return nil, fmt.Errorf("nil handler")
|
||||
}
|
||||
uri, err := handler.URL("id", i)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("build uri: %w", err)
|
||||
}
|
||||
return &userResponse{
|
||||
Avatar: u.Avatar,
|
||||
DisplayName: u.DisplayName,
|
||||
Initials: u.Initials,
|
||||
IsActive: u.Active,
|
||||
Role: u.Role,
|
||||
Tags: u.Tags,
|
||||
URI: uri.String(),
|
||||
Username: u.Username,
|
||||
}, nil
|
||||
}
|
||||
|
||||
type userR struct {
|
||||
router *mux.Router
|
||||
}
|
||||
type responseListUser struct {
|
||||
Users []*platform.User `json:"users"`
|
||||
}
|
||||
|
|
@ -56,6 +97,21 @@ func (res *userR) ByIDGet(ctx context.Context, r *http.Request, user platform.Us
|
|||
return u, nil
|
||||
}
|
||||
|
||||
func (res *userR) ByIDPut(ctx context.Context, r *http.Request, user platform.User, updates platform.UserChangeRequest) (string, *nhttp.ErrorWithStatus) {
|
||||
log.Info().Str("avatar", updates.Avatar).Msg("doing updates")
|
||||
vars := mux.Vars(r)
|
||||
user_id_str := vars["id"]
|
||||
user_id, err := strconv.Atoi(user_id_str)
|
||||
if err != nil {
|
||||
return "", nhttp.NewErrorStatus(http.StatusBadRequest, "user update: %w", err)
|
||||
}
|
||||
err = platform.UserUpdate(ctx, user, user_id, updates)
|
||||
if err != nil {
|
||||
return "", nhttp.NewError("user update: %w", err)
|
||||
}
|
||||
return "", nil
|
||||
}
|
||||
|
||||
func (res *userR) SelfGet(ctx context.Context, r *http.Request, user platform.User, query QueryParams) (*contentUserSelf, *nhttp.ErrorWithStatus) {
|
||||
counts, err := platform.NotificationCountsForUser(ctx, user)
|
||||
if err != nil {
|
||||
|
|
@ -86,20 +142,22 @@ func (res *userR) SelfGet(ctx context.Context, r *http.Request, user platform.Us
|
|||
}, nil
|
||||
}
|
||||
|
||||
func (res *userR) List(ctx context.Context, r *http.Request, user platform.User, query QueryParams) (*responseListUser, *nhttp.ErrorWithStatus) {
|
||||
users, err := platform.UsersByOrg(ctx, user.Organization)
|
||||
func (res *userR) List(ctx context.Context, r *http.Request, user platform.User, query QueryParams) ([]*userResponse, *nhttp.ErrorWithStatus) {
|
||||
users, err := platform.UserList(ctx, user)
|
||||
if err != nil {
|
||||
return nil, nhttp.NewError("list users: %w", err)
|
||||
}
|
||||
results := make([]*platform.User, len(users))
|
||||
i := 0
|
||||
for _, v := range users {
|
||||
results[i] = v
|
||||
i++
|
||||
results := make([]*userResponse, len(users))
|
||||
log.Debug().Int("len", len(users)).Msg("building response")
|
||||
for i, v := range users {
|
||||
log.Debug().Int("i", i).Msg("making results")
|
||||
resp, err := res.response(v)
|
||||
if err != nil {
|
||||
return nil, nhttp.NewError("create response: %w", err)
|
||||
}
|
||||
results[i] = resp
|
||||
}
|
||||
return &responseListUser{
|
||||
Users: results,
|
||||
}, nil
|
||||
return results, nil
|
||||
}
|
||||
|
||||
type responseListUserSuggestion struct {
|
||||
|
|
@ -118,18 +176,3 @@ func (res *userR) SuggestionGet(ctx context.Context, r *http.Request, user platf
|
|||
Users: users,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (res *userR) ByIDPut(ctx context.Context, r *http.Request, user platform.User, updates platform.UserChangeRequest) (string, *nhttp.ErrorWithStatus) {
|
||||
log.Info().Str("avatar", updates.Avatar).Msg("doing updates")
|
||||
vars := mux.Vars(r)
|
||||
user_id_str := vars["id"]
|
||||
user_id, err := strconv.Atoi(user_id_str)
|
||||
if err != nil {
|
||||
return "", nhttp.NewErrorStatus(http.StatusBadRequest, "user update: %w", err)
|
||||
}
|
||||
err = platform.UserUpdate(ctx, user, user_id, updates)
|
||||
if err != nil {
|
||||
return "", nhttp.NewError("user update: %w", err)
|
||||
}
|
||||
return "", nil
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue