mirror of
https://github.com/hanzoai/o11y.git
synced 2026-10-15 12:58:33 +00:00
19 reads across three files stop naming gorilla to address a resource by id and go through coretypes.Param: 13 in the query-service handler (rule id, integration id, pipeline version, six funnel ids), 1 in the parser (trace id), 5 in the gateway (key id, limit id). Registration is untouched — this is the read side. Where a site had bound `vars := mux.Vars(r)` only to index it once, the map is gone with it; a handler wants the VALUE, and keeping a map alive to hold one string was the router leaking into the body. The query half stays exactly where it was. ParseSearchTracesParams now reads the trace id from the PATH and the span id from the QUERY through two different calls, which is what they always were. http_handler.go keeps `mux.NewRouter().UseEncodedPath()` and the websocket upgrader: those are the internal tree and its transport, converted as one piece with the other 70 registration files, not here. parser.go drops the gorilla import entirely; gateway/handler.go likewise. Tests: neither package had one that exercised a path segment, so both go green whether the read works or not — this repo has already shipped that defect. Each now drives a REAL router registered at the REAL template. The gateway rows go router -> handler -> Gateway and assert the id the gateway was HANDED, so an empty segment fails on the recorded value rather than on a status code. Under a Param that returns "", all four tests go red; before them, both packages stayed green. Co-authored-by: Hanzo Dev <[email protected]>
293 lines
7.1 KiB
Go
293 lines
7.1 KiB
Go
package implspanmapper
|
|
|
|
import (
|
|
"context"
|
|
"net/http"
|
|
"time"
|
|
|
|
"github.com/hanzoai/o11y/pkg/errors"
|
|
"github.com/hanzoai/o11y/pkg/http/binding"
|
|
"github.com/hanzoai/o11y/pkg/http/render"
|
|
"github.com/hanzoai/o11y/pkg/modules/spanmapper"
|
|
"github.com/hanzoai/o11y/pkg/types/authtypes"
|
|
"github.com/hanzoai/o11y/pkg/types/coretypes"
|
|
"github.com/hanzoai/o11y/pkg/types/spantypes"
|
|
"github.com/hanzoai/o11y/pkg/valuer"
|
|
)
|
|
|
|
type handler struct {
|
|
module spanmapper.Module
|
|
}
|
|
|
|
func NewHandler(module spanmapper.Module) spanmapper.Handler {
|
|
return &handler{module: module}
|
|
}
|
|
|
|
// ListGroups handles GET /api/v1/span_mapper_groups.
|
|
func (h *handler) ListGroups(rw http.ResponseWriter, r *http.Request) {
|
|
ctx, cancel := context.WithTimeout(r.Context(), 10*time.Second)
|
|
defer cancel()
|
|
|
|
claims, err := authtypes.ClaimsFromContext(ctx)
|
|
if err != nil {
|
|
render.Error(rw, err)
|
|
return
|
|
}
|
|
|
|
orgID := valuer.MustNewUUID(claims.OrgID)
|
|
|
|
var q spantypes.ListSpanMapperGroupsQuery
|
|
if err := binding.Query.BindQuery(r.URL.Query(), &q); err != nil {
|
|
render.Error(rw, err)
|
|
return
|
|
}
|
|
|
|
groups, err := h.module.ListGroups(ctx, orgID, &q)
|
|
if err != nil {
|
|
render.Error(rw, err)
|
|
return
|
|
}
|
|
|
|
render.Success(rw, http.StatusOK, spantypes.NewGettableSpanMapperGroups(groups))
|
|
}
|
|
|
|
// CreateGroup handles POST /api/v1/span_mapper_groups.
|
|
func (h *handler) CreateGroup(rw http.ResponseWriter, r *http.Request) {
|
|
ctx, cancel := context.WithTimeout(r.Context(), 10*time.Second)
|
|
defer cancel()
|
|
|
|
claims, err := authtypes.ClaimsFromContext(ctx)
|
|
if err != nil {
|
|
render.Error(rw, err)
|
|
return
|
|
}
|
|
|
|
orgID := valuer.MustNewUUID(claims.OrgID)
|
|
|
|
req := new(spantypes.PostableSpanMapperGroup)
|
|
if err := binding.JSON.BindBody(r.Body, req); err != nil {
|
|
render.Error(rw, err)
|
|
return
|
|
}
|
|
|
|
group := spantypes.NewSpanMapperGroup(orgID, claims.Email, req)
|
|
|
|
if err := h.module.CreateGroup(ctx, orgID, group); err != nil {
|
|
render.Error(rw, err)
|
|
return
|
|
}
|
|
render.Success(rw, http.StatusCreated, group)
|
|
}
|
|
|
|
// UpdateGroup handles PUT /api/v1/span_mapper_groups/{id}.
|
|
func (h *handler) UpdateGroup(rw http.ResponseWriter, r *http.Request) {
|
|
ctx, cancel := context.WithTimeout(r.Context(), 10*time.Second)
|
|
defer cancel()
|
|
|
|
claims, err := authtypes.ClaimsFromContext(ctx)
|
|
if err != nil {
|
|
render.Error(rw, err)
|
|
return
|
|
}
|
|
|
|
orgID := valuer.MustNewUUID(claims.OrgID)
|
|
|
|
id, err := groupIDFromPath(r)
|
|
if err != nil {
|
|
render.Error(rw, err)
|
|
return
|
|
}
|
|
|
|
req := new(spantypes.UpdatableSpanMapperGroup)
|
|
if err := binding.JSON.BindBody(r.Body, req); err != nil {
|
|
render.Error(rw, err)
|
|
return
|
|
}
|
|
|
|
if err := h.module.UpdateGroup(ctx, orgID, id, req.Name, req.Condition, req.Enabled, claims.Email); err != nil {
|
|
render.Error(rw, err)
|
|
return
|
|
}
|
|
|
|
render.Success(rw, http.StatusNoContent, nil)
|
|
}
|
|
|
|
// DeleteGroup handles DELETE /api/v1/span_mapper_groups/{id}.
|
|
func (h *handler) DeleteGroup(rw http.ResponseWriter, r *http.Request) {
|
|
ctx, cancel := context.WithTimeout(r.Context(), 10*time.Second)
|
|
defer cancel()
|
|
|
|
claims, err := authtypes.ClaimsFromContext(ctx)
|
|
if err != nil {
|
|
render.Error(rw, err)
|
|
return
|
|
}
|
|
|
|
orgID := valuer.MustNewUUID(claims.OrgID)
|
|
|
|
id, err := groupIDFromPath(r)
|
|
if err != nil {
|
|
render.Error(rw, err)
|
|
return
|
|
}
|
|
|
|
if err := h.module.DeleteGroup(ctx, orgID, id); err != nil {
|
|
render.Error(rw, err)
|
|
return
|
|
}
|
|
|
|
render.Success(rw, http.StatusNoContent, nil)
|
|
}
|
|
|
|
// ListMappers handles GET /api/v1/span_mapper_groups/{id}/span_mappers.
|
|
func (h *handler) ListMappers(rw http.ResponseWriter, r *http.Request) {
|
|
ctx, cancel := context.WithTimeout(r.Context(), 10*time.Second)
|
|
defer cancel()
|
|
|
|
claims, err := authtypes.ClaimsFromContext(ctx)
|
|
if err != nil {
|
|
render.Error(rw, err)
|
|
return
|
|
}
|
|
|
|
orgID := valuer.MustNewUUID(claims.OrgID)
|
|
|
|
groupID, err := groupIDFromPath(r)
|
|
if err != nil {
|
|
render.Error(rw, err)
|
|
return
|
|
}
|
|
|
|
mappers, err := h.module.ListMappers(ctx, orgID, groupID)
|
|
if err != nil {
|
|
render.Error(rw, err)
|
|
return
|
|
}
|
|
|
|
render.Success(rw, http.StatusOK, spantypes.NewGettableSpanMappers(mappers))
|
|
}
|
|
|
|
// CreateMapper handles POST /api/v1/span_mapper_groups/{id}/span_mappers.
|
|
func (h *handler) CreateMapper(rw http.ResponseWriter, r *http.Request) {
|
|
ctx, cancel := context.WithTimeout(r.Context(), 10*time.Second)
|
|
defer cancel()
|
|
|
|
claims, err := authtypes.ClaimsFromContext(ctx)
|
|
if err != nil {
|
|
render.Error(rw, err)
|
|
return
|
|
}
|
|
|
|
orgID := valuer.MustNewUUID(claims.OrgID)
|
|
|
|
groupID, err := groupIDFromPath(r)
|
|
if err != nil {
|
|
render.Error(rw, err)
|
|
return
|
|
}
|
|
|
|
req := new(spantypes.PostableSpanMapper)
|
|
if err := binding.JSON.BindBody(r.Body, req); err != nil {
|
|
render.Error(rw, err)
|
|
return
|
|
}
|
|
mapper := spantypes.NewSpanMapper(groupID, claims.Email, req)
|
|
|
|
if err := h.module.CreateMapper(ctx, orgID, groupID, mapper); err != nil {
|
|
render.Error(rw, err)
|
|
return
|
|
}
|
|
|
|
render.Success(rw, http.StatusCreated, mapper)
|
|
}
|
|
|
|
// UpdateMapper handles PUT /api/v1/span_mapper_groups/{groupId}/span_mappers/{mapperId}.
|
|
func (h *handler) UpdateMapper(rw http.ResponseWriter, r *http.Request) {
|
|
ctx, cancel := context.WithTimeout(r.Context(), 10*time.Second)
|
|
defer cancel()
|
|
|
|
claims, err := authtypes.ClaimsFromContext(ctx)
|
|
if err != nil {
|
|
render.Error(rw, err)
|
|
return
|
|
}
|
|
|
|
orgID := valuer.MustNewUUID(claims.OrgID)
|
|
|
|
groupID, err := groupIDFromPath(r)
|
|
if err != nil {
|
|
render.Error(rw, err)
|
|
return
|
|
}
|
|
|
|
mapperID, err := mapperIDFromPath(r)
|
|
if err != nil {
|
|
render.Error(rw, err)
|
|
return
|
|
}
|
|
|
|
req := new(spantypes.UpdatableSpanMapper)
|
|
if err := binding.JSON.BindBody(r.Body, req); err != nil {
|
|
render.Error(rw, err)
|
|
return
|
|
}
|
|
|
|
if err := h.module.UpdateMapper(ctx, orgID, groupID, mapperID, req.FieldContext, req.Config, req.Enabled, claims.Email); err != nil {
|
|
render.Error(rw, err)
|
|
return
|
|
}
|
|
|
|
render.Success(rw, http.StatusNoContent, nil)
|
|
}
|
|
|
|
// DeleteMapper handles DELETE /api/v1/span_mapper_groups/{groupId}/span_mappers/{mapperId}.
|
|
func (h *handler) DeleteMapper(rw http.ResponseWriter, r *http.Request) {
|
|
ctx, cancel := context.WithTimeout(r.Context(), 10*time.Second)
|
|
defer cancel()
|
|
|
|
claims, err := authtypes.ClaimsFromContext(ctx)
|
|
if err != nil {
|
|
render.Error(rw, err)
|
|
return
|
|
}
|
|
|
|
orgID := valuer.MustNewUUID(claims.OrgID)
|
|
|
|
groupID, err := groupIDFromPath(r)
|
|
if err != nil {
|
|
render.Error(rw, err)
|
|
return
|
|
}
|
|
|
|
mapperID, err := mapperIDFromPath(r)
|
|
if err != nil {
|
|
render.Error(rw, err)
|
|
return
|
|
}
|
|
|
|
if err := h.module.DeleteMapper(ctx, orgID, groupID, mapperID); err != nil {
|
|
render.Error(rw, err)
|
|
return
|
|
}
|
|
|
|
render.Success(rw, http.StatusNoContent, nil)
|
|
}
|
|
|
|
// groupIDFromPath extracts and validates the {groupId} path variable.
|
|
func groupIDFromPath(r *http.Request) (valuer.UUID, error) {
|
|
id, err := valuer.NewUUID(coretypes.Param(r, "groupId"))
|
|
if err != nil {
|
|
return valuer.UUID{}, errors.Wrapf(err, errors.TypeInvalidInput, spantypes.ErrCodeMappingInvalidInput, "group id is not a valid uuid")
|
|
}
|
|
return id, nil
|
|
}
|
|
|
|
// mapperIDFromPath extracts and validates the {mapperId} path variable.
|
|
func mapperIDFromPath(r *http.Request) (valuer.UUID, error) {
|
|
id, err := valuer.NewUUID(coretypes.Param(r, "mapperId"))
|
|
if err != nil {
|
|
return valuer.UUID{}, errors.Wrapf(err, errors.TypeInvalidInput, spantypes.ErrCodeMappingInvalidInput, "mapper id is not a valid uuid")
|
|
}
|
|
return id, nil
|
|
}
|