Skip to content
This repository was archived by the owner on Nov 24, 2025. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
92 changes: 71 additions & 21 deletions traffic_ops/traffic_ops_golang/api/shared_handlers.go
Original file line number Diff line number Diff line change
Expand Up @@ -114,8 +114,8 @@ func decodeAndValidateRequestBody(r *http.Request, v Validator) error {
return v.Validate()
}

func hasDeleteKeyOption(obj interface{}, params map[string]string) (bool, error, error, int) {
optionsDeleter, ok := obj.(HasDeleteKeyOptions)
func checkIfOptionsDeleter(obj interface{}, params map[string]string) (bool, error, error, int) {
optionsDeleter, ok := obj.(OptionsDeleter)
if !ok {
return false, nil, nil, http.StatusOK
}
Expand Down Expand Up @@ -310,26 +310,52 @@ func DeleteHandler(deleter Deleter) http.HandlerFunc {
obj := reflect.New(objectType).Interface().(Deleter)
obj.SetInfo(inf)

deleteKeyOptionExists, userErr, sysErr, errCode := hasDeleteKeyOption(obj, inf.Params)
isOptionsDeleter, userErr, sysErr, errCode := checkIfOptionsDeleter(obj, inf.Params)
if userErr != nil || sysErr != nil {
HandleErr(w, r, inf.Tx.Tx, errCode, userErr, sysErr)
return
}
keyFields := obj.GetKeyFieldsInfo() // expecting a slice of the key fields info which is a struct with the field name and a function to convert a string into a interface{} of the right type. in most that will be [{Field:"id",Func: func(s string)(interface{},error){return strconv.Atoi(s)}}]
keys := make(map[string]interface{})
for _, kf := range keyFields {
paramKey := inf.Params[kf.Field]
if paramKey == "" {
HandleErr(w, r, inf.Tx.Tx, http.StatusBadRequest, errors.New("missing key: "+kf.Field), nil)
return
var (
keys = make(map[string]interface{})
err error
)
if isOptionsDeleter {
for key, info := range obj.(OptionsDeleter).DeleteKeyOptions() {
paramKey := inf.Params[key]
if paramKey == "" {
continue
}
switch reflect.ValueOf(info.Checker) {
case reflect.ValueOf(IsInt):
if keys[key], err = GetIntKey(paramKey); err != nil {
HandleErr(w, r, inf.Tx.Tx, http.StatusBadRequest, errors.New("failed to parse key: "+key), nil)
return
}
case reflect.ValueOf(IsBool):
if keys[key], err = strconv.ParseBool(paramKey); err != nil {
HandleErr(w, r, inf.Tx.Tx, http.StatusBadRequest, errors.New("failed to parse key: "+key), nil)
return
}
default:
keys[key] = paramKey
}
}
} else {
keyFields := obj.GetKeyFieldsInfo() // expecting a slice of the key fields info which is a struct with the field name and a function to convert a string into a interface{} of the right type. in most that will be [{Field:"id",Func: func(s string)(interface{},error){return strconv.Atoi(s)}}]
for _, kf := range keyFields {
paramKey := inf.Params[kf.Field]
if paramKey == "" {
HandleErr(w, r, inf.Tx.Tx, http.StatusBadRequest, errors.New("missing key: "+kf.Field), nil)
return
}

paramValue, err := kf.Func(paramKey)
if err != nil {
HandleErr(w, r, inf.Tx.Tx, http.StatusBadRequest, errors.New("failed to parse key: "+kf.Field), nil)
return
paramValue, err := kf.Func(paramKey)
if err != nil {
HandleErr(w, r, inf.Tx.Tx, http.StatusBadRequest, errors.New("failed to parse key: "+kf.Field), nil)
return
}
keys[kf.Field] = paramValue
}
keys[kf.Field] = paramValue
}
obj.SetKeys(keys) // if the type assertion of a key fails it will be should be set to the zero value of the type and the delete should fail (this means the code is not written properly no changes of user input should cause this.)

Expand All @@ -345,7 +371,7 @@ func DeleteHandler(deleter Deleter) http.HandlerFunc {
}
}

if deleteKeyOptionExists {
if isOptionsDeleter {
obj := reflect.New(objectType).Interface().(OptionsDeleter)
obj.SetInfo(inf)
userErr, sysErr, errCode = obj.OptionsDelete()
Expand Down Expand Up @@ -390,14 +416,38 @@ func DeprecatedDeleteHandler(deleter Deleter, alternative *string) http.HandlerF
obj := reflect.New(objectType).Interface().(Deleter)
obj.SetInfo(inf)

deleteKeyOptionExists, userErr, sysErr, errCode := hasDeleteKeyOption(obj, inf.Params)
isOptionsDeleter, userErr, sysErr, errCode := checkIfOptionsDeleter(obj, inf.Params)
if userErr != nil || sysErr != nil {
HandleDeprecatedErr(w, r, inf.Tx.Tx, errCode, userErr, sysErr, alternative)
return
}
if !deleteKeyOptionExists {
var (
keys = make(map[string]interface{})
err error
)
if isOptionsDeleter {
for key, info := range obj.(OptionsDeleter).DeleteKeyOptions() {
paramKey := inf.Params[key]
if paramKey == "" {
continue
}
switch reflect.ValueOf(info.Checker) {
case reflect.ValueOf(IsInt):
if keys[key], err = GetIntKey(paramKey); err != nil {
HandleDeprecatedErr(w, r, inf.Tx.Tx, http.StatusBadRequest, errors.New("missing key: "+key), nil, alternative)
return
}
case reflect.ValueOf(IsBool):
if keys[key], err = strconv.ParseBool(paramKey); err != nil {
HandleDeprecatedErr(w, r, inf.Tx.Tx, http.StatusBadRequest, errors.New("failed to parse key: "+key), nil, alternative)
return
}
default:
keys[key] = paramKey
}
}
} else {
keyFields := obj.GetKeyFieldsInfo() // expecting a slice of the key fields info which is a struct with the field name and a function to convert a string into a interface{} of the right type. in most that will be [{Field:"id",Func: func(s string)(interface{},error){return strconv.Atoi(s)}}]
keys := make(map[string]interface{})
for _, kf := range keyFields {
paramKey := inf.Params[kf.Field]
if paramKey == "" {
Expand All @@ -412,8 +462,8 @@ func DeprecatedDeleteHandler(deleter Deleter, alternative *string) http.HandlerF
}
keys[kf.Field] = paramValue
}
obj.SetKeys(keys) // if the type assertion of a key fails it will be should be set to the zero value of the type and the delete should fail (this means the code is not written properly no changes of user input should cause this.)
}
obj.SetKeys(keys) // if the type assertion of a key fails it will be should be set to the zero value of the type and the delete should fail (this means the code is not written properly no changes of user input should cause this.)

if t, ok := obj.(Tenantable); ok {
authorized, err := t.IsTenantAuthorized(inf.User)
Expand All @@ -427,7 +477,7 @@ func DeprecatedDeleteHandler(deleter Deleter, alternative *string) http.HandlerF
}
}

if deleteKeyOptionExists {
if isOptionsDeleter {
obj := reflect.New(objectType).Interface().(OptionsDeleter)
obj.SetInfo(inf)
userErr, sysErr, errCode = obj.OptionsDelete()
Expand Down
5 changes: 1 addition & 4 deletions traffic_ops/traffic_ops_golang/api/shared_interfaces.go
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ type OptionsDeleter interface {
OptionsDelete() (error, error, int)
APIInfoer
Identifier
DeleteKeyOptions() map[string]dbhelpers.WhereColumnInfo
}

type Validator interface {
Expand All @@ -102,10 +103,6 @@ type Tenantable interface {
IsTenantAuthorized(user *auth.CurrentUser) (bool, error)
}

type HasDeleteKeyOptions interface {
DeleteKeyOptions() map[string]dbhelpers.WhereColumnInfo
}

// APIInfoer is an interface that guarantees the existance of a variable through its setters and getters.
// Every CRUD operation uses this login session context
type APIInfoer interface {
Expand Down
9 changes: 7 additions & 2 deletions traffic_ops/traffic_ops_golang/region/regions.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,8 +70,13 @@ func (region TORegion) DeleteKeyOptions() map[string]dbhelpers.WhereColumnInfo {
}

func (region *TORegion) SetKeys(keys map[string]interface{}) {
i, _ := keys["id"].(int) //this utilizes the non panicking type assertion, if the thrown away ok variable is false i will be the zero of the type, 0 here.
region.ID = i
//this utilizes the non panicking type assertion, if the thrown away ok variable is false i will be the zero of the type, 0 here.
if id, exists := keys["id"].(int); exists {
region.ID = id
}
if name, exists := keys["name"].(string); exists {
region.Name = name
}
}

func (region *TORegion) GetAuditName() string {
Expand Down