You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@trafficcontrol.apache.org by da...@apache.org on 2018/06/27 14:48:44 UTC

[trafficcontrol] 02/02: Fix TO Go profileparams user err to 4xx not 5xx

This is an automated email from the ASF dual-hosted git repository.

dangogh pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/trafficcontrol.git

commit fe666166f6bf041c51d33ae3ea1e4795e365b176
Author: Robert Butts <ro...@apache.org>
AuthorDate: Tue Jun 26 15:44:57 2018 -0600

    Fix TO Go profileparams user err to 4xx not 5xx
    
    Changes TO Go profileparams to the new api helpers, which include
    validation, implicitly fixing the user response to a proper 4xx.
    
    Fixes #2465
---
 lib/go-tc/parameters.go                            | 189 +++++++++++++++++++--
 traffic_ops/traffic_ops_golang/api/api.go          |  19 +++
 .../traffic_ops_golang/dbhelpers/db_helpers.go     |  71 ++++++++
 .../profileparameter/postparameterprofile.go       |  82 ++-------
 .../profileparameter/postprofileparameter.go       |  86 +++-------
 .../profileparameter/postprofileparametersbyid.go  | 104 ++++--------
 .../postprofileparametersbyname.go                 | 136 ++++-----------
 traffic_ops/traffic_ops_golang/routes.go           |   8 +-
 8 files changed, 373 insertions(+), 322 deletions(-)

diff --git a/lib/go-tc/parameters.go b/lib/go-tc/parameters.go
index c099280..9bbf3b9 100644
--- a/lib/go-tc/parameters.go
+++ b/lib/go-tc/parameters.go
@@ -1,6 +1,18 @@
 package tc
 
