feat: custom content type icons, built-in types, user lookup
This commit is contained in:
1 parent
e6e088c963
commit
73a75d5cb9
31 files changed
+1611
-36
No files matched your search
@@ -0,0 +1,38 @@
|
||||
package builtintype
|
||||
|
||||
import (
|
||||
"RenewCMS/api/pkg/apperr"
|
||||
"RenewCMS/api/pkg/response"
|
||||
domain "RenewCMS/internal/domain/builtintype"
|
||||
"RenewCMS/internal/infrastructure/useCases"
|
||||
"errors"
|
||||
"net/http"
|
||||
|
||||
"github.com/go-chi/chi/v5"
|
||||
)
|
||||
|
||||
type Handler struct {
|
||||
ListUseCase *useCases.ListBuiltinTypesUseCase
|
||||
GetUseCase *useCases.GetBuiltinTypeUseCase
|
||||
}
|
||||
|
||||
func NewHandler(list *useCases.ListBuiltinTypesUseCase, get *useCases.GetBuiltinTypeUseCase) *Handler {
|
||||
return &Handler{ListUseCase: list, GetUseCase: get}
|
||||
}
|
||||
|
||||
func (h *Handler) List(w http.ResponseWriter, r *http.Request) {
|
||||
response.WriteJSON(w, http.StatusOK, h.ListUseCase.List())
|
||||
}
|
||||
|
||||
func (h *Handler) Get(w http.ResponseWriter, r *http.Request) {
|
||||
found, err := h.GetUseCase.Get(chi.URLParam(r, "key"))
|
||||
if errors.Is(err, domain.ErrNotFound) {
|
||||
http.Error(w, apperr.BuiltinTypeNotFoundErrorMessage, http.StatusNotFound)
|
||||
return
|
||||
}
|
||||
if err != nil {
|
||||
http.Error(w, "Internal server error", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
response.WriteJSON(w, http.StatusOK, found)
|
||||
}
|
||||
@@ -26,15 +26,35 @@ func NewHandler(create *useCases.CreateContentTypeUseCase, list *useCases.ListCo
|
||||
return &Handler{CreateUseCase: create, ListUseCase: list, GetUseCase: get, UpdateUseCase: update, DeleteUseCase: deleteUseCase}
|
||||
}
|
||||
|
||||
type iconInput struct {
|
||||
value *string
|
||||
invalid bool
|
||||
}
|
||||
|
||||
func (i *iconInput) UnmarshalJSON(data []byte) error {
|
||||
if string(data) == "null" {
|
||||
return nil
|
||||
}
|
||||
var text string
|
||||
if err := json.Unmarshal(data, &text); err != nil {
|
||||
i.invalid = true
|
||||
return nil
|
||||
}
|
||||
i.value = &text
|
||||
return nil
|
||||
}
|
||||
|
||||
type PostContentType struct {
|
||||
Key string `json:"key" validate:"required"`
|
||||
Name string `json:"name" validate:"required,max=100"`
|
||||
Icon iconInput `json:"icon"`
|
||||
Fields []domain.Field `json:"fields"`
|
||||
}
|
||||
|
||||
type PutContentType struct {
|
||||
Key string `json:"key"`
|
||||
Name string `json:"name" validate:"required,max=100"`
|
||||
Icon iconInput `json:"icon"`
|
||||
Fields []domain.Field `json:"fields"`
|
||||
}
|
||||
|
||||
@@ -42,23 +62,22 @@ type contentTypeResponse struct {
|
||||
ID uint32 `json:"id"`
|
||||
Key string `json:"key"`
|
||||
Name string `json:"name"`
|
||||
Icon *string `json:"icon"`
|
||||
Fields []domain.Field `json:"fields"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
}
|
||||
|
||||
func toResponse(contentType domain.ContentType) contentTypeResponse {
|
||||
fields := make([]domain.Field, 0, len(contentType.Fields))
|
||||
for _, field := range contentType.Fields {
|
||||
if field.Options == nil {
|
||||
field.Options = []string{}
|
||||
}
|
||||
fields = append(fields, field)
|
||||
fields := contentType.Fields
|
||||
if fields == nil {
|
||||
fields = []domain.Field{}
|
||||
}
|
||||
return contentTypeResponse{
|
||||
ID: contentType.ID,
|
||||
Key: contentType.Key,
|
||||
Name: contentType.Name,
|
||||
Icon: contentType.Icon,
|
||||
Fields: fields,
|
||||
CreatedAt: contentType.CreatedAt,
|
||||
UpdatedAt: contentType.UpdatedAt,
|
||||
@@ -77,6 +96,8 @@ func writeContentTypeError(w http.ResponseWriter, err error) {
|
||||
http.Error(w, apperr.SchemaIncompatibleErrorMessage, http.StatusConflict)
|
||||
case errors.Is(err, domain.ErrInvalidName):
|
||||
http.Error(w, apperr.BodyErrorMessage, http.StatusBadRequest)
|
||||
case errors.Is(err, domain.ErrInvalidIcon):
|
||||
http.Error(w, apperr.InvalidIconErrorMessage, http.StatusBadRequest)
|
||||
case errors.Is(err, domain.ErrInvalidKey):
|
||||
http.Error(w, apperr.InvalidContentTypeKeyErrorMessage, http.StatusBadRequest)
|
||||
case errors.Is(err, domain.ErrInvalidField):
|
||||
@@ -118,7 +139,11 @@ func (h *Handler) Create(w http.ResponseWriter, r *http.Request) {
|
||||
http.Error(w, err.Error(), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
created, err := h.CreateUseCase.Create(useCases.CreateContentTypeCommand{Key: request.Key, Name: request.Name, Fields: request.Fields})
|
||||
if request.Icon.invalid {
|
||||
writeContentTypeError(w, domain.ErrInvalidIcon)
|
||||
return
|
||||
}
|
||||
created, err := h.CreateUseCase.Create(useCases.CreateContentTypeCommand{Key: request.Key, Name: request.Name, Icon: request.Icon.value, Fields: request.Fields})
|
||||
if err != nil {
|
||||
writeContentTypeError(w, err)
|
||||
return
|
||||
@@ -136,7 +161,11 @@ func (h *Handler) Update(w http.ResponseWriter, r *http.Request) {
|
||||
http.Error(w, err.Error(), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
updated, err := h.UpdateUseCase.Update(useCases.UpdateContentTypeCommand{Key: chi.URLParam(r, "key"), Name: request.Name, Fields: request.Fields})
|
||||
if request.Icon.invalid {
|
||||
writeContentTypeError(w, domain.ErrInvalidIcon)
|
||||
return
|
||||
}
|
||||
updated, err := h.UpdateUseCase.Update(useCases.UpdateContentTypeCommand{Key: chi.URLParam(r, "key"), Name: request.Name, Icon: request.Icon.value, Fields: request.Fields})
|
||||
if err != nil {
|
||||
writeContentTypeError(w, err)
|
||||
return
|
||||
|
||||
@@ -0,0 +1,66 @@
|
||||
package user
|
||||
|
||||
import (
|
||||
"RenewCMS/api/pkg/apperr"
|
||||
"RenewCMS/api/pkg/response"
|
||||
domainUser "RenewCMS/internal/domain/user"
|
||||
"RenewCMS/internal/infrastructure/useCases"
|
||||
"errors"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type LookupHandler struct {
|
||||
LookupUseCase *useCases.LookupUserNamesUseCase
|
||||
}
|
||||
|
||||
type userNameResponse struct {
|
||||
ID uint32 `json:"id"`
|
||||
DisplayName string `json:"display_name"`
|
||||
}
|
||||
|
||||
func NewLookupHandler(lookup *useCases.LookupUserNamesUseCase) *LookupHandler {
|
||||
return &LookupHandler{LookupUseCase: lookup}
|
||||
}
|
||||
|
||||
func parseLookupIDs(raw string) ([]uint32, bool) {
|
||||
if raw == "" {
|
||||
return nil, false
|
||||
}
|
||||
if strings.Count(raw, ",")+1 > domainUser.MaxLookupIDs {
|
||||
return nil, false
|
||||
}
|
||||
parts := strings.Split(raw, ",")
|
||||
ids := make([]uint32, 0, len(parts))
|
||||
for _, part := range parts {
|
||||
id, err := strconv.ParseUint(part, 10, 32)
|
||||
if err != nil {
|
||||
return nil, false
|
||||
}
|
||||
ids = append(ids, uint32(id))
|
||||
}
|
||||
return ids, true
|
||||
}
|
||||
|
||||
func (h *LookupHandler) Lookup(w http.ResponseWriter, r *http.Request) {
|
||||
ids, ok := parseLookupIDs(r.URL.Query().Get("ids"))
|
||||
if !ok {
|
||||
http.Error(w, apperr.BodyErrorMessage, http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
names, err := h.LookupUseCase.Lookup(ids)
|
||||
if errors.Is(err, domainUser.ErrInvalidLookup) {
|
||||
http.Error(w, apperr.BodyErrorMessage, http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
if err != nil {
|
||||
http.Error(w, "Internal server error", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
items := make([]userNameResponse, 0, len(names))
|
||||
for _, name := range names {
|
||||
items = append(items, userNameResponse{ID: name.ID, DisplayName: name.DisplayName})
|
||||
}
|
||||
response.WriteJSON(w, http.StatusOK, items)
|
||||
}
|
||||
Reference in new issue
Block a user