dewrich closed pull request #2067: support compound keys in generic crud
URL: https://github.com/apache/incubator-trafficcontrol/pull/2067
 
 
   

This is a PR merged from a forked repository.
As GitHub hides the original diff on merge, it is displayed below for
the sake of provenance:

As this is a foreign pull request (from a fork), the diff is supplied
below (as it won't show otherwise due to GitHub magic):

diff --git a/traffic_ops/traffic_ops_golang/api/change_log.go 
b/traffic_ops/traffic_ops_golang/api/change_log.go
index 579d0632c..17c54a784 100644
--- a/traffic_ops/traffic_ops_golang/api/change_log.go
+++ b/traffic_ops/traffic_ops_golang/api/change_log.go
@@ -20,7 +20,7 @@ package api
  */
 
 import (
-       "strconv"
+       "fmt"
 
        "github.com/apache/incubator-trafficcontrol/lib/go-log"
        "github.com/apache/incubator-trafficcontrol/lib/go-tc"
@@ -49,8 +49,13 @@ const (
 )
 
 func CreateChangeLog(level string, action string, i Identifier, user 
auth.CurrentUser, db *sqlx.DB) error {
-       id, _ := i.GetID()
-       message := action + " " + i.GetType() + ": " + i.GetAuditName() + " id: 
" + strconv.Itoa(id)
+       keys, _ := i.GetKeys()
+       keysString := "{ "
+       for key, value := range keys {
+               keysString += key + ":" + fmt.Sprintf("%v", value) + " "
+       }
+       keysString += "}"
+       message := action + " " + i.GetType() + ": " + i.GetAuditName() + " 
keys: " + keysString
        // if the object has its own log message generation, use it
        if t, ok := i.(ChangeLogger); ok {
                m, err := t.ChangeLogMessage(action)
diff --git a/traffic_ops/traffic_ops_golang/api/change_log_test.go 
b/traffic_ops/traffic_ops_golang/api/change_log_test.go
index bfbcab2ba..cfd923049 100644
--- a/traffic_ops/traffic_ops_golang/api/change_log_test.go
+++ b/traffic_ops/traffic_ops_golang/api/change_log_test.go
@@ -32,8 +32,12 @@ import (
 type testIdentifier struct {
 }
 
-func (i *testIdentifier) GetID() (int, bool) {
-       return 1, true
+func (i testIdentifier) GetKeyFieldsInfo() []KeyFieldInfo {
+       return []KeyFieldInfo{{"id", GetIntKey}}
+}
+
+func (i *testIdentifier) GetKeys() (map[string]interface{}, bool) {
+       return map[string]interface{}{"id": 1}, true
 }
 
 func (i *testIdentifier) GetType() string {
@@ -55,8 +59,8 @@ func TestCreateChangeLog(t *testing.T) {
        defer db.Close()
        i := testIdentifier{}
 
-       id, _ := i.GetID()
-       expectedMessage := Created + " " + i.GetType() + ": " + 
i.GetAuditName() + " id: " + strconv.Itoa(id)
+       keys, _ := i.GetKeys()
+       expectedMessage := Created + " " + i.GetType() + ": " + 
i.GetAuditName() + " keys: { id:" + strconv.Itoa(keys["id"].(int)) + " }"
 
        mock.ExpectExec("INSERT").WithArgs(ApiChange, expectedMessage, 
1).WillReturnResult(sqlmock.NewResult(1, 1))
        user := auth.CurrentUser{ID: 1}
diff --git a/traffic_ops/traffic_ops_golang/api/shared_handlers.go 
b/traffic_ops/traffic_ops_golang/api/shared_handlers.go
index f1edb0da4..865245581 100644
--- a/traffic_ops/traffic_ops_golang/api/shared_handlers.go
+++ b/traffic_ops/traffic_ops_golang/api/shared_handlers.go
@@ -37,6 +37,19 @@ import (
 
 const PathParamsKey = "pathParams"
 
+type KeyFieldInfo struct {
+       Field string
+       Func  func(string) (interface{}, error)
+}
+
+func GetIntKey(s string) (interface{}, error) {
+       return strconv.Atoi(s)
+}
+
+func GetStringKey(s string) (interface{}, error) {
+       return s, nil
+}
+
 func GetPathParams(ctx context.Context) (map[string]string, error) {
        val := ctx.Value(PathParamsKey)
        if val != nil {
@@ -175,7 +188,7 @@ func UpdateHandler(typeRef Updater, db *sqlx.DB) 
http.HandlerFunc {
 
                //collect path parameters and user from context
                ctx := r.Context()
-               pathParams, err := GetPathParams(ctx)
+               params, err := GetCombinedParams(r)
                if err != nil {
                        log.Errorf("received error trying to get path 
parameters: %s", err)
                        handleErrs(http.StatusInternalServerError, err)
@@ -187,17 +200,31 @@ func UpdateHandler(typeRef Updater, db *sqlx.DB) 
http.HandlerFunc {
                        handleErrs(http.StatusInternalServerError, err)
                        return
                }
-               id, err := strconv.Atoi(pathParams["id"])
-               if err != nil {
-                       log.Errorf("received error trying to convert id path 
parameter: %s", err)
-                       handleErrs(http.StatusBadRequest, errors.New("id from 
path not parseable as int"))
-                       return
+
+               keyFields := u.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, ok := u.GetKeys()           // a map of keyField to 
keyValue where keyValue is an {}interface
+               if !ok {
+                       log.Errorf("unable to parse keys from request: %++v", u)
+                       handleErrs(http.StatusBadRequest, errors.New("unable to 
parse required keys from request body"))
                }
+               for _, keyFieldInfo := range keyFields {
+                       paramKey := params[keyFieldInfo.Field]
+                       if paramKey == "" {
+                               log.Errorf("missing key: %s", 
keyFieldInfo.Field)
+                               handleErrs(http.StatusBadRequest, 
errors.New("missing key: "+keyFieldInfo.Field))
+                               return
+                       }
 
-               iid, ok := u.GetID()
-               if !ok || iid != id {
-                       handleErrs(http.StatusBadRequest, errors.New("id in 
body does not match id in path"))
-                       return
+                       paramValue, err := keyFieldInfo.Func(paramKey)
+                       if err != nil {
+                               log.Errorf("failed to parse key %s: %s", 
keyFieldInfo.Field, err)
+                               handleErrs(http.StatusBadRequest, 
errors.New("failed to parse key: "+keyFieldInfo.Field))
+                       }
+
+                       if paramValue != keys[keyFieldInfo.Field] {
+                               handleErrs(http.StatusBadRequest, 
errors.New("key in body does not match key in params"))
+                               return
+                       }
                }
 
                // if the object has tenancy enabled, check that user is able 
to access the tenant
@@ -252,7 +279,7 @@ func DeleteHandler(typeRef Deleter, db *sqlx.DB) 
http.HandlerFunc {
                d := typeRef
 
                ctx := r.Context()
-               pathParams, err := GetPathParams(ctx)
+               params, err := GetCombinedParams(r)
                if err != nil {
                        handleErrs(http.StatusInternalServerError, err)
                        return
@@ -264,12 +291,24 @@ func DeleteHandler(typeRef Deleter, db *sqlx.DB) 
http.HandlerFunc {
                        return
                }
 
-               id, err := strconv.Atoi(pathParams["id"])
-               if err != nil {
-                       handleErrs(http.StatusBadRequest, errors.New("id from 
path not parseable as int"))
-                       return
+               keyFields := d.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 _, keyFieldInfo := range keyFields {
+                       paramKey := params[keyFieldInfo.Field]
+                       if paramKey == "" {
+                               log.Errorf("missing key: %s", 
keyFieldInfo.Field)
+                               handleErrs(http.StatusBadRequest, 
errors.New("missing key: "+keyFieldInfo.Field))
+                               return
+                       }
+
+                       paramValue, err := keyFieldInfo.Func(paramKey)
+                       if err != nil {
+                               log.Errorf("failed to parse key %s: %s", 
keyFieldInfo.Field, err)
+                               handleErrs(http.StatusBadRequest, 
errors.New("failed to parse key: "+keyFieldInfo.Field))
+                       }
+                       keys[keyFieldInfo.Field] = paramValue
                }
-               d.SetID(id)
+               d.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 the object has tenancy enabled, check that user is able 
to access the tenant
                if t, ok := d.(Tenantable); ok {
diff --git a/traffic_ops/traffic_ops_golang/api/shared_handlers_test.go 
b/traffic_ops/traffic_ops_golang/api/shared_handlers_test.go
index 50ee9edde..21b8d21f5 100644
--- a/traffic_ops/traffic_ops_golang/api/shared_handlers_test.go
+++ b/traffic_ops/traffic_ops_golang/api/shared_handlers_test.go
@@ -43,9 +43,18 @@ type tester struct {
 
 type emptyTester tester
 
-//Identifier interface functions
-func (i *tester) GetID() (int, bool) {
-       return i.ID, true
+func (i tester) GetKeyFieldsInfo() []KeyFieldInfo {
+       return []KeyFieldInfo{{"id", GetIntKey}}
+}
+
+//Implementation of the Identifier, Validator interface functions
+func (i tester) GetKeys() (map[string]interface{}, bool) {
+       return map[string]interface{}{"id": i.ID}, true
+}
+
+func (i *tester) SetKeys(keys map[string]interface{}) {
+       id, _ := 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.
+       i.ID = id
 }
 
 func (i *tester) GetType() string {
@@ -69,10 +78,6 @@ func (i *tester) Create(db *sqlx.DB, user auth.CurrentUser) 
(error, tc.ApiErrorT
        return i.error, i.errorType
 }
 
-func (i *tester) SetID(newID int) {
-       i.ID = newID
-}
-
 //Reader interface functions
 func (i *tester) Read(db *sqlx.DB, v map[string]string, user auth.CurrentUser) 
([]interface{}, []error, tc.ApiErrorType) {
        return []interface{}{tester{ID: 1}}, nil, tc.NoError
diff --git a/traffic_ops/traffic_ops_golang/api/shared_interfaces.go 
b/traffic_ops/traffic_ops_golang/api/shared_interfaces.go
index da23de554..5d4c20348 100644
--- a/traffic_ops/traffic_ops_golang/api/shared_interfaces.go
+++ b/traffic_ops/traffic_ops_golang/api/shared_interfaces.go
@@ -32,21 +32,22 @@ type Updater interface {
 }
 
 type Identifier interface {
-       GetID() (int, bool)
+       GetKeys() (map[string]interface{}, bool)
        GetType() string
        GetAuditName() string
+       GetKeyFieldsInfo() []KeyFieldInfo
 }
 
 type Creator interface {
        Create(db *sqlx.DB, user auth.CurrentUser) (error, tc.ApiErrorType)
-       SetID(int)
+       SetKeys(map[string]interface{})
        Identifier
        Validator
 }
 
 type Deleter interface {
        Delete(db *sqlx.DB, user auth.CurrentUser) (error, tc.ApiErrorType)
-       SetID(int)
+       SetKeys(map[string]interface{})
        Identifier
 }
 
diff --git a/traffic_ops/traffic_ops_golang/asn/asns.go 
b/traffic_ops/traffic_ops_golang/asn/asns.go
index 4833aad49..4a5db6707 100644
--- a/traffic_ops/traffic_ops_golang/asn/asns.go
+++ b/traffic_ops/traffic_ops_golang/asn/asns.go
@@ -48,30 +48,37 @@ func GetRefType() *TOASN {
        return &refType
 }
 
+func (asn TOASN) GetKeyFieldsInfo() []api.KeyFieldInfo {
+       return []api.KeyFieldInfo{{"id", api.GetIntKey}}
+}
+
 //Implementation of the Identifier, Validator interface functions
-func (asn TOASN) GetID() (int, bool) {
+func (asn TOASN) GetKeys() (map[string]interface{}, bool) {
        if asn.ID == nil {
-               return 0, false
+               return map[string]interface{}{"id": 0}, false
        }
-       return *asn.ID, true
+       return map[string]interface{}{"id": *asn.ID}, true
+}
+
+func (asn *TOASN) 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.
+       asn.ID = &i
 }
 
 func (asn TOASN) GetAuditName() string {
-       if asn.ASN == nil {
-               id, _ := asn.GetID()
-               return strconv.Itoa(id)
+       if asn.ASN != nil {
+               return strconv.Itoa(*asn.ASN)
        }
-       return strconv.Itoa(*asn.ASN)
+       if asn.ID != nil {
+               return strconv.Itoa(*asn.ID)
+       }
+       return "unknown"
 }
 
 func (asn TOASN) GetType() string {
        return "asn"
 }
 
-func (asn *TOASN) SetID(i int) {
-       asn.ID = &i
-}
-
 func (asn TOASN) Validate(db *sqlx.DB) []error {
        errs := validation.Errors{
                "asn":          validation.Validate(asn.ASN, validation.NotNil, 
validation.Min(0)),
@@ -138,7 +145,7 @@ func (asn *TOASN) Create(db *sqlx.DB, user 
auth.CurrentUser) (error, tc.ApiError
                log.Errorln(err)
                return tc.DBError, tc.SystemError
        }
-       asn.SetID(id)
+       asn.SetKeys(map[string]interface{}{"id": id})
        asn.LastUpdated = &lastUpdated
        err = tx.Commit()
        if err != nil {
diff --git a/traffic_ops/traffic_ops_golang/cdn/cdns.go 
b/traffic_ops/traffic_ops_golang/cdn/cdns.go
index c37e6e9ba..06c381aae 100644
--- a/traffic_ops/traffic_ops_golang/cdn/cdns.go
+++ b/traffic_ops/traffic_ops_golang/cdn/cdns.go
@@ -48,27 +48,34 @@ func GetRefType() *TOCDN {
        return &refType
 }
 
+func (cdn TOCDN) GetKeyFieldsInfo() []api.KeyFieldInfo {
+       return []api.KeyFieldInfo{{"id", api.GetIntKey}}
+}
+
 //Implementation of the Identifier, Validator interface functions
-func (cdn TOCDN) GetID() (int, bool) {
+func (cdn TOCDN) GetKeys() (map[string]interface{}, bool) {
        if cdn.ID == nil {
-               return 0, false
+               return map[string]interface{}{"id": 0}, false
        }
-       return *cdn.ID, true
+       return map[string]interface{}{"id": *cdn.ID}, true
 }
 
 func (cdn TOCDN) GetAuditName() string {
        if cdn.Name != nil {
                return *cdn.Name
        }
-       id, _ := cdn.GetID()
-       return strconv.Itoa(id)
+       if cdn.ID != nil {
+               return strconv.Itoa(*cdn.ID)
+       }
+       return "0"
 }
 
 func (cdn TOCDN) GetType() string {
        return "cdn"
 }
 
-func (cdn *TOCDN) SetID(i int) {
+func (cdn *TOCDN) 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.
        cdn.ID = &i
 }
 
@@ -163,7 +170,7 @@ func (cdn *TOCDN) Create(db *sqlx.DB, user 
auth.CurrentUser) (error, tc.ApiError
                log.Errorln(err)
                return tc.DBError, tc.SystemError
        }
-       cdn.SetID(id)
+       cdn.SetKeys(map[string]interface{}{"id": id})
        cdn.LastUpdated = &lastUpdated
        err = tx.Commit()
        if err != nil {
diff --git a/traffic_ops/traffic_ops_golang/deliveryservice/deliveryservices.go 
b/traffic_ops/traffic_ops_golang/deliveryservice/deliveryservices.go
index a969b21ae..a00075ad9 100644
--- a/traffic_ops/traffic_ops_golang/deliveryservice/deliveryservices.go
+++ b/traffic_ops/traffic_ops_golang/deliveryservice/deliveryservices.go
@@ -31,6 +31,7 @@ import (
        validation "github.com/go-ozzo/ozzo-validation"
        "github.com/go-ozzo/ozzo-validation/is"
 
+       
"github.com/apache/incubator-trafficcontrol/traffic_ops/traffic_ops_golang/api"
        
"github.com/apache/incubator-trafficcontrol/traffic_ops/traffic_ops_golang/auth"
        
"github.com/apache/incubator-trafficcontrol/traffic_ops/traffic_ops_golang/dbhelpers"
        
"github.com/apache/incubator-trafficcontrol/traffic_ops/traffic_ops_golang/tenant"
@@ -49,12 +50,21 @@ func GetRefType() *TODeliveryService {
        return &refType
 }
 
+func (ds TODeliveryService) GetKeyFieldsInfo() []api.KeyFieldInfo {
+       return []api.KeyFieldInfo{{"id", api.GetIntKey}}
+}
+
 //Implementation of the Identifier, Validator interface functions
-func (ds *TODeliveryService) GetID() (int, bool) {
+func (ds TODeliveryService) GetKeys() (map[string]interface{}, bool) {
        if ds.ID == nil {
-               return 0, false
+               return map[string]interface{}{"id": 0}, false
        }
-       return *ds.ID, true
+       return map[string]interface{}{"id": *ds.ID}, true
+}
+
+func (ds *TODeliveryService) 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.
+       ds.ID = &i
 }
 
 func (ds *TODeliveryService) GetAuditName() string {
@@ -68,10 +78,6 @@ func (ds *TODeliveryService) GetType() string {
        return "ds"
 }
 
-func (ds *TODeliveryService) SetID(i int) {
-       ds.ID = &i
-}
-
 func Validate(db *sqlx.DB, ds *tc.DeliveryServiceNullable) []error {
        if ds == nil {
                return []error{}
@@ -317,7 +323,7 @@ func (ds *TODeliveryService) Create(db *sqlx.DB, user 
auth.CurrentUser) (error,
                log.Errorln(err)
                return tc.DBError, tc.SystemError
        }
-       ds.SetID(id)
+       ds.SetKeys(map[string]interface{}{"id": id})
        ds.LastUpdated = &lastUpdated
        return nil, tc.NoError
 }
diff --git 
a/traffic_ops/traffic_ops_golang/deliveryservice/request/comment/comments.go 
b/traffic_ops/traffic_ops_golang/deliveryservice/request/comment/comments.go
index c83bfbfa3..1127ef9d5 100644
--- a/traffic_ops/traffic_ops_golang/deliveryservice/request/comment/comments.go
+++ b/traffic_ops/traffic_ops_golang/deliveryservice/request/comment/comments.go
@@ -45,26 +45,34 @@ func GetRefType() *TODeliveryServiceRequestComment {
        return &refType
 }
 
+func (comment TODeliveryServiceRequestComment) GetKeyFieldsInfo() 
[]api.KeyFieldInfo {
+       return []api.KeyFieldInfo{{"id", api.GetIntKey}}
+}
+
 //Implementation of the Identifier, Validator interface functions
-func (comment TODeliveryServiceRequestComment) GetID() (int, bool) {
+func (comment TODeliveryServiceRequestComment) GetKeys() 
(map[string]interface{}, bool) {
        if comment.ID == nil {
-               return 0, false
+               return map[string]interface{}{"id": 0}, false
        }
-       return *comment.ID, true
+       return map[string]interface{}{"id": *comment.ID}, true
+}
+
+func (comment *TODeliveryServiceRequestComment) 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.
+       comment.ID = &i
 }
 
 func (comment TODeliveryServiceRequestComment) GetAuditName() string {
-       return strconv.Itoa(*comment.ID)
+       if comment.ID != nil {
+               return strconv.Itoa(*comment.ID)
+       }
+       return "unknown"
 }
 
 func (comment TODeliveryServiceRequestComment) GetType() string {
        return "deliveryservice_request_comment"
 }
 
-func (comment *TODeliveryServiceRequestComment) SetID(i int) {
-       comment.ID = &i
-}
-
 func (comment TODeliveryServiceRequestComment) Validate(db *sqlx.DB) []error {
        errs := validation.Errors{
                "deliveryServiceRequestId": 
validation.Validate(comment.DeliveryServiceRequestID, validation.NotNil),
@@ -129,7 +137,7 @@ func (comment *TODeliveryServiceRequestComment) Create(db 
*sqlx.DB, user auth.Cu
                log.Errorln(err)
                return tc.DBError, tc.SystemError
        }
-       comment.SetID(id)
+       comment.SetKeys(map[string]interface{}{"id": id})
        comment.LastUpdated = &lastUpdated
        err = tx.Commit()
        if err != nil {
diff --git 
a/traffic_ops/traffic_ops_golang/deliveryservice/request/comment/comments_test.go
 
b/traffic_ops/traffic_ops_golang/deliveryservice/request/comment/comments_test.go
index 575abdd33..3745da141 100644
--- 
a/traffic_ops/traffic_ops_golang/deliveryservice/request/comment/comments_test.go
+++ 
b/traffic_ops/traffic_ops_golang/deliveryservice/request/comment/comments_test.go
@@ -27,7 +27,6 @@ import (
 
        
"github.com/apache/incubator-trafficcontrol/traffic_ops/traffic_ops_golang/api"
        
"github.com/apache/incubator-trafficcontrol/traffic_ops/traffic_ops_golang/test"
-
 )
 
 func TestFuncs(t *testing.T) {
diff --git a/traffic_ops/traffic_ops_golang/deliveryservice/request/requests.go 
b/traffic_ops/traffic_ops_golang/deliveryservice/request/requests.go
index 9b9fc8bcb..4f11ba88f 100644
--- a/traffic_ops/traffic_ops_golang/deliveryservice/request/requests.go
+++ b/traffic_ops/traffic_ops_golang/deliveryservice/request/requests.go
@@ -46,14 +46,21 @@ func GetRefType() *TODeliveryServiceRequest {
        return &refType
 }
 
-//Implementation of the Identifier, Validator interface functions
+func (req TODeliveryServiceRequest) GetKeyFieldsInfo() []api.KeyFieldInfo {
+       return []api.KeyFieldInfo{{"id", api.GetIntKey}}
+}
 
-// GetID is part of the tc.Identifier interface
-func (req TODeliveryServiceRequest) GetID() (int, bool) {
+//Implementation of the Identifier, Validator interface functions
+func (req TODeliveryServiceRequest) GetKeys() (map[string]interface{}, bool) {
        if req.ID == nil {
-               return 0, false
+               return map[string]interface{}{"id": 0}, false
        }
-       return *req.ID, true
+       return map[string]interface{}{"id": *req.ID}, true
+}
+
+func (req *TODeliveryServiceRequest) 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.
+       req.ID = &i
 }
 
 // GetAuditName is part of the tc.Identifier interface
@@ -66,11 +73,6 @@ func (req TODeliveryServiceRequest) GetType() string {
        return "deliveryservice_request"
 }
 
-// SetID is part of the tc.Identifier interface
-func (req *TODeliveryServiceRequest) SetID(i int) {
-       req.ID = &i
-}
-
 // Read implements the api.Reader interface
 func (req *TODeliveryServiceRequest) Read(db *sqlx.DB, parameters 
map[string]string, user auth.CurrentUser) ([]interface{}, []error, 
tc.ApiErrorType) {
        queryParamsToQueryCols := map[string]dbhelpers.WhereColumnInfo{
@@ -355,7 +357,7 @@ func (req *TODeliveryServiceRequest) Create(db *sqlx.DB, 
user auth.CurrentUser)
                log.Errorln(err)
                return tc.DBError, tc.SystemError
        }
-       req.SetID(id)
+       req.SetKeys(map[string]interface{}{"id": id})
        req.LastUpdated = &lastUpdated
        err = tx.Commit()
        if err != nil {
@@ -435,8 +437,11 @@ func (req *TODeliveryServiceRequest) Delete(db *sqlx.DB, 
user auth.CurrentUser)
 
 func (req TODeliveryServiceRequest) getXMLID() string {
        if req.DeliveryService == nil || req.DeliveryService.XMLID == nil {
-               id, _ := req.GetID()
-               return strconv.Itoa(id)
+
+               if req.ID != nil {
+                       return strconv.Itoa(*req.ID)
+               }
+               return "0"
        }
        return *req.DeliveryService.XMLID
 }
diff --git 
a/traffic_ops/traffic_ops_golang/deliveryservice/request/requests_test.go 
b/traffic_ops/traffic_ops_golang/deliveryservice/request/requests_test.go
index 5aec066c2..7abb25cac 100644
--- a/traffic_ops/traffic_ops_golang/deliveryservice/request/requests_test.go
+++ b/traffic_ops/traffic_ops_golang/deliveryservice/request/requests_test.go
@@ -83,10 +83,10 @@ func TestGetDeliveryServiceRequest(t *testing.T) {
        */
        }
 
-       r.SetID(10)
-       id, _ := r.GetID()
-       if id != 10 {
-               t.Errorf("expected ID to be %d,  not %d", 10, id)
+       r.SetKeys(map[string]interface{}{"id": 10})
+       keys, _ := r.GetKeys()
+       if keys["id"].(int) != 10 {
+               t.Errorf("expected ID to be %d,  not %d", 10, keys["id"].(int))
        }
        exp := "10"
        if s != r.GetAuditName() {
diff --git a/traffic_ops/traffic_ops_golang/division/divisions.go 
b/traffic_ops/traffic_ops_golang/division/divisions.go
index 43caa78b5..260db1da6 100644
--- a/traffic_ops/traffic_ops_golang/division/divisions.go
+++ b/traffic_ops/traffic_ops_golang/division/divisions.go
@@ -46,22 +46,29 @@ func GetRefType() *TODivision {
 }
 
 func (division TODivision) GetAuditName() string {
-       if division.Name == nil {
-               id, _ := division.GetID()
-               return strconv.Itoa(id)
+       if division.Name != nil {
+               return *division.Name
        }
-       return *division.Name
+       if division.ID != nil {
+               return strconv.Itoa(*division.ID)
+       }
+       return "unknown"
+}
+
+func (division TODivision) GetKeyFieldsInfo() []api.KeyFieldInfo {
+       return []api.KeyFieldInfo{{"id", api.GetIntKey}}
 }
 
 //Implementation of the Identifier, Validator interface functions
-func (division TODivision) GetID() (int, bool) {
+func (division TODivision) GetKeys() (map[string]interface{}, bool) {
        if division.ID == nil {
-               return 0, false
+               return map[string]interface{}{"id": 0}, false
        }
-       return *division.ID, true
+       return map[string]interface{}{"id": *division.ID}, true
 }
 
-func (division *TODivision) SetID(i int) {
+func (division *TODivision) 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.
        division.ID = &i
 }
 
@@ -134,7 +141,7 @@ func (division *TODivision) Create(db *sqlx.DB, user 
auth.CurrentUser) (error, t
                log.Errorln(err)
                return tc.DBError, tc.SystemError
        }
-       division.SetID(id)
+       division.SetKeys(map[string]interface{}{"id": id})
        division.LastUpdated = &lastUpdated
        err = tx.Commit()
        if err != nil {
diff --git a/traffic_ops/traffic_ops_golang/parameter/parameters.go 
b/traffic_ops/traffic_ops_golang/parameter/parameters.go
index eb4bbb6b8..a75de8c8d 100644
--- a/traffic_ops/traffic_ops_golang/parameter/parameters.go
+++ b/traffic_ops/traffic_ops_golang/parameter/parameters.go
@@ -46,12 +46,21 @@ func GetRefType() *TOParameter {
        return &refType
 }
 
+func (parameter TOParameter) GetKeyFieldsInfo() []api.KeyFieldInfo {
+       return []api.KeyFieldInfo{{"id", api.GetIntKey}}
+}
+
 //Implementation of the Identifier, Validator interface functions
-func (parameter *TOParameter) GetID() (int, bool) {
+func (parameter TOParameter) GetKeys() (map[string]interface{}, bool) {
        if parameter.ID == nil {
-               return 0, false
+               return map[string]interface{}{"id": 0}, false
        }
-       return *parameter.ID, true
+       return map[string]interface{}{"id": *parameter.ID}, true
+}
+
+func (parameter *TOParameter) 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.
+       parameter.ID = &i
 }
 
 func (parameter *TOParameter) GetAuditName() string {
@@ -68,10 +77,6 @@ func (parameter *TOParameter) GetType() string {
        return "parameter"
 }
 
-func (parameter *TOParameter) SetID(i int) {
-       parameter.ID = &i
-}
-
 // Validate fulfills the api.Validator interface
 func (parameter TOParameter) Validate(db *sqlx.DB) []error {
 
@@ -147,7 +152,7 @@ func (pl *TOParameter) Create(db *sqlx.DB, user 
auth.CurrentUser) (error, tc.Api
                return tc.DBError, tc.SystemError
        }
 
-       pl.SetID(id)
+       pl.SetKeys(map[string]interface{}{"id": id})
        pl.LastUpdated = &lastUpdated
        err = tx.Commit()
        if err != nil {
diff --git a/traffic_ops/traffic_ops_golang/physlocation/phys_locations.go 
b/traffic_ops/traffic_ops_golang/physlocation/phys_locations.go
index be4524996..dcbb9a085 100644
--- a/traffic_ops/traffic_ops_golang/physlocation/phys_locations.go
+++ b/traffic_ops/traffic_ops_golang/physlocation/phys_locations.go
@@ -45,12 +45,21 @@ func GetRefType() *TOPhysLocation {
        return &refType
 }
 
+func (pl TOPhysLocation) GetKeyFieldsInfo() []api.KeyFieldInfo {
+       return []api.KeyFieldInfo{{"id", api.GetIntKey}}
+}
+
 //Implementation of the Identifier, Validator interface functions
-func (pl *TOPhysLocation) GetID() (int, bool) {
+func (pl TOPhysLocation) GetKeys() (map[string]interface{}, bool) {
        if pl.ID == nil {
-               return 0, false
+               return map[string]interface{}{"id": 0}, false
        }
-       return *pl.ID, true
+       return map[string]interface{}{"id": *pl.ID}, true
+}
+
+func (pl *TOPhysLocation) 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.
+       pl.ID = &i
 }
 
 func (pl *TOPhysLocation) GetAuditName() string {
@@ -67,10 +76,6 @@ func (pl *TOPhysLocation) GetType() string {
        return "physLocation"
 }
 
-func (pl *TOPhysLocation) SetID(i int) {
-       pl.ID = &i
-}
-
 func (pl *TOPhysLocation) Validate(db *sqlx.DB) []error {
        errs := validation.Errors{
                "address":   validation.Validate(pl.Address, 
validation.Required),
@@ -272,7 +277,7 @@ func (pl *TOPhysLocation) Create(db *sqlx.DB, user 
auth.CurrentUser) (error, tc.
                return tc.DBError, tc.SystemError
        }
 
-       pl.SetID(id)
+       pl.SetKeys(map[string]interface{}{"id": id})
        pl.LastUpdated = &lastUpdated
        err = tx.Commit()
        if err != nil {
diff --git a/traffic_ops/traffic_ops_golang/profile/profiles.go 
b/traffic_ops/traffic_ops_golang/profile/profiles.go
index e4903bd08..faccb26fe 100644
--- a/traffic_ops/traffic_ops_golang/profile/profiles.go
+++ b/traffic_ops/traffic_ops_golang/profile/profiles.go
@@ -45,12 +45,21 @@ func GetRefType() *TOProfile {
        return &refType
 }
 
+func (prof TOProfile) GetKeyFieldsInfo() []api.KeyFieldInfo {
+       return []api.KeyFieldInfo{{"id", api.GetIntKey}}
+}
+
 //Implementation of the Identifier, Validator interface functions
-func (prof TOProfile) GetID() (int, bool) {
+func (prof TOProfile) GetKeys() (map[string]interface{}, bool) {
        if prof.ID == nil {
-               return 0, false
+               return map[string]interface{}{"id": 0}, false
        }
-       return *prof.ID, true
+       return map[string]interface{}{"id": *prof.ID}, true
+}
+
+func (prof *TOProfile) 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.
+       prof.ID = &i
 }
 
 func (prof *TOProfile) GetAuditName() string {
@@ -67,10 +76,6 @@ func (prof *TOProfile) GetType() string {
        return "profile"
 }
 
-func (prof *TOProfile) SetID(i int) {
-       prof.ID = &i
-}
-
 func (prof *TOProfile) Validate(db *sqlx.DB) []error {
        errs := validation.Errors{
                "name":        validation.Validate(prof.Name, 
validation.Required),
@@ -263,7 +268,7 @@ func (prof *TOProfile) Create(db *sqlx.DB, user 
auth.CurrentUser) (error, tc.Api
                return tc.DBError, tc.SystemError
        }
 
-       prof.SetID(id)
+       prof.SetKeys(map[string]interface{}{"id": id})
        prof.LastUpdated = &lastUpdated
        err = tx.Commit()
        if err != nil {
diff --git a/traffic_ops/traffic_ops_golang/region/regions.go 
b/traffic_ops/traffic_ops_golang/region/regions.go
index 6719f3e2e..e891caac1 100644
--- a/traffic_ops/traffic_ops_golang/region/regions.go
+++ b/traffic_ops/traffic_ops_golang/region/regions.go
@@ -42,9 +42,18 @@ func GetRefType() *TORegion {
        return &refType
 }
 
+func (region TORegion) GetKeyFieldsInfo() []api.KeyFieldInfo {
+       return []api.KeyFieldInfo{{"id", api.GetIntKey}}
+}
+
 //Implementation of the Identifier, Validator interface functions
-func (region *TORegion) GetID() (int, bool) {
-       return region.ID, true
+func (region TORegion) GetKeys() (map[string]interface{}, bool) {
+       return map[string]interface{}{"id": region.ID}, true
+}
+
+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
 }
 
 func (region *TORegion) GetAuditName() string {
@@ -55,10 +64,6 @@ func (region *TORegion) GetType() string {
        return "region"
 }
 
-func (region *TORegion) SetID(i int) {
-       region.ID = i
-}
-
 func (region *TORegion) Validate(db *sqlx.DB) []error {
        errs := []error{}
        if len(region.Name) < 1 {
@@ -239,7 +244,7 @@ func (region *TORegion) Create(db *sqlx.DB, user 
auth.CurrentUser) (error, tc.Ap
                log.Errorln(err)
                return tc.DBError, tc.SystemError
        }
-       region.SetID(id)
+       region.SetKeys(map[string]interface{}{"id": id})
        region.LastUpdated = lastUpdated
        err = tx.Commit()
        if err != nil {
diff --git a/traffic_ops/traffic_ops_golang/server/servers.go 
b/traffic_ops/traffic_ops_golang/server/servers.go
index 35bb845d3..a08a33db7 100644
--- a/traffic_ops/traffic_ops_golang/server/servers.go
+++ b/traffic_ops/traffic_ops_golang/server/servers.go
@@ -48,30 +48,37 @@ func GetRefType() *TOServer {
        return &refType
 }
 
+func (server TOServer) GetKeyFieldsInfo() []api.KeyFieldInfo {
+       return []api.KeyFieldInfo{{"id", api.GetIntKey}}
+}
+
 //Implementation of the Identifier, Validator interface functions
-func (server *TOServer) GetID() (int, bool) {
+func (server TOServer) GetKeys() (map[string]interface{}, bool) {
        if server.ID == nil {
-               return 0, false
+               return map[string]interface{}{"id": 0}, false
        }
-       return *server.ID, true
+       return map[string]interface{}{"id": *server.ID}, true
+}
+
+func (server *TOServer) 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.
+       server.ID = &i
 }
 
 func (server *TOServer) GetAuditName() string {
        if server.DomainName != nil {
                return *server.DomainName
        }
-       id, _ := server.GetID()
-       return strconv.Itoa(id)
+       if server.ID != nil {
+               return strconv.Itoa(*server.ID)
+       }
+       return "unknown"
 }
 
 func (server *TOServer) GetType() string {
        return "server"
 }
 
-func (server *TOServer) SetID(i int) {
-       server.ID = &i
-}
-
 func (server *TOServer) Validate(db *sqlx.DB) []error {
 
        noSpaces := validation.NewStringRule(tovalidate.NoSpaces, "cannot 
contain spaces")
@@ -441,7 +448,7 @@ func (server *TOServer) Create(db *sqlx.DB, user 
auth.CurrentUser) (error, tc.Ap
                log.Errorln(err)
                return tc.DBError, tc.SystemError
        }
-       server.SetID(id)
+       server.SetKeys(map[string]interface{}{"id": id})
        server.LastUpdated = &lastUpdated
        err = tx.Commit()
        if err != nil {
diff --git a/traffic_ops/traffic_ops_golang/status/statuses.go 
b/traffic_ops/traffic_ops_golang/status/statuses.go
index 3cc11c84a..df5d3f3ea 100644
--- a/traffic_ops/traffic_ops_golang/status/statuses.go
+++ b/traffic_ops/traffic_ops_golang/status/statuses.go
@@ -45,30 +45,37 @@ func GetRefType() *TOStatus {
        return &refType
 }
 
+func (status TOStatus) GetKeyFieldsInfo() []api.KeyFieldInfo {
+       return []api.KeyFieldInfo{{"id", api.GetIntKey}}
+}
+
 //Implementation of the Identifier, Validator interface functions
-func (status TOStatus) GetID() (int, bool) {
+func (status TOStatus) GetKeys() (map[string]interface{}, bool) {
        if status.ID == nil {
-               return 0, false
+               return map[string]interface{}{"id": 0}, false
        }
-       return *status.ID, true
+       return map[string]interface{}{"id": *status.ID}, true
+}
+
+func (status *TOStatus) 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.
+       status.ID = &i
 }
 
 func (status TOStatus) GetAuditName() string {
-       if status.Name == nil {
-               id, _ := status.GetID()
-               return strconv.Itoa(id)
+       if status.Name != nil {
+               return *status.Name
        }
-       return *status.Name
+       if status.ID != nil {
+               return strconv.Itoa(*status.ID)
+       }
+       return "unknown"
 }
 
 func (status TOStatus) GetType() string {
        return "status"
 }
 
-func (status *TOStatus) SetID(i int) {
-       status.ID = &i
-}
-
 func (status TOStatus) Validate(db *sqlx.DB) []error {
        errs := validation.Errors{
                "name": validation.Validate(status.Name, validation.NotNil, 
validation.Required),
@@ -249,7 +256,7 @@ func (status *TOStatus) Create(db *sqlx.DB, user 
auth.CurrentUser) (error, tc.Ap
                log.Errorln(err)
                return tc.DBError, tc.SystemError
        }
-       status.SetID(id)
+       status.SetKeys(map[string]interface{}{"id": id})
        status.LastUpdated = &lastUpdated
        err = tx.Commit()
        if err != nil {
diff --git a/traffic_ops/traffic_ops_golang/types/types.go 
b/traffic_ops/traffic_ops_golang/types/types.go
index 41811b40f..74c10eb8f 100644
--- a/traffic_ops/traffic_ops_golang/types/types.go
+++ b/traffic_ops/traffic_ops_golang/types/types.go
@@ -45,12 +45,21 @@ func GetRefType() *TOType {
        return &refType
 }
 
+func (typ TOType) GetKeyFieldsInfo() []api.KeyFieldInfo {
+       return []api.KeyFieldInfo{{"id", api.GetIntKey}}
+}
+
 //Implementation of the Identifier, Validator interface functions
-func (typ *TOType) GetID() (int, bool) {
+func (typ TOType) GetKeys() (map[string]interface{}, bool) {
        if typ.ID == nil {
-               return 0, false
+               return map[string]interface{}{"id": 0}, false
        }
-       return *typ.ID, true
+       return map[string]interface{}{"id": *typ.ID}, true
+}
+
+func (typ *TOType) 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.
+       typ.ID = &i
 }
 
 func (typ *TOType) GetAuditName() string {
@@ -67,10 +76,6 @@ func (typ *TOType) GetType() string {
        return "type"
 }
 
-func (typ *TOType) SetID(i int) {
-       typ.ID = &i
-}
-
 func (typ *TOType) Validate(db *sqlx.DB) []error {
        errs := validation.Errors{
                "name":         validation.Validate(typ.Name, 
validation.Required),
@@ -255,7 +260,7 @@ func (typ *TOType) Create(db *sqlx.DB, user 
auth.CurrentUser) (error, tc.ApiErro
                return tc.DBError, tc.SystemError
        }
 
-       typ.SetID(id)
+       typ.SetKeys(map[string]interface{}{"id": id})
        typ.LastUpdated = &lastUpdated
        err = tx.Commit()
        if err != nil {


 

----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on GitHub and use the
URL above to go to the specific comment.
 
For queries about this service, please contact Infrastructure at:
[email protected]


With regards,
Apache Git Services

Reply via email to