-import "encoding/json"
+import (
+	"bytes"
+	"database/sql"
+	"encoding/json"
+	"errors"
+	"fmt"
+	"strconv"
+	"strings"
+
+	"github.com/apache/trafficcontrol/lib/go-util"
+
+	"github.com/lib/pq"
+)
 
 /*
  * Licensed to the Apache Software Foundation (ASF) under one
@@ -61,10 +73,66 @@ type ProfileParameterByName struct {
 }
 
 type ProfileParameterByNamePost struct {
-	ConfigFile string `json:"configFile"`
-	Name       string `json:"name"`
-	Secure     int    `json:"secure"`
-	Value      string `json:"value"`
+	ConfigFile *string `json:"configFile"`
+	Name       *string `json:"name"`
+	Secure     *int    `json:"secure"`
+	Value      *string `json:"value"`
+}
+
+func (p *ProfileParameterByNamePost) Validate(tx *sql.Tx) error {
+	errs := []string{}
+	if p.ConfigFile == nil || *p.ConfigFile == "" {
+		errs = append(errs, "configFile")
+	}
+	if p.Name == nil || *p.Name == "" {
+		errs = append(errs, "name")
+	}
+	if p.Secure == nil {
+		errs = append(errs, "secure")
+	}
+	if p.Value == nil {
+		errs = append(errs, "value")
+	}
+	if len(errs) > 0 {
+		return errors.New("required fields missing: " + strings.Join(errs, ", "))
+	}
+	return nil
+}
+
+// ProfileParametersByNamePost is the object posted to profile/name/parameter endpoints. This object may be posted as either a single JSON object, or an array of objects. Either will unmarshal into this object; a single object will unmarshal into an array of 1 element.
+type ProfileParametersByNamePost []ProfileParameterByNamePost
+
+func (pp *ProfileParametersByNamePost) UnmarshalJSON(bts []byte) error {
+	bts = bytes.TrimLeft(bts, " \n\t\r")
+	if len(bts) == 0 {
+		errors.New("no body")
+	}
+	if bts[0] == '[' {
+		ppArr := []ProfileParameterByNamePost{}
+		err := json.Unmarshal(bts, &ppArr)
+		*pp = ppArr
+		return err
+	}
+	p := ProfileParameterByNamePost{}
+	if err := json.Unmarshal(bts, &p); err != nil {
+		return err
+	}
+	*pp = append(*pp, p)
+	return nil
+}
+
+func (pp *ProfileParametersByNamePost) Validate(tx *sql.Tx) error {
+	errs := []string{}
+	ppArr := ([]ProfileParameterByNamePost)(*pp)
+	for i, profileParam := range ppArr {
+		if err := profileParam.Validate(tx); err != nil {
+			errs = append(errs, "object "+strconv.Itoa(i)+": "+err.Error())
+		}
+	}
+	if len(errs) > 0 {
+		return errors.New("validate errors: " + strings.Join(errs, "; "))
+	}
+	return nil
 }
 
 type ProfileParameterPostRespObj struct {
@@ -79,13 +147,112 @@ type ProfileParameterPostResp struct {
 }
 
 type PostProfileParam struct {
-	ProfileID int64   `json:"profileId"`
-	ParamIDs  []int64 `json:"paramIds"`
-	Replace   bool    `json:"replace"`
+	ProfileID *int64   `json:"profileId"`
+	ParamIDs  *[]int64 `json:"paramIds"`
+	Replace   *bool    `json:"replace"`
+}
+
+func (pp *PostProfileParam) Sanitize(tx *sql.Tx) {
+	if pp.Replace == nil {
+		pp.Replace = util.BoolPtr(false)
+	}
+}
+
+func (pp *PostProfileParam) Validate(tx *sql.Tx) error {
+	pp.Sanitize(tx)
+	errs := []string{}
+	if pp.ProfileID == nil {
+		errs = append(errs, "profileId missing")
+	} else if ok, err := ProfileExists(*pp.ProfileID, tx); err != nil {
+		errs = append(errs, "checking profile ID "+strconv.Itoa(int(*pp.ProfileID))+" existence: "+err.Error())
+	} else if !ok {
+		errs = append(errs, "no profile with ID "+strconv.Itoa(int(*pp.ProfileID))+" exists")
+	}
+	if pp.ParamIDs == nil {
+		errs = append(errs, "paramIds missing")
+	} else if ok, err := ParamsExist(*pp.ParamIDs, tx); err != nil {
+		errs = append(errs, fmt.Sprintf("checking parameter IDs %v existence: "+err.Error(), *pp.ParamIDs))
+	} else if !ok {
+		errs = append(errs, fmt.Sprintf("parameters with IDs %v don't all exist", *pp.ParamIDs))
+	}
+	if len(errs) > 0 {
+		return errors.New("validate errors: " + strings.Join(errs, ", "))
+	}
+	return nil
 }
 
 type PostParamProfile struct {
-	ParamID    int64   `json:"paramId"`
-	ProfileIDs []int64 `json:"profileIds"`
-	Replace    bool    `json:"replace"`
+	ParamID    *int64   `json:"paramId"`
+	ProfileIDs *[]int64 `json:"profileIds"`
+	Replace    *bool    `json:"replace"`
+}
+
+func (pp *PostParamProfile) Sanitize(tx *sql.Tx) {
+	if pp.Replace == nil {
+		pp.Replace = util.BoolPtr(false)
+	}
+}
+
+func (pp *PostParamProfile) Validate(tx *sql.Tx) error {
+	pp.Sanitize(tx)
+
+	errs := []string{}
+	if pp.ParamID == nil {
+		errs = append(errs, "paramId missing")
+	} else if ok, err := ParamExists(*pp.ParamID, tx); err != nil {
+		errs = append(errs, "checking param ID "+strconv.Itoa(int(*pp.ParamID))+" existence: "+err.Error())
+	} else if !ok {
+		errs = append(errs, "no parameter with ID "+strconv.Itoa(int(*pp.ParamID))+" exists")
+	}
+	if pp.ProfileIDs == nil {
+		errs = append(errs, "profileIds missing")
+	} else if ok, err := ProfilesExist(*pp.ProfileIDs, tx); err != nil {
+		errs = append(errs, fmt.Sprintf("checking profiles IDs %v existence: "+err.Error(), *pp.ProfileIDs))
+	} else if !ok {
+		errs = append(errs, fmt.Sprintf("profiles with IDs %v don't all exist", *pp.ProfileIDs))
+	}
+	if len(errs) > 0 {
+		return errors.New("validate errors: " + strings.Join(errs, ", "))
+	}
+	return nil
+}
+
+// ParamExists returns whether a parameter with the given id exists, and any error.
+// TODO move to helper package.
+func ParamExists(id int64, tx *sql.Tx) (bool, error) {
+	count := 0
+	if err := tx.QueryRow(`SELECT count(*) from parameter where id = $1`, id).Scan(&count); err != nil {
+		return false, errors.New("querying param existence from id: " + err.Error())
+	}
+	return count > 0, nil
+}
+
+// ProfilesExist returns whether profiles exist for all the given ids, and any error.
+// TODO move to helper package.
+func ProfilesExist(ids []int64, tx *sql.Tx) (bool, error) {
+	count := 0
+	if err := tx.QueryRow(`SELECT count(*) from profile where id = ANY($1)`, pq.Array(ids)).Scan(&count); err != nil {
+		return false, errors.New("querying profiles existence from id: " + err.Error())
+	}
+	return count == len(ids), nil
+}
+
+// ProfileExists returns whether a profile with the given id exists, and any error.
+// TODO move to helper package.
+func ProfileExists(id int64, tx *sql.Tx) (bool, error) {
+	count := 0
+	if err := tx.QueryRow(`SELECT count(*) from profile where id = $1`, id).Scan(&count); err != nil {
+		return false, errors.New("querying profile existence from id: " + err.Error())
+	}
+	return count > 0, nil
+}
+
+// ParamsExist returns whether parameters exist for all the given ids, and any error.
+// TODO move to helper package.
+func ParamsExist(ids []int64, tx *sql.Tx) (bool, error) {
+	count := 0
+	if err := tx.QueryRow(`SELECT count(*) from parameter where id = ANY($1)`, pq.Array(ids)).Scan(&count); err != nil {
+		return false, errors.New("querying parameters existence from id: " + err.Error())
+	}
+	return count == len(ids), nil
 }
diff --git a/traffic_ops/traffic_ops_golang/api/api.go b/traffic_ops/traffic_ops_golang/api/api.go
index 5310fa5..5194ec0 100644
--- a/traffic_ops/traffic_ops_golang/api/api.go
+++ b/traffic_ops/traffic_ops_golang/api/api.go
@@ -137,6 +137,25 @@ func WriteRespAlert(w http.ResponseWriter, r *http.Request, level tc.AlertLevel,
 	w.Write(respBts)
 }
 
+// WriteRespAlertObj Writes the given alert, and the given response object.
+// This is a helper for the common case; not using this in unusual cases is perfectly acceptable.
+func WriteRespAlertObj(w http.ResponseWriter, r *http.Request, level tc.AlertLevel, msg string, obj interface{}) {
+	resp := struct {
+		tc.Alerts
+		Response interface{} `json:"response"`
+	}{
+		Alerts:   tc.CreateAlerts(level, msg),
+		Response: obj,
+	}
+	respBts, err := json.Marshal(resp)
+	if err != nil {
+		HandleErr(w, r, http.StatusInternalServerError, nil, errors.New("marshalling JSON: "+err.Error()))
+		return
+	}
+	w.Header().Set(tc.ContentType, tc.ApplicationJson)
+	w.Write(respBts)
+}
+
 // IntParams parses integer parameters, and returns map of the given params, or an error if any integer param is not an integer. The intParams may be nil if no integer parameters are required. Note this does not check existence; if an integer paramter is required, it should be included in the requiredParams given to NewInfo.
 // This is a helper for the common case; not using this in unusual cases is perfectly acceptable.
 func IntParams(params map[string]string, intParamNames []string) (map[string]int, error) {
diff --git a/traffic_ops/traffic_ops_golang/dbhelpers/db_helpers.go b/traffic_ops/traffic_ops_golang/dbhelpers/db_helpers.go
index 04dd8ce..d5acc41 100644
--- a/traffic_ops/traffic_ops_golang/dbhelpers/db_helpers.go
+++ b/traffic_ops/traffic_ops_golang/dbhelpers/db_helpers.go
@@ -156,3 +156,74 @@ func AddTenancyCheck(where string, queryValues map[string]interface{}, tenantCol
 
 	return where, queryValues
 }
+
+// GetGlobalParams returns the value of the global param, whether it existed, or any error
+func GetGlobalParam(tx *sql.Tx, name string) (string, bool, error) {
+	return GetParam(tx, name, "global")
+}
+
+// GetParam returns the value of the param, whether it existed, or any error.
+func GetParam(tx *sql.Tx, name string, configFile string) (string, bool, error) {
+	val := ""
+	if err := tx.QueryRow(`select value from parameter where name = $1 and config_file = $2`, name, configFile).Scan(&val); err != nil {
+		if err == sql.ErrNoRows {
+			return "", false, nil
+		}
+		return "", false, errors.New("Error querying global paramter '" + name + "': " + err.Error())
+	}
+	return val, true, nil
+}
+
+// GetDSNameFromID returns the delivery service name, whether it existed, and any error.
+func GetDSNameFromID(tx *sql.Tx, id int) (tc.DeliveryServiceName, bool, error) {
+	name := tc.DeliveryServiceName("")
+	if err := tx.QueryRow(`select xml_id from deliveryservice where id = $1`, id).Scan(&name); err != nil {
+		if err == sql.ErrNoRows {
+			return tc.DeliveryServiceName(""), false, nil
+		}
+		return tc.DeliveryServiceName(""), false, errors.New("querying delivery service name: " + err.Error())
+	}
+	return name, true, nil
+}
+
+// returns returns the delivery service name and cdn, whether it existed, and any error.
+func GetDSNameAndCDNFromID(tx *sql.Tx, id int) (tc.DeliveryServiceName, tc.CDNName, bool, error) {
+	name := tc.DeliveryServiceName("")
+	cdn := tc.CDNName("")
+	if err := tx.QueryRow(`
+SELECT ds.xml_id, cdn.name
+FROM deliveryservice as ds
+JOIN cdn on cdn.id = ds.cdn_id
+WHERE ds.id = $1
+`, id).Scan(&name, &cdn); err != nil {
+		if err == sql.ErrNoRows {
+			return tc.DeliveryServiceName(""), tc.CDNName(""), false, nil
+		}
+		return tc.DeliveryServiceName(""), tc.CDNName(""), false, errors.New("querying delivery service name: " + err.Error())
+	}
+	return name, cdn, true, nil
+}
+
+// GetProfileNameFromID returns the profile's name, whether a profile with ID exists, or any error.
+func GetProfileNameFromID(id int, tx *sql.Tx) (string, bool, error) {
+	name := ""
+	if err := tx.QueryRow(`SELECT name from profile where id = $1`, id).Scan(&name); err != nil {
+		if err == sql.ErrNoRows {
+			return "", false, nil
+		}
+		return "", false, errors.New("querying profile name from id: " + err.Error())
+	}
+	return name, true, nil
+}
+
+// GetProfileIDFromName returns the profile's ID, whether a profile with name exists, or any error.
+func GetProfileIDFromName(name string, tx *sql.Tx) (int, bool, error) {
+	id := 0
+	if err := tx.QueryRow(`SELECT id from profile where name = $1`, name).Scan(&id); err != nil {
+		if err == sql.ErrNoRows {
+			return 0, false, nil
+		}
+		return 0, false, errors.New("querying profile id from name: " + err.Error())
+	}
+	return id, true, nil
+}
diff --git a/traffic_ops/traffic_ops_golang/profileparameter/postparameterprofile.go b/traffic_ops/traffic_ops_golang/profileparameter/postparameterprofile.go
index 84362e7..a5ba2a4 100644
--- a/traffic_ops/traffic_ops_golang/profileparameter/postparameterprofile.go
+++ b/traffic_ops/traffic_ops_golang/profileparameter/postparameterprofile.go
@@ -21,7 +21,6 @@ package profileparameter
 
 import (
 	"database/sql"
-	"encoding/json"
 	"errors"
 	"fmt"
 	"net/http"
@@ -32,79 +31,33 @@ import (
 	"github.com/lib/pq"
 )
 
-func PostParamProfile(db *sql.DB) http.HandlerFunc {
-	return func(w http.ResponseWriter, r *http.Request) {
-		defer r.Body.Close()
-
-		paramProfile := tc.PostParamProfile{}
-		if err := json.NewDecoder(r.Body).Decode(&paramProfile); err != nil {
-			api.HandleErr(w, r, http.StatusBadRequest, errors.New("malformed JSON"), nil)
-			return
-		}
-
-		if ok, err := paramExists(paramProfile.ParamID, db); err != nil {
-			api.HandleErr(w, r, http.StatusInternalServerError, nil, fmt.Errorf("checking param ID %d existence: "+err.Error(), paramProfile.ParamID))
-			return
-		} else if !ok {
-			api.HandleErr(w, r, http.StatusBadRequest, fmt.Errorf("no parameter with ID %d exists", paramProfile.ParamID), nil)
-			return
-		}
-		if ok, err := profilesExist(paramProfile.ProfileIDs, db); err != nil {
-			api.HandleErr(w, r, http.StatusInternalServerError, nil, fmt.Errorf("checking profiles IDs %v existence: "+err.Error(), paramProfile.ProfileIDs))
-			return
-		} else if !ok {
-			api.HandleErr(w, r, http.StatusBadRequest, fmt.Errorf("profiles with IDs %v don't all exist", paramProfile.ProfileIDs), nil)
-			return
-		}
-		if err := insertParameterProfile(paramProfile, db); err != nil {
-			api.HandleErr(w, r, http.StatusInternalServerError, nil, errors.New("posting parameter profile: "+err.Error()))
-			return
-		}
-		// TODO create helper func
-		resp := struct {
-			Response tc.PostParamProfile `json:"response"`
-			tc.Alerts
-		}{paramProfile, tc.CreateAlerts(tc.SuccessLevel, fmt.Sprintf("%d profiles were assigned to the %d parameter", len(paramProfile.ProfileIDs), paramProfile.ParamID))}
-		respBts, err := json.Marshal(resp)
-		if err != nil {
-			api.HandleErr(w, r, http.StatusInternalServerError, nil, errors.New("posting parameter profiles: "+err.Error()))
-			return
-		}
-		w.Header().Set(tc.ContentType, tc.ApplicationJson)
-		w.Write(respBts)
+func PostParamProfile(w http.ResponseWriter, r *http.Request) {
+	inf, userErr, sysErr, errCode := api.NewInfo(r, nil, nil)
+	if userErr != nil || sysErr != nil {
+		api.HandleErr(w, r, errCode, userErr, sysErr)
+		return
 	}
-}
+	defer inf.Close()
 
-func paramExists(id int64, db *sql.DB) (bool, error) {
-	count := 0
-	if err := db.QueryRow(`SELECT count(*) from parameter where id = $1`, id).Scan(&count); err != nil {
-		return false, errors.New("querying param existence from id: " + err.Error())
+	paramProfile := tc.PostParamProfile{}
+	if err := api.Parse(r.Body, inf.Tx.Tx, &paramProfile); err != nil {
+		api.HandleErr(w, r, http.StatusBadRequest, errors.New("parse error: "+err.Error()), nil)
+		return
 	}
-	return count > 0, nil
-}
-
-func profilesExist(ids []int64, db *sql.DB) (bool, error) {
-	count := 0
-	if err := db.QueryRow(`SELECT count(*) from profile where id = ANY($1)`, pq.Array(ids)).Scan(&count); err != nil {
-		return false, errors.New("querying profiles existence from id: " + err.Error())
+	if err := insertParameterProfile(paramProfile, inf.Tx.Tx); err != nil {
+		api.HandleErr(w, r, http.StatusInternalServerError, nil, errors.New("posting parameter profile: "+err.Error()))
+		return
 	}
-	return count == len(ids), nil
+	*inf.CommitTx = true
+	api.WriteRespAlertObj(w, r, tc.SuccessLevel, fmt.Sprintf("%d profiles were assigned to the %d parameter", len(*paramProfile.ProfileIDs), *paramProfile.ParamID), paramProfile)
 }
 
-func insertParameterProfile(post tc.PostParamProfile, db *sql.DB) error {
-	tx, err := db.Begin()
-	if err != nil {
-		return errors.New("beginning transaction: " + err.Error())
-	}
-	commitTx := false
-	defer FinishTx(tx, &commitTx)
-
-	if post.Replace {
+func insertParameterProfile(post tc.PostParamProfile, tx *sql.Tx) error {
+	if *post.Replace {
 		if _, err := tx.Exec(`DELETE FROM profile_parameter WHERE parameter = $1`, post.ParamID); err != nil {
 			return errors.New("deleting old parameter profile: " + err.Error())
 		}
 	}
-
 	q := `
 INSERT INTO profile_parameter (profile, parameter)
 VALUES (unnest($1::int[]), $2)
@@ -113,6 +66,5 @@ ON CONFLICT DO NOTHING;
 	if _, err := tx.Exec(q, pq.Array(post.ProfileIDs), post.ParamID); err != nil {
 		return errors.New("inserting parameter profile: " + err.Error())
 	}
-	commitTx = true
 	return nil
 }
diff --git a/traffic_ops/traffic_ops_golang/profileparameter/postprofileparameter.go b/traffic_ops/traffic_ops_golang/profileparameter/postprofileparameter.go
index b405e59..c1c2c71 100644
--- a/traffic_ops/traffic_ops_golang/profileparameter/postprofileparameter.go
+++ b/traffic_ops/traffic_ops_golang/profileparameter/postprofileparameter.go
@@ -21,7 +21,6 @@ package profileparameter
 
 import (
 	"database/sql"
-	"encoding/json"
 	"errors"
 	"fmt"
 	"net/http"
@@ -32,87 +31,40 @@ import (
 	"github.com/lib/pq"
 )
 
-func PostProfileParam(db *sql.DB) http.HandlerFunc {
-	return func(w http.ResponseWriter, r *http.Request) {
-		defer r.Body.Close()
-
-		profileParam := tc.PostProfileParam{}
-		if err := json.NewDecoder(r.Body).Decode(&profileParam); err != nil {
-			api.HandleErr(w, r, http.StatusBadRequest, errors.New("malformed JSON"), nil)
-			return
-		}
-
-		if ok, err := profileExists(profileParam.ProfileID, db); err != nil {
-			api.HandleErr(w, r, http.StatusInternalServerError, nil, fmt.Errorf("checking profile ID %d existence: "+err.Error(), profileParam.ProfileID))
-			return
-		} else if !ok {
-			api.HandleErr(w, r, http.StatusBadRequest, fmt.Errorf("no profile with ID %d exists", profileParam.ProfileID), nil)
-			return
-		}
-		if ok, err := paramsExist(profileParam.ParamIDs, db); err != nil {
-			api.HandleErr(w, r, http.StatusInternalServerError, nil, fmt.Errorf("checking parameters IDs %v existence: "+err.Error(), profileParam.ParamIDs))
-			return
-		} else if !ok {
-			api.HandleErr(w, r, http.StatusBadRequest, fmt.Errorf("parameters with IDs %v don't all exist", profileParam.ParamIDs), nil)
-			return
-		}
-		if err := insertProfileParameter(profileParam, db); err != nil {
-			api.HandleErr(w, r, http.StatusInternalServerError, nil, errors.New("posting profile parameter: "+err.Error()))
-			return
-		}
-		// TODO create helper func
-		resp := struct {
-			Response tc.PostProfileParam `json:"response"`
-			tc.Alerts
-		}{profileParam, tc.CreateAlerts(tc.SuccessLevel, fmt.Sprintf("%d parameters were assigned to the %d profile", len(profileParam.ParamIDs), profileParam.ProfileID))}
-		respBts, err := json.Marshal(resp)
-		if err != nil {
-			api.HandleErr(w, r, http.StatusInternalServerError, nil, errors.New("posting profile parameters by name: "+err.Error()))
-			return
-		}
-		w.Header().Set(tc.ContentType, tc.ApplicationJson)
-		w.Write(respBts)
+func PostProfileParam(w http.ResponseWriter, r *http.Request) {
+	inf, userErr, sysErr, errCode := api.NewInfo(r, nil, nil)
+	if userErr != nil || sysErr != nil {
+		api.HandleErr(w, r, errCode, userErr, sysErr)
+		return
 	}
-}
+	defer inf.Close()
 
-func profileExists(id int64, db *sql.DB) (bool, error) {
-	count := 0
-	if err := db.QueryRow(`SELECT count(*) from profile where id = $1`, id).Scan(&count); err != nil {
-		return false, errors.New("querying profile existence from id: " + err.Error())
+	profileParam := tc.PostProfileParam{}
+	if err := api.Parse(r.Body, inf.Tx.Tx, &profileParam); err != nil {
+		api.HandleErr(w, r, http.StatusBadRequest, errors.New("parse error: "+err.Error()), nil)
+		return
 	}
-	return count > 0, nil
-}
-
-func paramsExist(ids []int64, db *sql.DB) (bool, error) {
-	count := 0
-	if err := db.QueryRow(`SELECT count(*) from parameter where id = ANY($1)`, pq.Array(ids)).Scan(&count); err != nil {
-		return false, errors.New("querying parameters existence from id: " + err.Error())
+	if err := insertProfileParameter(profileParam, inf.Tx.Tx); err != nil {
+		api.HandleErr(w, r, http.StatusInternalServerError, nil, errors.New("posting profile parameter: "+err.Error()))
+		return
 	}
-	return count == len(ids), nil
+	*inf.CommitTx = true
+	api.WriteRespAlertObj(w, r, tc.SuccessLevel, fmt.Sprintf("%d parameters were assigned to the %d profile", len(*profileParam.ParamIDs), *profileParam.ProfileID), profileParam)
 }
 
-func insertProfileParameter(post tc.PostProfileParam, db *sql.DB) error {
-	tx, err := db.Begin()
-	if err != nil {
-		return errors.New("beginning transaction: " + err.Error())
-	}
-	commitTx := false
-	defer FinishTx(tx, &commitTx)
-
-	if post.Replace {
-		if _, err := tx.Exec(`DELETE FROM profile_parameter WHERE profile = $1`, post.ProfileID); err != nil {
+func insertProfileParameter(post tc.PostProfileParam, tx *sql.Tx) error {
+	if *post.Replace {
+		if _, err := tx.Exec(`DELETE FROM profile_parameter WHERE profile = $1`, *post.ProfileID); err != nil {
 			return errors.New("deleting old profile parameter: " + err.Error())
 		}
 	}
-
 	q := `
 INSERT INTO profile_parameter (profile, parameter)
 VALUES ($1, unnest($2::int[]))
 ON CONFLICT DO NOTHING;
 `
-	if _, err := tx.Exec(q, post.ProfileID, pq.Array(post.ParamIDs)); err != nil {
+	if _, err := tx.Exec(q, *post.ProfileID, pq.Array(*post.ParamIDs)); err != nil {
 		return errors.New("inserting profile parameter: " + err.Error())
 	}
-	commitTx = true
 	return nil
 }
diff --git a/traffic_ops/traffic_ops_golang/profileparameter/postprofileparametersbyid.go b/traffic_ops/traffic_ops_golang/profileparameter/postprofileparametersbyid.go
index 355b888..e207656 100644
--- a/traffic_ops/traffic_ops_golang/profileparameter/postprofileparametersbyid.go
+++ b/traffic_ops/traffic_ops_golang/profileparameter/postprofileparametersbyid.go
@@ -20,91 +20,47 @@ package profileparameter
  */
 
 import (
-	"bytes"
-	"database/sql"
-	"encoding/json"
 	"errors"
 	"fmt"
-	"io/ioutil"
 	"net/http"
 
 	"github.com/apache/trafficcontrol/lib/go-tc"
 	"github.com/apache/trafficcontrol/traffic_ops/traffic_ops_golang/api"
+	dbhelp "github.com/apache/trafficcontrol/traffic_ops/traffic_ops_golang/dbhelpers"
 )
 
