ocket8888 commented on code in PR #7213:
URL: https://github.com/apache/trafficcontrol/pull/7213#discussion_r1050958389


##########
traffic_ops/traffic_ops_golang/servercapability/servercapability.go:
##########
@@ -151,6 +153,213 @@ func (v *TOServerCapability) Update(h http.Header) 
(error, error, int) {
        return nil, nil, http.StatusOK
 }
 
+func GetServerCapability(w http.ResponseWriter, r *http.Request) {
+       var sc tc.ServerCapabilityV4
+       inf, userErr, sysErr, errCode := api.NewInfo(r, nil, nil)
+       tx := inf.Tx
+       if userErr != nil || sysErr != nil {
+               api.HandleErr(w, r, tx.Tx, errCode, userErr, sysErr)
+               return
+       }
+       defer inf.Close()
+
+       // Query Parameters to Database Query column mappings
+       queryParamsToQueryCols := map[string]dbhelpers.WhereColumnInfo{
+               "name": {Column: "sc.name", Checker: nil},
+       }
+       if _, ok := inf.Params["orderby"]; !ok {
+               inf.Params["orderby"] = "name"
+       }
+       where, orderBy, pagination, queryValues, errs := 
dbhelpers.BuildWhereAndOrderByAndPagination(inf.Params, queryParamsToQueryCols)
+       if len(errs) > 0 {
+               api.HandleErr(w, r, tx.Tx, http.StatusBadRequest, 
util.JoinErrs(errs), nil)
+               return
+       }
+
+       selectQuery := "SELECT name, description, last_updated FROM 
server_capability sc"
+       query := selectQuery + where + orderBy + pagination
+       rows, err := tx.NamedQuery(query, queryValues)
+       if err != nil {
+               api.HandleErr(w, r, tx.Tx, http.StatusInternalServerError, nil, 
fmt.Errorf("server capability read: error getting server capability(ies): %w", 
err))
+               return
+       }
+       defer log.Close(rows, "unable to close DB connection")
+
+       scList := []tc.ServerCapabilityV4{}
+       for rows.Next() {
+               if err = rows.Scan(&sc.Name, &sc.Description, &sc.LastUpdated); 
err != nil {
+                       api.HandleErr(w, r, tx.Tx, 
http.StatusInternalServerError, nil, fmt.Errorf("error getting server 
capability(ies): %w", err))
+                       return
+               }
+               scList = append(scList, sc)
+       }
+
+       api.WriteResp(w, r, scList)
+       return
+}
+
+func UpdateServerCapability(w http.ResponseWriter, r *http.Request) {
+       inf, userErr, sysErr, errCode := api.NewInfo(r, nil, nil)
+       if userErr != nil || sysErr != nil {
+               api.HandleErr(w, r, inf.Tx.Tx, errCode, userErr, sysErr)
+               return
+       }
+       defer inf.Close()
+
+       tx := inf.Tx.Tx
+       sc, readValErr := readAndValidateJsonStruct(r)
+       if readValErr != nil {
+               api.HandleErr(w, r, tx, http.StatusBadRequest, readValErr, nil)
+               return
+       }
+
+       requestedName := inf.Params["name"]
+       // check if the entity was already updated
+       userErr, sysErr, errCode = api.CheckIfUnModifiedByName(r.Header, 
inf.Tx, requestedName, "server_capability")
+       if userErr != nil || sysErr != nil {
+               api.HandleErr(w, r, tx, errCode, userErr, sysErr)
+               return
+       }
+
+       //update name and description of a capability
+       query := `UPDATE server_capability sc SET
+               name = $1,
+               description = $2
+       WHERE sc.name = $3
+       RETURNING sc.name, sc.description, sc.last_updated`
+
+       err := tx.QueryRow(query, sc.Name, sc.Description, 
requestedName).Scan(&sc.Name, &sc.Description, &sc.LastUpdated)
+       if err != nil {
+               if err == sql.ErrNoRows {
+                       api.HandleErr(w, r, tx, http.StatusBadRequest, err, nil)
+                       return
+               }
+               usrErr, sysErr, code := api.ParseDBError(err)
+               api.HandleErr(w, r, tx, code, usrErr, sysErr)
+               return
+       }
+       alerts := tc.CreateAlerts(tc.SuccessLevel, "server capability was 
updated")
+       api.WriteAlertsObj(w, r, http.StatusOK, alerts, sc)
+       return
+}
+
+func CreateServerCapability(w http.ResponseWriter, r *http.Request) {
+       inf, userErr, sysErr, errCode := api.NewInfo(r, nil, nil)
+       if userErr != nil || sysErr != nil {
+               api.HandleErr(w, r, inf.Tx.Tx, errCode, userErr, sysErr)
+               return
+       }
+       defer inf.Close()
+       tx := inf.Tx.Tx
+
+       sc, readValErr := readAndValidateJsonStruct(r)
+       if readValErr != nil {
+               api.HandleErr(w, r, tx, http.StatusBadRequest, readValErr, nil)
+               return
+       }
+
+       // check if capability already exists
+       var count int
+       err := tx.QueryRow("SELECT count(*) from server_capability where name = 
$1", sc.Name).Scan(&count)
+       if err != nil {
+               api.HandleErr(w, r, tx, http.StatusInternalServerError, nil, 
fmt.Errorf("error: %w, when checking if server capability with name %s exists", 
err, sc.Name))
+               return
+       }
+       if count == 1 {
+               api.HandleErr(w, r, tx, http.StatusBadRequest, 
fmt.Errorf("server_capability name '%s' already exists.", sc.Name), nil)

Review Comment:
   error strings shouldn't end with punctuation



##########
traffic_ops/traffic_ops_golang/servercapability/servercapability.go:
##########
@@ -151,6 +153,213 @@ func (v *TOServerCapability) Update(h http.Header) 
(error, error, int) {
        return nil, nil, http.StatusOK
 }
 
+func GetServerCapability(w http.ResponseWriter, r *http.Request) {
+       var sc tc.ServerCapabilityV4
+       inf, userErr, sysErr, errCode := api.NewInfo(r, nil, nil)
+       tx := inf.Tx
+       if userErr != nil || sysErr != nil {
+               api.HandleErr(w, r, tx.Tx, errCode, userErr, sysErr)
+               return
+       }
+       defer inf.Close()
+
+       // Query Parameters to Database Query column mappings
+       queryParamsToQueryCols := map[string]dbhelpers.WhereColumnInfo{
+               "name": {Column: "sc.name", Checker: nil},
+       }
+       if _, ok := inf.Params["orderby"]; !ok {
+               inf.Params["orderby"] = "name"
+       }
+       where, orderBy, pagination, queryValues, errs := 
dbhelpers.BuildWhereAndOrderByAndPagination(inf.Params, queryParamsToQueryCols)
+       if len(errs) > 0 {
+               api.HandleErr(w, r, tx.Tx, http.StatusBadRequest, 
util.JoinErrs(errs), nil)
+               return
+       }
+
+       selectQuery := "SELECT name, description, last_updated FROM 
server_capability sc"
+       query := selectQuery + where + orderBy + pagination
+       rows, err := tx.NamedQuery(query, queryValues)
+       if err != nil {
+               api.HandleErr(w, r, tx.Tx, http.StatusInternalServerError, nil, 
fmt.Errorf("server capability read: error getting server capability(ies): %w", 
err))
+               return
+       }
+       defer log.Close(rows, "unable to close DB connection")
+
+       scList := []tc.ServerCapabilityV4{}
+       for rows.Next() {
+               if err = rows.Scan(&sc.Name, &sc.Description, &sc.LastUpdated); 
err != nil {
+                       api.HandleErr(w, r, tx.Tx, 
http.StatusInternalServerError, nil, fmt.Errorf("error getting server 
capability(ies): %w", err))
+                       return
+               }
+               scList = append(scList, sc)
+       }
+
+       api.WriteResp(w, r, scList)
+       return
+}
+
+func UpdateServerCapability(w http.ResponseWriter, r *http.Request) {
+       inf, userErr, sysErr, errCode := api.NewInfo(r, nil, nil)
+       if userErr != nil || sysErr != nil {
+               api.HandleErr(w, r, inf.Tx.Tx, errCode, userErr, sysErr)
+               return
+       }
+       defer inf.Close()
+
+       tx := inf.Tx.Tx
+       sc, readValErr := readAndValidateJsonStruct(r)
+       if readValErr != nil {
+               api.HandleErr(w, r, tx, http.StatusBadRequest, readValErr, nil)
+               return
+       }
+
+       requestedName := inf.Params["name"]
+       // check if the entity was already updated
+       userErr, sysErr, errCode = api.CheckIfUnModifiedByName(r.Header, 
inf.Tx, requestedName, "server_capability")
+       if userErr != nil || sysErr != nil {
+               api.HandleErr(w, r, tx, errCode, userErr, sysErr)
+               return
+       }
+
+       //update name and description of a capability
+       query := `UPDATE server_capability sc SET
+               name = $1,
+               description = $2
+       WHERE sc.name = $3
+       RETURNING sc.name, sc.description, sc.last_updated`
+
+       err := tx.QueryRow(query, sc.Name, sc.Description, 
requestedName).Scan(&sc.Name, &sc.Description, &sc.LastUpdated)
+       if err != nil {
+               if err == sql.ErrNoRows {
+                       api.HandleErr(w, r, tx, http.StatusBadRequest, err, nil)
+                       return
+               }
+               usrErr, sysErr, code := api.ParseDBError(err)
+               api.HandleErr(w, r, tx, code, usrErr, sysErr)
+               return
+       }
+       alerts := tc.CreateAlerts(tc.SuccessLevel, "server capability was 
updated")
+       api.WriteAlertsObj(w, r, http.StatusOK, alerts, sc)
+       return
+}
+
+func CreateServerCapability(w http.ResponseWriter, r *http.Request) {
+       inf, userErr, sysErr, errCode := api.NewInfo(r, nil, nil)
+       if userErr != nil || sysErr != nil {
+               api.HandleErr(w, r, inf.Tx.Tx, errCode, userErr, sysErr)
+               return
+       }
+       defer inf.Close()
+       tx := inf.Tx.Tx
+
+       sc, readValErr := readAndValidateJsonStruct(r)
+       if readValErr != nil {
+               api.HandleErr(w, r, tx, http.StatusBadRequest, readValErr, nil)
+               return
+       }
+
+       // check if capability already exists
+       var count int
+       err := tx.QueryRow("SELECT count(*) from server_capability where name = 
$1", sc.Name).Scan(&count)
+       if err != nil {
+               api.HandleErr(w, r, tx, http.StatusInternalServerError, nil, 
fmt.Errorf("error: %w, when checking if server capability with name %s exists", 
err, sc.Name))
+               return
+       }
+       if count == 1 {
+               api.HandleErr(w, r, tx, http.StatusBadRequest, 
fmt.Errorf("server_capability name '%s' already exists.", sc.Name), nil)
+               return
+       }
+
+       // create server capability
+       query := `INSERT INTO server_capability (name, description) VALUES ($1, 
$2) RETURNING last_updated`
+       err = tx.QueryRow(query, sc.Name, sc.Description).Scan(&sc.LastUpdated)
+       if err != nil {
+               if err == sql.ErrNoRows {
+                       api.HandleErr(w, r, tx, http.StatusBadRequest, err, nil)
+                       return
+               }
+               usrErr, sysErr, code := api.ParseDBError(err)
+               api.HandleErr(w, r, tx, code, usrErr, sysErr)
+               return
+       }
+       alerts := tc.CreateAlerts(tc.SuccessLevel, "server capability was 
created.")
+       w.Header().Set("Location", 
fmt.Sprintf("/api/%d.%d/server_capabilities$?name=%s", inf.Version.Major, 
inf.Version.Minor, sc.Name))
+       api.WriteAlertsObj(w, r, http.StatusCreated, alerts, sc)
+       return
+}
+
+func DeleteServerCapability(w http.ResponseWriter, r *http.Request) {
+       inf, userErr, sysErr, errCode := api.NewInfo(r, nil, nil)
+       tx := inf.Tx.Tx
+       if userErr != nil || sysErr != nil {
+               api.HandleErr(w, r, tx, errCode, userErr, sysErr)
+               return
+       }
+       defer inf.Close()
+
+       name := inf.Params["name"]
+       exists, err := dbhelpers.GetSCInfo(tx, name)
+       if err != nil {
+               api.HandleErr(w, r, tx, http.StatusInternalServerError, nil, 
err)
+               return
+       }
+       if !exists {
+               if name != "" {
+                       api.HandleErr(w, r, tx, http.StatusNotFound, 
fmt.Errorf("no server capability exists by name: %s", name), nil)
+                       return
+               } else {
+                       api.HandleErr(w, r, tx, http.StatusBadRequest, 
fmt.Errorf("no server capability exists for empty name: %s", name), nil)
+                       return
+               }
+       }
+
+       assignedServer := 0
+       if err := inf.Tx.Get(&assignedServer, "SELECT count(server) FROM 
server_server_capability ssc WHERE ssc.server_capability=$1", name); err != nil 
{
+               api.HandleErr(w, r, tx, http.StatusInternalServerError, nil, 
fmt.Errorf("server capability delete, counting assigned servers: %w", err))
+               return
+       } else if assignedServer != 0 {
+               api.HandleErr(w, r, tx, http.StatusBadRequest, fmt.Errorf("can 
not delete a server capability with %d assigned servers", assignedServer), nil)
+               return
+       }
+
+       res, err := tx.Exec("DELETE FROM server_capability AS sc WHERE 
sc.name=$1", name)
+       if err != nil {
+               api.HandleErr(w, r, tx, http.StatusInternalServerError, nil, 
err)
+               return
+       }
+       rowsAffected, err := res.RowsAffected()
+       if err != nil {
+               api.HandleErr(w, r, tx, http.StatusInternalServerError, nil, 
fmt.Errorf("determining rows affected for delete server capability: %w", err))
+               return
+       }
+       if rowsAffected == 0 {
+               api.HandleErr(w, r, tx, http.StatusBadRequest, fmt.Errorf("no 
rows deleted for server capability"), nil)

Review Comment:
   This seems like an error that's internal to the server to me.



##########
traffic_ops/traffic_ops_golang/dbhelpers/db_helpers_test.go:
##########
@@ -408,3 +408,55 @@ func TestGetCDNIDFromName(t *testing.T) {
        }
 
 }
+
+func TestGetSCInfo(t *testing.T) {
+       var testCases = []struct {
+               description   string
+               name          string
+               expectedError error
+               exists        bool
+       }{
+               //{
+               //      description:   "Success: Get valid SC",
+               //      name:          "hdd",
+               //      expectedError: nil,
+               //      exists:        true,
+               //},
+               {
+                       description:   "Failure: SC not in DB",
+                       name:          "disk",
+                       expectedError: errors.New("error getting server 
capability info: sql: no rows in result set"),
+                       exists:        false,
+               },
+       }
+       for _, testCase := range testCases {
+               t.Run(testCase.description, func(t *testing.T) {
+                       t.Log("Starting test scenario: ", testCase.description)

Review Comment:
   You shouldn't need to log this, since the results for the `t.Run` function 
will include the description in the name, and everything will be under that.



##########
traffic_ops/traffic_ops_golang/servercapability/servercapability.go:
##########
@@ -151,6 +153,213 @@ func (v *TOServerCapability) Update(h http.Header) 
(error, error, int) {
        return nil, nil, http.StatusOK
 }
 
+func GetServerCapability(w http.ResponseWriter, r *http.Request) {
+       var sc tc.ServerCapabilityV4
+       inf, userErr, sysErr, errCode := api.NewInfo(r, nil, nil)
+       tx := inf.Tx
+       if userErr != nil || sysErr != nil {
+               api.HandleErr(w, r, tx.Tx, errCode, userErr, sysErr)
+               return
+       }
+       defer inf.Close()
+
+       // Query Parameters to Database Query column mappings
+       queryParamsToQueryCols := map[string]dbhelpers.WhereColumnInfo{
+               "name": {Column: "sc.name", Checker: nil},
+       }
+       if _, ok := inf.Params["orderby"]; !ok {
+               inf.Params["orderby"] = "name"
+       }
+       where, orderBy, pagination, queryValues, errs := 
dbhelpers.BuildWhereAndOrderByAndPagination(inf.Params, queryParamsToQueryCols)
+       if len(errs) > 0 {
+               api.HandleErr(w, r, tx.Tx, http.StatusBadRequest, 
util.JoinErrs(errs), nil)
+               return
+       }
+
+       selectQuery := "SELECT name, description, last_updated FROM 
server_capability sc"
+       query := selectQuery + where + orderBy + pagination
+       rows, err := tx.NamedQuery(query, queryValues)
+       if err != nil {
+               api.HandleErr(w, r, tx.Tx, http.StatusInternalServerError, nil, 
fmt.Errorf("server capability read: error getting server capability(ies): %w", 
err))
+               return
+       }
+       defer log.Close(rows, "unable to close DB connection")
+
+       scList := []tc.ServerCapabilityV4{}
+       for rows.Next() {
+               if err = rows.Scan(&sc.Name, &sc.Description, &sc.LastUpdated); 
err != nil {
+                       api.HandleErr(w, r, tx.Tx, 
http.StatusInternalServerError, nil, fmt.Errorf("error getting server 
capability(ies): %w", err))
+                       return
+               }
+               scList = append(scList, sc)
+       }
+
+       api.WriteResp(w, r, scList)
+       return
+}
+
+func UpdateServerCapability(w http.ResponseWriter, r *http.Request) {
+       inf, userErr, sysErr, errCode := api.NewInfo(r, nil, nil)
+       if userErr != nil || sysErr != nil {
+               api.HandleErr(w, r, inf.Tx.Tx, errCode, userErr, sysErr)
+               return
+       }
+       defer inf.Close()
+
+       tx := inf.Tx.Tx
+       sc, readValErr := readAndValidateJsonStruct(r)
+       if readValErr != nil {
+               api.HandleErr(w, r, tx, http.StatusBadRequest, readValErr, nil)
+               return
+       }
+
+       requestedName := inf.Params["name"]
+       // check if the entity was already updated
+       userErr, sysErr, errCode = api.CheckIfUnModifiedByName(r.Header, 
inf.Tx, requestedName, "server_capability")
+       if userErr != nil || sysErr != nil {
+               api.HandleErr(w, r, tx, errCode, userErr, sysErr)
+               return
+       }
+
+       //update name and description of a capability
+       query := `UPDATE server_capability sc SET
+               name = $1,
+               description = $2
+       WHERE sc.name = $3
+       RETURNING sc.name, sc.description, sc.last_updated`
+
+       err := tx.QueryRow(query, sc.Name, sc.Description, 
requestedName).Scan(&sc.Name, &sc.Description, &sc.LastUpdated)
+       if err != nil {
+               if err == sql.ErrNoRows {
+                       api.HandleErr(w, r, tx, http.StatusBadRequest, err, nil)
+                       return
+               }
+               usrErr, sysErr, code := api.ParseDBError(err)
+               api.HandleErr(w, r, tx, code, usrErr, sysErr)
+               return
+       }
+       alerts := tc.CreateAlerts(tc.SuccessLevel, "server capability was 
updated")
+       api.WriteAlertsObj(w, r, http.StatusOK, alerts, sc)
+       return
+}
+
+func CreateServerCapability(w http.ResponseWriter, r *http.Request) {
+       inf, userErr, sysErr, errCode := api.NewInfo(r, nil, nil)
+       if userErr != nil || sysErr != nil {
+               api.HandleErr(w, r, inf.Tx.Tx, errCode, userErr, sysErr)
+               return
+       }
+       defer inf.Close()
+       tx := inf.Tx.Tx
+
+       sc, readValErr := readAndValidateJsonStruct(r)
+       if readValErr != nil {
+               api.HandleErr(w, r, tx, http.StatusBadRequest, readValErr, nil)
+               return
+       }
+
+       // check if capability already exists
+       var count int
+       err := tx.QueryRow("SELECT count(*) from server_capability where name = 
$1", sc.Name).Scan(&count)
+       if err != nil {
+               api.HandleErr(w, r, tx, http.StatusInternalServerError, nil, 
fmt.Errorf("error: %w, when checking if server capability with name %s exists", 
err, sc.Name))
+               return
+       }
+       if count == 1 {
+               api.HandleErr(w, r, tx, http.StatusBadRequest, 
fmt.Errorf("server_capability name '%s' already exists.", sc.Name), nil)
+               return
+       }
+
+       // create server capability
+       query := `INSERT INTO server_capability (name, description) VALUES ($1, 
$2) RETURNING last_updated`
+       err = tx.QueryRow(query, sc.Name, sc.Description).Scan(&sc.LastUpdated)
+       if err != nil {
+               if err == sql.ErrNoRows {
+                       api.HandleErr(w, r, tx, http.StatusBadRequest, err, nil)
+                       return
+               }

Review Comment:
   If an `INSERT` affects zero rows without violating a constraint, that's not 
a bad request; it means that there's something very wrong with the database.



##########
traffic_ops/traffic_ops_golang/dbhelpers/db_helpers_test.go:
##########
@@ -408,3 +408,55 @@ func TestGetCDNIDFromName(t *testing.T) {
        }
 
 }
+
+func TestGetSCInfo(t *testing.T) {
+       var testCases = []struct {
+               description   string
+               name          string
+               expectedError error
+               exists        bool
+       }{
+               //{
+               //      description:   "Success: Get valid SC",
+               //      name:          "hdd",
+               //      expectedError: nil,
+               //      exists:        true,
+               //},
+               {
+                       description:   "Failure: SC not in DB",
+                       name:          "disk",
+                       expectedError: errors.New("error getting server 
capability info: sql: no rows in result set"),
+                       exists:        false,
+               },
+       }
+       for _, testCase := range testCases {
+               t.Run(testCase.description, func(t *testing.T) {
+                       t.Log("Starting test scenario: ", testCase.description)
+                       mockDB, mock, err := sqlmock.New()
+                       if err != nil {
+                               t.Fatalf("an error '%s' was not expected when 
opening a stub database connection", err)
+                       }
+                       defer mockDB.Close()
+
+                       db := sqlx.NewDb(mockDB, "sqlmock")
+                       defer db.Close()
+
+                       rows := sqlmock.NewRows([]string{"name"})
+                       mock.ExpectBegin()
+                       if testCase.exists {
+                               rows = rows.AddRow(testCase.name)
+                       }
+                       mock.ExpectQuery("SELECT").WillReturnRows(rows)
+                       mock.ExpectCommit()
+
+                       scExists, err := GetSCInfo(db.MustBegin().Tx, 
testCase.name)
+                       if testCase.exists != scExists {
+                               t.Errorf("Expected return exists: %t, actual 
%t", testCase.exists, scExists)
+                       }
+
+                       if err == testCase.expectedError {
+                               t.Errorf("getSCInfo expected: %s, actual: %s", 
testCase.expectedError, err)
+                       }
+               })
+       }
+}

Review Comment:
   So, it seems you've commented out the positive test, and I think I know why. 
I was puzzled how the negative test would be passing since two `error`s aren't 
equal just because they have the same message, but then I realized: your 
negative test passing is a false positive, because your test fails *if the 
error you get is equal to the error you expect*, which is the opposite of what 
expectation implies. Line 457 should be doing the opposite of what it's doing, 
but that alone won't pass, because you also need the correct error specified on 
line 428 - I believe you're looking for `sql.ErrNoRows`, and that comparison 
should be done with `errors.Is`.



##########
traffic_ops/traffic_ops_golang/servercapability/servercapability.go:
##########
@@ -151,6 +153,213 @@ func (v *TOServerCapability) Update(h http.Header) 
(error, error, int) {
        return nil, nil, http.StatusOK
 }
 
+func GetServerCapability(w http.ResponseWriter, r *http.Request) {
+       var sc tc.ServerCapabilityV4
+       inf, userErr, sysErr, errCode := api.NewInfo(r, nil, nil)
+       tx := inf.Tx
+       if userErr != nil || sysErr != nil {
+               api.HandleErr(w, r, tx.Tx, errCode, userErr, sysErr)
+               return
+       }
+       defer inf.Close()
+
+       // Query Parameters to Database Query column mappings
+       queryParamsToQueryCols := map[string]dbhelpers.WhereColumnInfo{
+               "name": {Column: "sc.name", Checker: nil},
+       }
+       if _, ok := inf.Params["orderby"]; !ok {
+               inf.Params["orderby"] = "name"
+       }
+       where, orderBy, pagination, queryValues, errs := 
dbhelpers.BuildWhereAndOrderByAndPagination(inf.Params, queryParamsToQueryCols)
+       if len(errs) > 0 {
+               api.HandleErr(w, r, tx.Tx, http.StatusBadRequest, 
util.JoinErrs(errs), nil)
+               return
+       }
+
+       selectQuery := "SELECT name, description, last_updated FROM 
server_capability sc"
+       query := selectQuery + where + orderBy + pagination
+       rows, err := tx.NamedQuery(query, queryValues)
+       if err != nil {
+               api.HandleErr(w, r, tx.Tx, http.StatusInternalServerError, nil, 
fmt.Errorf("server capability read: error getting server capability(ies): %w", 
err))
+               return
+       }
+       defer log.Close(rows, "unable to close DB connection")
+
+       scList := []tc.ServerCapabilityV4{}
+       for rows.Next() {
+               if err = rows.Scan(&sc.Name, &sc.Description, &sc.LastUpdated); 
err != nil {
+                       api.HandleErr(w, r, tx.Tx, 
http.StatusInternalServerError, nil, fmt.Errorf("error getting server 
capability(ies): %w", err))
+                       return
+               }
+               scList = append(scList, sc)
+       }
+
+       api.WriteResp(w, r, scList)
+       return
+}
+
+func UpdateServerCapability(w http.ResponseWriter, r *http.Request) {
+       inf, userErr, sysErr, errCode := api.NewInfo(r, nil, nil)
+       if userErr != nil || sysErr != nil {
+               api.HandleErr(w, r, inf.Tx.Tx, errCode, userErr, sysErr)
+               return
+       }
+       defer inf.Close()
+
+       tx := inf.Tx.Tx
+       sc, readValErr := readAndValidateJsonStruct(r)
+       if readValErr != nil {
+               api.HandleErr(w, r, tx, http.StatusBadRequest, readValErr, nil)
+               return
+       }
+
+       requestedName := inf.Params["name"]
+       // check if the entity was already updated
+       userErr, sysErr, errCode = api.CheckIfUnModifiedByName(r.Header, 
inf.Tx, requestedName, "server_capability")
+       if userErr != nil || sysErr != nil {
+               api.HandleErr(w, r, tx, errCode, userErr, sysErr)
+               return
+       }
+
+       //update name and description of a capability
+       query := `UPDATE server_capability sc SET
+               name = $1,
+               description = $2
+       WHERE sc.name = $3
+       RETURNING sc.name, sc.description, sc.last_updated`
+
+       err := tx.QueryRow(query, sc.Name, sc.Description, 
requestedName).Scan(&sc.Name, &sc.Description, &sc.LastUpdated)
+       if err != nil {
+               if err == sql.ErrNoRows {
+                       api.HandleErr(w, r, tx, http.StatusBadRequest, err, nil)
+                       return
+               }

Review Comment:
   You don't need to bother checking this. The above call to 
`api.CheckIfUnModifiedByName` already covers the case that the SC doesn't exist.



##########
traffic_ops/traffic_ops_golang/servercapability/servercapability.go:
##########
@@ -151,6 +153,213 @@ func (v *TOServerCapability) Update(h http.Header) 
(error, error, int) {
        return nil, nil, http.StatusOK
 }
 
+func GetServerCapability(w http.ResponseWriter, r *http.Request) {
+       var sc tc.ServerCapabilityV4
+       inf, userErr, sysErr, errCode := api.NewInfo(r, nil, nil)
+       tx := inf.Tx
+       if userErr != nil || sysErr != nil {
+               api.HandleErr(w, r, tx.Tx, errCode, userErr, sysErr)
+               return
+       }
+       defer inf.Close()
+
+       // Query Parameters to Database Query column mappings
+       queryParamsToQueryCols := map[string]dbhelpers.WhereColumnInfo{
+               "name": {Column: "sc.name", Checker: nil},
+       }
+       if _, ok := inf.Params["orderby"]; !ok {
+               inf.Params["orderby"] = "name"
+       }
+       where, orderBy, pagination, queryValues, errs := 
dbhelpers.BuildWhereAndOrderByAndPagination(inf.Params, queryParamsToQueryCols)
+       if len(errs) > 0 {
+               api.HandleErr(w, r, tx.Tx, http.StatusBadRequest, 
util.JoinErrs(errs), nil)
+               return
+       }
+
+       selectQuery := "SELECT name, description, last_updated FROM 
server_capability sc"
+       query := selectQuery + where + orderBy + pagination
+       rows, err := tx.NamedQuery(query, queryValues)
+       if err != nil {
+               api.HandleErr(w, r, tx.Tx, http.StatusInternalServerError, nil, 
fmt.Errorf("server capability read: error getting server capability(ies): %w", 
err))
+               return
+       }
+       defer log.Close(rows, "unable to close DB connection")
+
+       scList := []tc.ServerCapabilityV4{}
+       for rows.Next() {
+               if err = rows.Scan(&sc.Name, &sc.Description, &sc.LastUpdated); 
err != nil {
+                       api.HandleErr(w, r, tx.Tx, 
http.StatusInternalServerError, nil, fmt.Errorf("error getting server 
capability(ies): %w", err))
+                       return
+               }
+               scList = append(scList, sc)
+       }
+
+       api.WriteResp(w, r, scList)
+       return
+}
+
+func UpdateServerCapability(w http.ResponseWriter, r *http.Request) {
+       inf, userErr, sysErr, errCode := api.NewInfo(r, nil, nil)
+       if userErr != nil || sysErr != nil {
+               api.HandleErr(w, r, inf.Tx.Tx, errCode, userErr, sysErr)
+               return
+       }
+       defer inf.Close()
+
+       tx := inf.Tx.Tx
+       sc, readValErr := readAndValidateJsonStruct(r)
+       if readValErr != nil {
+               api.HandleErr(w, r, tx, http.StatusBadRequest, readValErr, nil)
+               return
+       }
+
+       requestedName := inf.Params["name"]
+       // check if the entity was already updated
+       userErr, sysErr, errCode = api.CheckIfUnModifiedByName(r.Header, 
inf.Tx, requestedName, "server_capability")
+       if userErr != nil || sysErr != nil {
+               api.HandleErr(w, r, tx, errCode, userErr, sysErr)
+               return
+       }
+
+       //update name and description of a capability
+       query := `UPDATE server_capability sc SET
+               name = $1,
+               description = $2
+       WHERE sc.name = $3
+       RETURNING sc.name, sc.description, sc.last_updated`
+
+       err := tx.QueryRow(query, sc.Name, sc.Description, 
requestedName).Scan(&sc.Name, &sc.Description, &sc.LastUpdated)
+       if err != nil {
+               if err == sql.ErrNoRows {
+                       api.HandleErr(w, r, tx, http.StatusBadRequest, err, nil)
+                       return
+               }
+               usrErr, sysErr, code := api.ParseDBError(err)
+               api.HandleErr(w, r, tx, code, usrErr, sysErr)
+               return
+       }
+       alerts := tc.CreateAlerts(tc.SuccessLevel, "server capability was 
updated")
+       api.WriteAlertsObj(w, r, http.StatusOK, alerts, sc)
+       return
+}
+
+func CreateServerCapability(w http.ResponseWriter, r *http.Request) {
+       inf, userErr, sysErr, errCode := api.NewInfo(r, nil, nil)
+       if userErr != nil || sysErr != nil {
+               api.HandleErr(w, r, inf.Tx.Tx, errCode, userErr, sysErr)
+               return
+       }
+       defer inf.Close()
+       tx := inf.Tx.Tx
+
+       sc, readValErr := readAndValidateJsonStruct(r)
+       if readValErr != nil {
+               api.HandleErr(w, r, tx, http.StatusBadRequest, readValErr, nil)
+               return
+       }
+
+       // check if capability already exists
+       var count int
+       err := tx.QueryRow("SELECT count(*) from server_capability where name = 
$1", sc.Name).Scan(&count)
+       if err != nil {
+               api.HandleErr(w, r, tx, http.StatusInternalServerError, nil, 
fmt.Errorf("error: %w, when checking if server capability with name %s exists", 
err, sc.Name))
+               return
+       }
+       if count == 1 {
+               api.HandleErr(w, r, tx, http.StatusBadRequest, 
fmt.Errorf("server_capability name '%s' already exists.", sc.Name), nil)
+               return
+       }
+
+       // create server capability
+       query := `INSERT INTO server_capability (name, description) VALUES ($1, 
$2) RETURNING last_updated`
+       err = tx.QueryRow(query, sc.Name, sc.Description).Scan(&sc.LastUpdated)
+       if err != nil {
+               if err == sql.ErrNoRows {
+                       api.HandleErr(w, r, tx, http.StatusBadRequest, err, nil)
+                       return
+               }
+               usrErr, sysErr, code := api.ParseDBError(err)
+               api.HandleErr(w, r, tx, code, usrErr, sysErr)
+               return
+       }
+       alerts := tc.CreateAlerts(tc.SuccessLevel, "server capability was 
created.")
+       w.Header().Set("Location", 
fmt.Sprintf("/api/%d.%d/server_capabilities$?name=%s", inf.Version.Major, 
inf.Version.Minor, sc.Name))

Review Comment:
   The `$` in here makes this incorrect; I suspect you copy/pasted from the 
handler's route definition?



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to