-func PostProfileParamsByID(db *sql.DB) http.HandlerFunc {
-	return func(w http.ResponseWriter, r *http.Request) {
-		defer r.Body.Close()
-		_, intParams, userErr, sysErr, errCode := api.AllParams(r, []string{"id"}, []string{"id"})
-		if userErr != nil || sysErr != nil {
-			api.HandleErr(w, r, errCode, userErr, sysErr)
-			return
-		}
-		bts, err := ioutil.ReadAll(r.Body)
-		if err != nil {
-			api.HandleErr(w, r, http.StatusBadRequest, errors.New("body read failed"), nil)
-			return
-		}
-		bts = bytes.TrimLeft(bts, " \n\t\r")
-		if len(bts) == 0 {
-			api.HandleErr(w, r, http.StatusBadRequest, errors.New("no body"), nil)
-			return
-		}
-		profParams := []tc.ProfileParameterByNamePost{}
-		if bts[0] == '[' {
-			if err := json.Unmarshal(bts, &profParams); err != nil {
-				api.HandleErr(w, r, http.StatusBadRequest, errors.New("malformed JSON"), nil)
-				return
-			}
-		} else {
-			param := tc.ProfileParameterByNamePost{}
-			if err := json.Unmarshal(bts, &param); err != nil {
-				api.HandleErr(w, r, http.StatusInternalServerError, errors.New("posting profile parameters by name: "+err.Error()), nil)
-				return
-			}
-			profParams = append(profParams, param)
-		}
-
-		profileID := intParams["id"]
-		profileName, profileExists, err := getProfileNameFromID(profileID, db)
-		if err != nil {
-			api.HandleErr(w, r, http.StatusInternalServerError, nil, fmt.Errorf("getting profile ID %d: "+err.Error(), profileID))
-			return
-		}
-		if !profileExists {
-			api.HandleErr(w, r, http.StatusBadRequest, fmt.Errorf("no profile with ID %d exists", profileID), nil)
-			return
-		}
+func PostProfileParamsByID(w http.ResponseWriter, r *http.Request) {
+	inf, userErr, sysErr, errCode := api.NewInfo(r, []string{"id"}, []string{"id"})
+	if userErr != nil || sysErr != nil {
+		api.HandleErr(w, r, errCode, userErr, sysErr)
+		return
+	}
+	defer inf.Close()
 
-		insertedObjs, err := insertParametersForProfile(profileName, profParams, db)
-		if err != nil {
-			api.HandleErr(w, r, http.StatusInternalServerError, nil, errors.New("posting profile parameters by name: "+err.Error()))
-			return
-		}
+	profParams := tc.ProfileParametersByNamePost{}
+	if err := api.Parse(r.Body, inf.Tx.Tx, &profParams); err != nil {
+		api.HandleErr(w, r, http.StatusBadRequest, errors.New("parse error: "+err.Error()), nil)
+		return
+	}
 
-		// TODO create helper func
-		resp := struct {
-			Response tc.ProfileParameterPostResp `json:"response"`
-			tc.Alerts
-		}{tc.ProfileParameterPostResp{Parameters: insertedObjs, ProfileName: profileName, ProfileID: profileID}, tc.CreateAlerts(tc.SuccessLevel, "Assign parameters successfully to profile "+profileName)}
-		respBts, err := json.Marshal(resp)
-		if err != nil {
-			api.HandleErr(w, r, http.StatusInternalServerError, nil, errors.New("posting profile parameters by name: "+err.Error()))
-			return
-		}
-		w.Header().Set(tc.ContentType, tc.ApplicationJson)
-		w.Write(respBts)
+	profileID := inf.IntParams["id"]
+	profileName, profileExists, err := dbhelp.GetProfileNameFromID(profileID, inf.Tx.Tx)
+	if err != nil {
+		api.HandleErr(w, r, http.StatusInternalServerError, nil, fmt.Errorf("getting profile ID %d: "+err.Error(), profileID))
+		return
+	}
+	if !profileExists {
+		api.HandleErr(w, r, http.StatusBadRequest, fmt.Errorf("no profile with ID %d exists", profileID), nil)
+		return
 	}
-}
 
-// getProfileIDFromName returns the profile's name, whether a profile with ID exists, or any error.
-func getProfileNameFromID(id int, db *sql.DB) (string, bool, error) {
-	name := ""
-	if err := db.QueryRow(`SELECT name from profile where id = $1`, id).Scan(&name); err != nil {
-		if err == sql.ErrNoRows {
-			return "", false, nil
-		}
-		return "", false, errors.New("querying profile name from id: " + err.Error())
+	insertedObjs, err := insertParametersForProfile(profileName, profParams, inf.Tx.Tx)
+	if err != nil {
+		api.HandleErr(w, r, http.StatusInternalServerError, nil, errors.New("posting profile parameters by name: "+err.Error()))
+		return
 	}
-	return name, true, nil
+
+	resp := tc.ProfileParameterPostResp{Parameters: insertedObjs, ProfileName: profileName, ProfileID: profileID}
+	*inf.CommitTx = true
+	api.WriteRespAlertObj(w, r, tc.SuccessLevel, "Assign parameters successfully to profile "+profileName, resp)
 }
diff --git a/traffic_ops/traffic_ops_golang/profileparameter/postprofileparametersbyname.go b/traffic_ops/traffic_ops_golang/profileparameter/postprofileparametersbyname.go
index 9d6eb87..18c9043 100644
--- a/traffic_ops/traffic_ops_golang/profileparameter/postprofileparametersbyname.go
+++ b/traffic_ops/traffic_ops_golang/profileparameter/postprofileparametersbyname.go
@@ -20,104 +20,51 @@ package profileparameter
  */
 
 import (
-	"bytes"
 	"database/sql"
-	"encoding/json"
 	"errors"
-	"io/ioutil"
 	"net/http"
 
 	"github.com/apache/trafficcontrol/lib/go-tc"
 	"github.com/apache/trafficcontrol/traffic_ops/traffic_ops_golang/api"
+	dbhelp "github.com/apache/trafficcontrol/traffic_ops/traffic_ops_golang/dbhelpers"
 
 	"github.com/lib/pq"
 )
 
-func PostProfileParamsByName(db *sql.DB) http.HandlerFunc {
-	return func(w http.ResponseWriter, r *http.Request) {
-		defer r.Body.Close()
-		params, _, userErr, sysErr, errCode := api.AllParams(r, []string{"name"}, nil)
-		if userErr != nil || sysErr != nil {
-			api.HandleErr(w, r, errCode, userErr, sysErr)
-			return
-		}
-		bts, err := ioutil.ReadAll(r.Body)
-		if err != nil {
-			api.HandleErr(w, r, http.StatusBadRequest, errors.New("body read failed"), nil)
-			return
-		}
-		bts = bytes.TrimLeft(bts, " \n\t\r")
-		if len(bts) == 0 {
-			api.HandleErr(w, r, http.StatusBadRequest, errors.New("no body"), nil)
-			return
-		}
-		profParams := []tc.ProfileParameterByNamePost{}
-		if bts[0] == '[' {
-			if err := json.Unmarshal(bts, &profParams); err != nil {
-				api.HandleErr(w, r, http.StatusBadRequest, errors.New("malformed JSON"), nil)
-				return
-			}
-		} else {
-			param := tc.ProfileParameterByNamePost{}
-			if err := json.Unmarshal(bts, &param); err != nil {
-				api.HandleErr(w, r, http.StatusInternalServerError, errors.New("posting profile parameters by name: "+err.Error()), nil)
-				return
-			}
-			profParams = append(profParams, param)
-		}
-		profileName := params["name"]
-
-		profileID, profileExists, err := getProfileIDFromName(profileName, db)
-		if err != nil {
-			api.HandleErr(w, r, http.StatusInternalServerError, nil, errors.New("getting profile '"+profileName+"' ID: "+err.Error()))
-			return
-		}
-		if !profileExists {
-			api.HandleErr(w, r, http.StatusBadRequest, errors.New("no profile with that name exists"), nil)
-			return
-		}
-
-		insertedObjs, err := insertParametersForProfile(profileName, profParams, db)
-		if err != nil {
-			api.HandleErr(w, r, http.StatusInternalServerError, nil, errors.New("posting profile parameters by name: "+err.Error()))
-			return
-		}
-
-		// TODO create helper func
-		resp := struct {
-			Response tc.ProfileParameterPostResp `json:"response"`
-			tc.Alerts
-		}{tc.ProfileParameterPostResp{Parameters: insertedObjs, ProfileName: profileName, ProfileID: profileID}, tc.CreateAlerts(tc.SuccessLevel, "Assign parameters successfully to profile "+profileName)}
-		respBts, err := json.Marshal(resp)
-		if err != nil {
-			api.HandleErr(w, r, http.StatusInternalServerError, nil, errors.New("posting profile parameters by name: "+err.Error()))
-			return
-		}
-		w.Header().Set(tc.ContentType, tc.ApplicationJson)
-		w.Write(respBts)
+func PostProfileParamsByName(w http.ResponseWriter, r *http.Request) {
+	inf, userErr, sysErr, errCode := api.NewInfo(r, []string{"name"}, nil)
+	if userErr != nil || sysErr != nil {
+		api.HandleErr(w, r, errCode, userErr, sysErr)
+		return
 	}
-}
-
-// getProfileIDFromName returns the profile's ID, whether a profile with name exists, or any error.
-func getProfileIDFromName(name string, db *sql.DB) (int, bool, error) {
-	id := 0
-	if err := db.QueryRow(`SELECT id from profile where name = $1`, name).Scan(&id); err != nil {
-		if err == sql.ErrNoRows {
-			return 0, false, nil
-		}
-		return 0, false, errors.New("querying profile id from name: " + err.Error())
+	defer inf.Close()
+	profParams := tc.ProfileParametersByNamePost{}
+	if err := api.Parse(r.Body, inf.Tx.Tx, &profParams); err != nil {
+		api.HandleErr(w, r, http.StatusBadRequest, errors.New("parse error: "+err.Error()), nil)
+		return
 	}
-	return id, true, nil
+	profileName := inf.Params["name"]
+	profileID, profileExists, err := dbhelp.GetProfileIDFromName(profileName, inf.Tx.Tx)
+	if err != nil {
+		api.HandleErr(w, r, http.StatusInternalServerError, nil, errors.New("getting profile '"+profileName+"' ID: "+err.Error()))
+		return
+	}
+	if !profileExists {
+		api.HandleErr(w, r, http.StatusBadRequest, errors.New("no profile with that name exists"), nil)
+		return
+	}
+	insertedObjs, err := insertParametersForProfile(profileName, profParams, inf.Tx.Tx)
+	if err != nil {
+		api.HandleErr(w, r, http.StatusInternalServerError, nil, errors.New("posting profile parameters by name: "+err.Error()))
+		return
+	}
+	resp := tc.ProfileParameterPostResp{Parameters: insertedObjs, ProfileName: profileName, ProfileID: profileID}
+	*inf.CommitTx = true
+	api.WriteRespAlertObj(w, r, tc.SuccessLevel, "Assign parameters successfully to profile "+profileName, resp)
 }
 
 // insertParametersForProfile returns the PostResp object, because the ID is needed, and the ID must be associated with the real key (name,value,config_file), so we might as well return the whole object.
-func insertParametersForProfile(profileName string, params []tc.ProfileParameterByNamePost, db *sql.DB) ([]tc.ProfileParameterPostRespObj, error) {
-	tx, err := db.Begin()
-	if err != nil {
-		return nil, errors.New("beginning transaction: " + err.Error())
-	}
-	commitTx := false
-	defer FinishTx(tx, &commitTx)
+func insertParametersForProfile(profileName string, params tc.ProfileParametersByNamePost, tx *sql.Tx) ([]tc.ProfileParameterPostRespObj, error) {
 	insertParamsQ := `
 INSERT INTO parameter (name, config_file, value, secure)
 VALUES (unnest($1::text[]), unnest($2::text[]), unnest($3::text[]), unnest($4::bool[]))
@@ -128,10 +75,10 @@ ON CONFLICT(name, config_file, value) DO UPDATE set name=EXCLUDED.name RETURNING
 	paramValues := make([]string, len(params))
 	paramSecures := make([]bool, len(params))
 	for i, param := range params {
-		paramNames[i] = param.Name
-		paramConfigFiles[i] = param.ConfigFile
-		paramValues[i] = param.Value
-		if param.Secure != 0 {
+		paramNames[i] = *param.Name
+		paramConfigFiles[i] = *param.ConfigFile
+		paramValues[i] = *param.Value
+		if *param.Secure != 0 {
 			paramSecures[i] = true
 		}
 	}
@@ -156,7 +103,7 @@ ON CONFLICT(name, config_file, value) DO UPDATE set name=EXCLUDED.name RETURNING
 			secureNum = 1
 		}
 		ids = append(ids, id)
-		insertedObjs = append(insertedObjs, tc.ProfileParameterPostRespObj{ID: id, ProfileParameterByNamePost: tc.ProfileParameterByNamePost{Name: name, ConfigFile: configFile, Value: value, Secure: secureNum}})
+		insertedObjs = append(insertedObjs, tc.ProfileParameterPostRespObj{ID: id, ProfileParameterByNamePost: tc.ProfileParameterByNamePost{Name: &name, ConfigFile: &configFile, Value: &value, Secure: &secureNum}})
 	}
 	insertProfileParamsQ := `
 INSERT INTO profile_parameter (profile, parameter)
@@ -167,18 +114,5 @@ ON CONFLICT DO NOTHING;
 		return nil, errors.New("inserting profile parameters: " + err.Error())
 	}
 
-	commitTx = true
 	return insertedObjs, nil
 }
-
-// FinishTx commits the transaction if commit is true when it's called, otherwise it rolls back the transaction. This is designed to be called in a defer.
-func FinishTx(tx *sql.Tx, commit *bool) {
-	if tx == nil {
-		return
-	}
-	if !*commit {
-		tx.Rollback()
-		return
-	}
-	tx.Commit()
-}
diff --git a/traffic_ops/traffic_ops_golang/routes.go b/traffic_ops/traffic_ops_golang/routes.go
index 789a2ec..2af1464 100644
--- a/traffic_ops/traffic_ops_golang/routes.go
+++ b/traffic_ops/traffic_ops_golang/routes.go
@@ -273,12 +273,12 @@ func Routes(d ServerData) ([]Route, []RawRoute, http.Handler, error) {
 		{1.1, http.MethodGet, `profiles/{id}/unassigned_parameters/?(\.json)?$`, profileparameter.GetUnassigned(d.DB.DB), auth.PrivLevelReadOnly, Authenticated, nil},
 		{1.1, http.MethodGet, `profiles/name/{name}/parameters/?(\.json)?$`, profileparameter.GetProfileName(d.DB.DB), auth.PrivLevelReadOnly, Authenticated, nil},
 		{1.1, http.MethodGet, `parameters/profile/{name}/?(\.json)?$`, profileparameter.GetProfileName(d.DB.DB), auth.PrivLevelReadOnly, Authenticated, nil},
-		{1.1, http.MethodPost, `profiles/name/{name}/parameters/?$`, profileparameter.PostProfileParamsByName(d.DB.DB), auth.PrivLevelOperations, Authenticated, nil},
-		{1.1, http.MethodPost, `profiles/{id}/parameters/?$`, profileparameter.PostProfileParamsByID(d.DB.DB), auth.PrivLevelOperations, Authenticated, nil},
+		{1.1, http.MethodPost, `profiles/name/{name}/parameters/?$`, profileparameter.PostProfileParamsByName, auth.PrivLevelOperations, Authenticated, nil},
+		{1.1, http.MethodPost, `profiles/{id}/parameters/?$`, profileparameter.PostProfileParamsByID, auth.PrivLevelOperations, Authenticated, nil},
 		{1.1, http.MethodGet, `profileparameters/?(\.json)?$`, api.ReadHandler(profileparameter.GetRefType(), d.DB), auth.PrivLevelReadOnly, Authenticated, nil},
 		{1.1, http.MethodPost, `profileparameters/?$`, api.CreateHandler(profileparameter.GetRefType(), d.DB), auth.PrivLevelOperations, Authenticated, nil},
-		{1.1, http.MethodPost, `profileparameter/?$`, profileparameter.PostProfileParam(d.DB.DB), auth.PrivLevelOperations, Authenticated, nil},
-		{1.1, http.MethodPost, `parameterprofile/?$`, profileparameter.PostParamProfile(d.DB.DB), auth.PrivLevelOperations, Authenticated, nil},
+		{1.1, http.MethodPost, `profileparameter/?$`, profileparameter.PostProfileParam, auth.PrivLevelOperations, Authenticated, nil},
+		{1.1, http.MethodPost, `parameterprofile/?$`, profileparameter.PostParamProfile, auth.PrivLevelOperations, Authenticated, nil},
 		{1.1, http.MethodDelete, `profileparameters/{profileId}/{parameterId}$`, api.DeleteHandler(profileparameter.GetRefType(), d.DB), auth.PrivLevelOperations, Authenticated, nil},
 
 		//Tenants