This is an automated email from the ASF dual-hosted git repository.
rshah pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/trafficcontrol.git
The following commit(s) were added to refs/heads/master by this push:
new 7ce9214315 Fixes ProfileParameters V5 apis to respond with RFC3339
date/time Format (#7738)
7ce9214315 is described below
commit 7ce921431500b7fd0c567c5d0726c35ca7f78f25
Author: Jagan Parthiban <[email protected]>
AuthorDate: Thu Aug 24 07:29:42 2023 +0530
Fixes ProfileParameters V5 apis to respond with RFC3339 date/time Format
(#7738)
* RFC3339 Changes for ProfileParameters. Fixes
https://github.com/apache/trafficcontrol/issues/7737
* CHANGELOG.md entry added
* Fixed PR review comments
* Fixed PR review comments
* Remove strconv function and use APIInfo
---
CHANGELOG.md | 1 +
docs/source/api/v5/profileparameters.rst | 79 ++++--
lib/go-tc/parameters.go | 33 +++
lib/go-tc/profile_parameters.go | 36 +++
traffic_ops/testing/api/v5/cdn_locks_test.go | 2 +-
.../testing/api/v5/profile_parameters_test.go | 2 +-
traffic_ops/testing/api/v5/traffic_control_test.go | 2 +-
.../traffic_ops_golang/dbhelpers/db_helpers.go | 15 +
.../profileparameter/profile_parameters.go | 302 +++++++++++++++++++++
traffic_ops/traffic_ops_golang/routing/routes.go | 8 +-
traffic_ops/v5-client/profile_parameter.go | 4 +-
11 files changed, 458 insertions(+), 26 deletions(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 16eaf2d80f..1a46e6c808 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -77,6 +77,7 @@ The format is based on [Keep a
Changelog](http://keepachangelog.com/en/1.0.0/).
- [#7742](https://github.com/apache/trafficcontrol/pull/7742) *Traffic Ops*
Changed api tests to supply the absolute path of certs.
### Fixed
+- [#7738](https://github.com/apache/trafficcontrol/pull/7738) *Traffic Ops*
Fixes ProfileParameters V5 apis to respond with RFC3339 date/time Format
- [#7730](https://github.com/apache/trafficcontrol/pull/7730) *Traffic
Monitor* Fixed the panic seen in TM when `plugin.system_stats.timestamp_ms`
appears as float and not string.
- [#4393](https://github.com/apache/trafficcontrol/issues/4393) *Traffic Ops*
Fixed the error code and alert structure when TO is queried for a delivery
service with no ssl keys.
- [#7690](https://github.com/apache/trafficcontrol/pull/7690) *Traffic Ops*
Fixes Logs V5 api to respond with RFC3339 timestamps.
diff --git a/docs/source/api/v5/profileparameters.rst
b/docs/source/api/v5/profileparameters.rst
index 58e184d3ae..efdd82a354 100644
--- a/docs/source/api/v5/profileparameters.rst
+++ b/docs/source/api/v5/profileparameters.rst
@@ -52,7 +52,7 @@ Request Structure
Response Structure
------------------
-:lastUpdated: The date and time at which this
:term:`Profile`/:term:`Parameter` association was last modified, in
:ref:`non-rfc-datetime`
+:lastUpdated: The date and time at which this
:term:`Profile`/:term:`Parameter` association was last modified, in :rfc:`3339`
:parameter: The :ref:`parameter-id` of a :term:`Parameter` assigned to
``profile``
:profile: The :ref:`profile-name` of the :term:`Profile` to which the
:term:`Parameter` identified by ``parameter`` is assigned
@@ -73,12 +73,12 @@ Response Structure
{ "response": [
{
- "lastUpdated": "2018-12-05 17:50:49+00",
+ "lastUpdated": "2018-12-05T21:44:14.410503+05:30",
"profile": "GLOBAL",
"parameter": 4
},
{
- "lastUpdated": "2018-12-05 17:50:49+00",
+ "lastUpdated": "2018-12-05T21:44:14.410503+05:30",
"profile": "GLOBAL",
"parameter": 5
}
@@ -152,7 +152,7 @@ Array Format
Response Structure
------------------
-:lastUpdated: The date and time at which the :term:`Profile`/:term:`Parameter`
assignment was last modified, in :ref:`non-rfc-datetime`
+:lastUpdated: The date and time at which the :term:`Profile`/:term:`Parameter`
assignment was last modified, in :rfc:`3339`
:parameter: :ref:`parameter-name` of the :term:`Parameter` which is assigned
to ``profile``
:parameterId: The :ref:`parameter-id` of the assigned :term:`Parameter`
:profile: :ref:`profile-name` of the :term:`Profile` to which the
:term:`Parameter` is assigned
@@ -173,16 +173,61 @@ Response Structure
Date: Mon, 10 Dec 2018 13:50:11 GMT
Content-Length: 166
- { "alerts": [
- {
- "text": "profileParameter was created.",
- "level": "success"
- }
- ],
- "response": {
- "lastUpdated": null,
- "profile": null,
- "profileId": 18,
- "parameter": null,
- "parameterId": 1
- }}
+ {
+ "alerts": [
+ {
+ "text": "All Requested ProfileParameters were created.",
+ "level": "success"
+ }
+ ],
+ "response": [
+ {
+ "lastUpdated": "2018-12-05T21:44:14.410503+05:30",
+ "profile": "testProfile1",
+ "profileId": 18,
+ "parameter": "testParam1",
+ "parameterId": 1
+ }
+ ]
+ }
+
+.. code-block:: http
+ :caption: Response Example - Array Format
+
+ HTTP/1.1 200 OK
+ Access-Control-Allow-Credentials: true
+ Access-Control-Allow-Headers: Origin, X-Requested-With, Content-Type,
Accept, Set-Cookie, Cookie
+ Access-Control-Allow-Methods: POST,GET,OPTIONS,PUT,DELETE
+ Access-Control-Allow-Origin: *
+ Content-Type: application/json
+ Set-Cookie: mojolicious=...; Path=/; Expires=Mon, 18 Nov 2019 17:40:54
GMT; Max-Age=3600; HttpOnly
+ Whole-Content-Sha512:
eDmIwlzX44fZdxLRPHMNa8aoGAK5fQv9Y70A2eeQHfEkliU4evwcsQ4WeHcH0l3/wPTGlpyC0gwLo8LQQpUxWQ==
+ X-Server-Name: traffic_ops_golang/
+ Date: Mon, 10 Dec 2018 13:50:11 GMT
+ Content-Length: 166
+
+
+ {
+ "alerts": [
+ {
+ "text": "All Requested Profile_Parameters were created.",
+ "level": "success"
+ }
+ ],
+ "response": [
+ {
+ "lastUpdated": "2018-12-05T21:44:14.410503+05:30",
+ "profile": "test1",
+ "profileId": 17,
+ "parameter": "tm.toolname",
+ "parameterId": 1091
+ },
+ {
+ "lastUpdated": "2018-12-05T21:44:14.410503+05:30",
+ "profile": "test2",
+ "profileId": 18,
+ "parameter": "maxRevalDurationDays",
+ "parameterId": 1091
+ }
+ ]
+ }
\ No newline at end of file
diff --git a/lib/go-tc/parameters.go b/lib/go-tc/parameters.go
index 57cce3206f..8b6a84aa5f 100644
--- a/lib/go-tc/parameters.go
+++ b/lib/go-tc/parameters.go
@@ -27,6 +27,7 @@ import (
"fmt"
"strconv"
"strings"
+ "time"
"github.com/apache/trafficcontrol/lib/go-util"
@@ -295,6 +296,16 @@ type ProfileParametersNullable struct {
Parameter *int `json:"parameter" db:"parameter_id"`
}
+// ProfileParametersNullableV5 is the latest minor version of the major
version 5
+type ProfileParametersNullableV5 ProfileParametersNullableV50
+
+// ProfileParametersNullableV50 is an object of the form returned by the
Traffic Ops /profileparameters endpoint.
+type ProfileParametersNullableV50 struct {
+ LastUpdated *time.Time `json:"lastUpdated" db:"last_updated"`
+ Profile *string `json:"profile" db:"profile"`
+ Parameter *int `json:"parameter" db:"parameter_id"`
+}
+
// ProfileParametersNullableResponse is the structure of a response from
// Traffic Ops to GET requests made to its /profileparameters API endpoint.
//
@@ -315,6 +326,18 @@ type ProfileParam struct {
LastUpdated *TimeNoMod `json:"lastUpdated"`
}
+// ProfileParamV5 is the latest minor version of the major version 5
+type ProfileParamV5 ProfileParamV50
+
+// ProfileParamV50 is a relationship between a Profile and some Parameter
+// assigned to it as it appears in the Traffic Ops API's responses to the
+// /profileparameters endpoint.
+type ProfileParamV50 struct {
+ Parameter int `json:"parameter"`
+ Profile string `json:"profile"`
+ LastUpdated *time.Time `json:"lastUpdated"`
+}
+
// ProfileParameterCreationRequest is the type of data accepted by Traffic
// Ops as payloads in POST requests to its /profileparameters endpoint.
type ProfileParameterCreationRequest struct {
@@ -329,6 +352,16 @@ type ProfileParametersAPIResponse struct {
Alerts
}
+// ProfileParametersAPIResponseV5 is the latest minor version of the major
version 5
+type ProfileParametersAPIResponseV5 ProfileParametersAPIResponseV50
+
+// ProfileParametersAPIResponseV50 is the type of a response from Traffic Ops
to
+// requests made to its /profileparameters endpoint.
+type ProfileParametersAPIResponseV50 struct {
+ Response []ProfileParamV5 `json:"response"`
+ Alerts
+}
+
// ProfileExportImportParameterNullable is an object of the form used by
Traffic Ops
// to represent parameters for exported and imported profiles.
type ProfileExportImportParameterNullable struct {
diff --git a/lib/go-tc/profile_parameters.go b/lib/go-tc/profile_parameters.go
index f4ff9d7070..7cbef1ccc8 100644
--- a/lib/go-tc/profile_parameters.go
+++ b/lib/go-tc/profile_parameters.go
@@ -19,6 +19,8 @@ package tc
* under the License.
*/
+import "time"
+
// ProfileParametersResponse is the type of the response from Traffic Ops to
// GET requests made to its /profileparameters API endpoint.
type ProfileParametersResponse struct {
@@ -59,3 +61,37 @@ type ProfileParameterNullable struct {
Parameter *string `json:"parameter" db:"parameter"`
ParameterID *int `json:"parameterId" db:"parameter_id"`
}
+
+// ProfileParametersResponseV5 is the type of the response from Traffic Ops to
+// GET requests made to its /profileparameters API endpoint.
+type ProfileParametersResponseV5 struct {
+ Response []ProfileParameterV5 `json:"response"`
+ Alerts
+}
+
+// ProfileParameterResponseV5 is a single ProfileParameter response for Create
to
+// depict what changed.
+// swagger:response ProfileParameterResponse
+// in: body
+type ProfileParameterResponseV5 struct {
+ // in: body
+ Response ProfileParameterV5 `json:"response"`
+ Alerts
+}
+
+// ProfileParameterV5 is the latest minor version of the major version 5
+type ProfileParameterV5 ProfileParameterV50
+
+// ProfileParameterV50 is a representation of a relationship between a
Parameter
+// and a Profile to which it is assigned.
+//
+// Note that not all unique identifiers for each represented object in this
+// relationship structure are guaranteed to be populated by the Traffic Ops
+// API.
+type ProfileParameterV50 struct {
+ LastUpdated time.Time `json:"lastUpdated"`
+ Profile string `json:"profile"`
+ ProfileID int `json:"profileId"`
+ Parameter string `json:"parameter"`
+ ParameterID int `json:"parameterId"`
+}
diff --git a/traffic_ops/testing/api/v5/cdn_locks_test.go
b/traffic_ops/testing/api/v5/cdn_locks_test.go
index 583d22cb41..04e36a2293 100644
--- a/traffic_ops/testing/api/v5/cdn_locks_test.go
+++ b/traffic_ops/testing/api/v5/cdn_locks_test.go
@@ -326,7 +326,7 @@ func TestCDNLocks(t *testing.T) {
"profileId": GetProfileID(t,
"EDGEInCDN2")(),
"parameterId":
GetParameterID(t, "CONFIG proxy.config.admin.user_id", "records.config",
"STRING ats")(),
},
- Expectations:
utils.CkRequest(utils.NoError(), utils.HasStatus(http.StatusOK)),
+ Expectations:
utils.CkRequest(utils.NoError(), utils.HasStatus(http.StatusCreated)),
},
"FORBIDDEN when ADMIN USER DOESNT OWN LOCK": {
ClientSession: TOSession,
diff --git a/traffic_ops/testing/api/v5/profile_parameters_test.go
b/traffic_ops/testing/api/v5/profile_parameters_test.go
index d1bb2576a9..16976a8ef3 100644
--- a/traffic_ops/testing/api/v5/profile_parameters_test.go
+++ b/traffic_ops/testing/api/v5/profile_parameters_test.go
@@ -68,7 +68,7 @@ func TestProfileParameters(t *testing.T) {
},
},
},
- Expectations:
utils.CkRequest(utils.NoError(), utils.HasStatus(http.StatusOK)),
+ Expectations:
utils.CkRequest(utils.NoError(), utils.HasStatus(http.StatusCreated)),
},
"BAD REQUEST when INVALID PROFILEID and
PARAMETERID": {
ClientSession: TOSession,
diff --git a/traffic_ops/testing/api/v5/traffic_control_test.go
b/traffic_ops/testing/api/v5/traffic_control_test.go
index 4d9db708b5..6fa619b368 100644
--- a/traffic_ops/testing/api/v5/traffic_control_test.go
+++ b/traffic_ops/testing/api/v5/traffic_control_test.go
@@ -41,7 +41,7 @@ type TrafficControl struct {
Origins []tc.Origin
`json:"origins"`
Profiles []tc.Profile
`json:"profiles"`
Parameters []tc.Parameter
`json:"parameters"`
- ProfileParameters []tc.ProfileParameter
`json:"profileParameters"`
+ ProfileParameters
[]tc.ProfileParameterV5 `json:"profileParameters"`
PhysLocations []tc.PhysLocationV5
`json:"physLocations"`
Regions []tc.RegionV5
`json:"regions"`
Roles []tc.RoleV4
`json:"roles"`
diff --git a/traffic_ops/traffic_ops_golang/dbhelpers/db_helpers.go
b/traffic_ops/traffic_ops_golang/dbhelpers/db_helpers.go
index c70056279b..464502c2d2 100644
--- a/traffic_ops/traffic_ops_golang/dbhelpers/db_helpers.go
+++ b/traffic_ops/traffic_ops_golang/dbhelpers/db_helpers.go
@@ -2304,3 +2304,18 @@ func DeleteCoordinate(tx *sql.Tx, cacheGroupID int,
coordinateID int) error {
}
return nil
}
+
+// ProfileParameterExists confirms whether the ProfileParameter exists, and an
error (if one occurs).
+func ProfileParameterExists(tx *sql.Tx, profileID string, parameterID string)
(bool, error) {
+ var count int
+ if err := tx.QueryRow("SELECT count(*) FROM profile_parameter WHERE
profile=$1 and parameter=$2", profileID, parameterID).Scan(&count); err != nil {
+ return false, fmt.Errorf("error getting profile_parameter info:
%w", err)
+ }
+ if count == 0 {
+ return false, nil
+ }
+ if count != 1 {
+ return false, fmt.Errorf("getting profile_parameter info -
expected row count: 1, actual: %d", count)
+ }
+ return true, nil
+}
diff --git
a/traffic_ops/traffic_ops_golang/profileparameter/profile_parameters.go
b/traffic_ops/traffic_ops_golang/profileparameter/profile_parameters.go
index 85846682f3..507f806e9a 100644
--- a/traffic_ops/traffic_ops_golang/profileparameter/profile_parameters.go
+++ b/traffic_ops/traffic_ops_golang/profileparameter/profile_parameters.go
@@ -20,16 +20,23 @@ package profileparameter
*/
import (
+ "database/sql"
+ "encoding/json"
"errors"
+ "fmt"
+ "io"
"net/http"
+ "reflect"
"strconv"
"time"
+ "github.com/apache/trafficcontrol/lib/go-log"
"github.com/apache/trafficcontrol/lib/go-tc"
"github.com/apache/trafficcontrol/lib/go-tc/tovalidate"
"github.com/apache/trafficcontrol/lib/go-util"
"github.com/apache/trafficcontrol/traffic_ops/traffic_ops_golang/api"
"github.com/apache/trafficcontrol/traffic_ops/traffic_ops_golang/dbhelpers"
+
"github.com/apache/trafficcontrol/traffic_ops/traffic_ops_golang/util/ims"
validation "github.com/go-ozzo/ozzo-validation"
)
@@ -212,3 +219,298 @@ func deleteQuery() string {
WHERE profile=:profile_id and parameter=:parameter_id`
return query
}
+
+func GetProfileParameter(w http.ResponseWriter, r *http.Request) {
+ var runSecond bool
+ var maxTime time.Time
+ 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{
+ "profileId": {Column: "pp.profile"},
+ "parameterId": {Column: "pp.parameter"},
+ "lastUpdated": {Column: "pp.last_updated"},
+ }
+ if _, ok := inf.Params["orderby"]; !ok {
+ inf.Params["orderby"] = "parameter"
+ }
+ 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
+ }
+
+ if inf.Config.UseIMS {
+ runSecond, maxTime = ims.TryIfModifiedSinceQuery(tx, r.Header,
queryValues, selectMaxLastUpdatedQuery(where))
+ if !runSecond {
+ log.Debugln("IMS HIT")
+ api.AddLastModifiedHdr(w, maxTime)
+ w.WriteHeader(http.StatusNotModified)
+ return
+ }
+ log.Debugln("IMS MISS")
+ } else {
+ log.Debugln("Non IMS request")
+ }
+
+ 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("Profile Parameter read: error getting Profile Parameter(s): %w",
err))
+ return
+ }
+ defer log.Close(rows, "unable to close DB connection")
+
+ profileParams := tc.ProfileParametersNullableV5{}
+ profileParamsList := []tc.ProfileParametersNullableV5{}
+ for rows.Next() {
+ if err = rows.Scan(&profileParams.LastUpdated,
&profileParams.Parameter, &profileParams.Profile); err != nil {
+ api.HandleErr(w, r, tx.Tx,
http.StatusInternalServerError, nil, fmt.Errorf("error getting profile
parameter(s): %w", err))
+ return
+ }
+
+ profileParamsList = append(profileParamsList, profileParams)
+ }
+
+ api.WriteResp(w, r, profileParamsList)
+ return
+}
+
+func CreateProfileParameter(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
+
+ body, err := io.ReadAll(r.Body)
+ if err != nil {
+ api.HandleErr(w, r, tx, http.StatusBadRequest,
errors.New("error reading request body"), nil)
+ return
+ }
+ defer r.Body.Close()
+
+ // Initial Unmarshal to validate request body
+ var data interface{}
+ err = json.Unmarshal(body, &data)
+ if err != nil {
+ api.HandleErr(w, r, tx, http.StatusBadRequest,
errors.New("invalid request format"), nil)
+ return
+ }
+
+ // This code block decides if the request body is a slice of parameters
or a single object.
+ var profileParams []tc.ProfileParameterCreationRequest
+ switch reflect.TypeOf(data).Kind() {
+ case reflect.Slice:
+ if err := json.Unmarshal(body, &profileParams); err != nil {
+ api.HandleErr(w, r, tx, http.StatusBadRequest,
errors.New("error unmarshalling slice"), nil)
+ return
+ }
+ case reflect.Map:
+ // If it is a single object it is still converted to a slice
for code simplicity.
+ var profileParam tc.ProfileParameterCreationRequest
+ if err := json.Unmarshal(body, &profileParam); err != nil {
+ api.HandleErr(w, r, tx, http.StatusBadRequest,
errors.New("error unmarshalling single object"), nil)
+ return
+ }
+ profileParams = append(profileParams, profileParam)
+ default:
+ api.HandleErr(w, r, tx, http.StatusBadRequest,
errors.New("invalid request format"), nil)
+ return
+ }
+
+ // Validate all objects of the every profile parameter from the request
slice
+ for _, profileParameter := range profileParams {
+ readValErr := validateRequestProfileParameter(profileParameter)
+ if readValErr != nil {
+ api.HandleErr(w, r, tx, http.StatusBadRequest,
readValErr, nil)
+ return
+ }
+ }
+
+ // Check user Permissions on all Profiles requested
+ for _, profileParameter := range profileParams {
+ cdnName, err := dbhelpers.GetCDNNameFromProfileID(tx,
profileParameter.ProfileID)
+ if err != nil {
+ api.HandleErr(w, r, tx, http.StatusInternalServerError,
err, nil)
+ return
+ }
+ userErr, sysErr, errCode =
dbhelpers.CheckIfCurrentUserCanModifyCDN(tx, string(cdnName), inf.User.UserName)
+ if userErr != nil || sysErr != nil {
+ api.HandleErr(w, r, tx, errCode, userErr, sysErr)
+ return
+ }
+ }
+
+ // Check if any of the profile parameter from the request slice already
exists
+ for _, profileParameter := range profileParams {
+ var count int
+ err = tx.QueryRow("SELECT count(*) from profile_parameter where
profile = $1 and parameter = $2", profileParameter.ProfileID,
profileParameter.ParameterID).Scan(&count)
+ if err != nil {
+ api.HandleErr(w, r, tx, http.StatusInternalServerError,
nil, fmt.Errorf("error: %w, when checking if profile parameter with profile_id
%d and parameter_id %d exists", err, profileParameter.ProfileID,
profileParameter.ParameterID))
+ return
+ }
+ if count == 1 {
+ api.HandleErr(w, r, tx, http.StatusBadRequest,
fmt.Errorf("profile parameter with profile_id %d and parameter_id %d already
exists", profileParameter.ProfileID, profileParameter.ParameterID), nil)
+ return
+ }
+ }
+
+ // Create all profile parameters from the request slice
+ var objProfileParams []tc.ProfileParameterV5
+ for _, profileParameter := range profileParams {
+ query := `
+ INSERT INTO profile_parameter (
+ profile,
+ parameter
+ ) VALUES (
+ $1, $2
+ ) RETURNING profile, parameter, last_updated
+`
+ var objProfileParam tc.ProfileParameterV5
+ err = tx.QueryRow(
+ query,
+ profileParameter.ProfileID,
+ profileParameter.ParameterID,
+ ).Scan(
+ &objProfileParam.ProfileID,
+ &objProfileParam.ParameterID,
+ &objProfileParam.LastUpdated,
+ )
+
+ if err != nil {
+ if errors.Is(err, sql.ErrNoRows) {
+ api.HandleErr(w, r, tx,
http.StatusInternalServerError, fmt.Errorf("error: %w in profile_parameter with
with profile_id %d and parameter_id %d", err, profileParameter.ProfileID,
profileParameter.ParameterID), nil)
+ return
+ }
+ usrErr, sysErr, code := api.ParseDBError(err)
+ api.HandleErr(w, r, tx, code, usrErr, sysErr)
+ return
+ }
+
+ // Fetch the Profile Name from ID to insert in type
ProfileParameterV5
+ profileName, ok, err :=
dbhelpers.GetProfileNameFromID(profileParameter.ProfileID, tx)
+ if err != nil {
+ api.HandleErr(w, r, inf.Tx.Tx,
http.StatusInternalServerError, nil, errors.New("getting profile name from id:
"+err.Error()))
+ return
+ } else if !ok {
+ api.HandleErr(w, r, inf.Tx.Tx, http.StatusNotFound,
errors.New("profile not found"), nil)
+ return
+ }
+
+ // Fetch the Parameter Name from ID to insert in type
ProfileParameterV5
+ parameterName, ok, err := dbhelpers.GetParamNameByID(tx,
profileParameter.ParameterID)
+ if err != nil {
+ api.HandleErr(w, r, inf.Tx.Tx,
http.StatusInternalServerError, nil, errors.New("getting parameter name from
id: "+err.Error()))
+ return
+ } else if !ok {
+ api.HandleErr(w, r, inf.Tx.Tx, http.StatusNotFound,
errors.New("parameter not found"), nil)
+ return
+ }
+
+ objProfileParam.Profile = profileName
+ objProfileParam.Parameter = parameterName
+ objProfileParams = append(objProfileParams, objProfileParam)
+ }
+ alerts := tc.CreateAlerts(tc.SuccessLevel, "All Requested
ProfileParameters were created.")
+ api.WriteAlertsObj(w, r, http.StatusCreated, alerts, objProfileParams)
+ return
+
+}
+
+func DeleteProfileParameter(w http.ResponseWriter, r *http.Request) {
+ inf, userErr, sysErr, errCode := api.NewInfo(r, nil,
[]string{"profileId"})
+ tx := inf.Tx.Tx
+ if userErr != nil || sysErr != nil {
+ api.HandleErr(w, r, tx, errCode, userErr, sysErr)
+ return
+ }
+ defer inf.Close()
+
+ profileID := inf.Params["profileId"]
+ parameterID := inf.Params["parameterId"]
+ intProfileID := inf.IntParams["profileId"]
+
+ if profileID == "" || parameterID == "" {
+ api.HandleErr(w, r, tx, http.StatusBadRequest,
fmt.Errorf("couldn't delete Profile_Parameter. profileID & parameterID Cannot
be empty for Delete Operation"), nil)
+ return
+ }
+
+ cdnName, err := dbhelpers.GetCDNNameFromProfileID(tx, intProfileID)
+ if err != nil {
+ api.HandleErr(w, r, tx, http.StatusInternalServerError, err,
nil)
+ return
+ }
+ userErr, sysErr, errCode = dbhelpers.CheckIfCurrentUserCanModifyCDN(tx,
string(cdnName), inf.User.UserName)
+ if userErr != nil || sysErr != nil {
+ api.HandleErr(w, r, tx, errCode, userErr, sysErr)
+ return
+ }
+
+ exists, err := dbhelpers.ProfileParameterExists(tx, profileID,
parameterID)
+ if err != nil {
+ api.HandleErr(w, r, tx, http.StatusInternalServerError, nil,
err)
+ return
+ }
+ if !exists {
+ api.HandleErr(w, r, tx, http.StatusNotFound, fmt.Errorf("no
profile_parameter exists by profile_id: %s & parameter_id: %s", profileID,
parameterID), nil)
+ return
+ }
+
+ res, err := tx.Exec("DELETE FROM profile_parameter AS pp WHERE
pp.profile=$1 and parameter=$2", profileID, parameterID)
+ 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("error determining rows affected for delete profile_parameter: %w",
err))
+ return
+ }
+ if rowsAffected == 0 {
+ api.HandleErr(w, r, tx, http.StatusInternalServerError,
fmt.Errorf("no rows deleted for profile_parameter"), nil)
+ return
+ }
+ alerts := tc.CreateAlerts(tc.SuccessLevel, "profile_parameter"+
+ " was deleted.")
+ api.WriteAlerts(w, r, http.StatusOK, alerts)
+ return
+}
+
+func selectMaxLastUpdatedQuery(where string) string {
+ return `
+ SELECT max(t) from (
+ SELECT max(pp.last_updated) as t FROM profile_parameter pp
+ JOIN profile prof ON prof.id = pp.profile
+ JOIN parameter param ON param.id = pp.parameter ` + where +
+ ` UNION ALL
+ SELECT max(last_updated) as t FROM last_deleted l WHERE
l.table_name = 'profile_parameter'
+ ) as res
+ `
+}
+
+// validateRequestProfileParameter validate the JSON objects
+func validateRequestProfileParameter(profileParameter
tc.ProfileParameterCreationRequest) error {
+ errs := make(map[string]error)
+
+ errs[ProfileIDQueryParam] =
validation.Validate(profileParameter.ProfileID, validation.Required)
+ errs[ParameterIDQueryParam] =
validation.Validate(profileParameter.ParameterID, validation.Required)
+
+ if len(errs) > 0 {
+ var errorSlice []error
+ for _, err := range errs {
+ errorSlice = append(errorSlice, err)
+ }
+ userErr := util.JoinErrs(errorSlice)
+ return userErr
+ }
+ return nil
+}
diff --git a/traffic_ops/traffic_ops_golang/routing/routes.go
b/traffic_ops/traffic_ops_golang/routing/routes.go
index 0a6285cca1..8412bf74f7 100644
--- a/traffic_ops/traffic_ops_golang/routing/routes.go
+++ b/traffic_ops/traffic_ops_golang/routing/routes.go
@@ -438,11 +438,11 @@ func Routes(d ServerData) ([]Route, http.Handler, error) {
{Version: api.Version{Major: 5, Minor: 0}, Method:
http.MethodGet, Path: `profiles/name/{name}/parameters/?$`, Handler:
profileparameter.GetProfileName, RequiredPrivLevel: auth.PrivLevelReadOnly,
RequiredPermissions: []string{"PROFILE:READ", "PARAMETER:READ"}, Authenticated:
Authenticated, Middlewares: nil, ID: 426773783231},
{Version: api.Version{Major: 5, Minor: 0}, Method:
http.MethodPost, Path: `profiles/name/{name}/parameters/?$`, Handler:
profileparameter.PostProfileParamsByName, RequiredPrivLevel:
auth.PrivLevelOperations, RequiredPermissions: []string{"PROFILE:UPDATE",
"PROFILE:READ", "PARAMETER:READ"}, Authenticated: Authenticated, Middlewares:
nil, ID: 435594558231},
{Version: api.Version{Major: 5, Minor: 0}, Method:
http.MethodPost, Path: `profiles/{id}/parameters/?$`, Handler:
profileparameter.PostProfileParamsByID, RequiredPrivLevel:
auth.PrivLevelOperations, RequiredPermissions: []string{"PROFILE:UPDATE",
"PROFILE:READ", "PARAMETER:READ"}, Authenticated: Authenticated, Middlewares:
nil, ID: 41681870831},
- {Version: api.Version{Major: 5, Minor: 0}, Method:
http.MethodGet, Path: `profileparameters/?$`, Handler:
api.ReadHandler(&profileparameter.TOProfileParameter{}), RequiredPrivLevel:
auth.PrivLevelReadOnly, RequiredPermissions: []string{"PROFILE:READ",
"PARAMETER:READ"}, Authenticated: Authenticated, Middlewares: nil, ID:
45060980531},
- {Version: api.Version{Major: 5, Minor: 0}, Method:
http.MethodPost, Path: `profileparameters/?$`, Handler:
api.CreateHandler(&profileparameter.TOProfileParameter{}), RequiredPrivLevel:
auth.PrivLevelOperations, RequiredPermissions: []string{"PROFILE:READ",
"PARAMETER:READ", "PROFILE:UPDATE"}, Authenticated: Authenticated, Middlewares:
nil, ID: 42880969331},
- {Version: api.Version{Major: 5, Minor: 0}, Method:
http.MethodPost, Path: `profileparameter/?$`, Handler:
profileparameter.PostProfileParam, RequiredPrivLevel: auth.PrivLevelOperations,
RequiredPermissions: []string{"PROFILE:READ", "PARAMETER:READ",
"PROFILE:UPDATE"}, Authenticated: Authenticated, Middlewares: nil, ID:
42427531},
{Version: api.Version{Major: 5, Minor: 0}, Method:
http.MethodPost, Path: `parameterprofile/?$`, Handler:
profileparameter.PostParamProfile, RequiredPrivLevel: auth.PrivLevelOperations,
RequiredPermissions: []string{"PROFILE:UPDATE", "PROFILE:READ",
"PARAMETER:READ"}, Authenticated: Authenticated, Middlewares: nil, ID:
408061086131},
- {Version: api.Version{Major: 5, Minor: 0}, Method:
http.MethodDelete, Path: `profileparameters/{profileId}/{parameterId}$`,
Handler: api.DeleteHandler(&profileparameter.TOProfileParameter{}),
RequiredPrivLevel: auth.PrivLevelOperations, RequiredPermissions:
[]string{"PROFILE:UPDATE", "PROFILE:READ", "PARAMETER:READ"}, Authenticated:
Authenticated, Middlewares: nil, ID: 42483952931},
+ {Version: api.Version{Major: 5, Minor: 0}, Method:
http.MethodPost, Path: `profileparameter/?$`, Handler:
profileparameter.PostProfileParam, RequiredPrivLevel: auth.PrivLevelOperations,
RequiredPermissions: []string{"PROFILE:READ", "PARAMETER:READ",
"PROFILE:UPDATE"}, Authenticated: Authenticated, Middlewares: nil, ID:
42427531},
+ {Version: api.Version{Major: 5, Minor: 0}, Method:
http.MethodGet, Path: `profileparameters/?$`, Handler:
profileparameter.GetProfileParameter, RequiredPrivLevel:
auth.PrivLevelReadOnly, RequiredPermissions: []string{"PROFILE:READ",
"PARAMETER:READ"}, Authenticated: Authenticated, Middlewares: nil, ID:
45060980531},
+ {Version: api.Version{Major: 5, Minor: 0}, Method:
http.MethodPost, Path: `profileparameters/?$`, Handler:
profileparameter.CreateProfileParameter, RequiredPrivLevel:
auth.PrivLevelOperations, RequiredPermissions: []string{"PROFILE:READ",
"PARAMETER:READ", "PROFILE:UPDATE"}, Authenticated: Authenticated, Middlewares:
nil, ID: 42880969331},
+ {Version: api.Version{Major: 5, Minor: 0}, Method:
http.MethodDelete, Path: `profileparameters/{profileId}/{parameterId}$`,
Handler: profileparameter.DeleteProfileParameter, RequiredPrivLevel:
auth.PrivLevelOperations, RequiredPermissions: []string{"PROFILE:UPDATE",
"PROFILE:READ", "PARAMETER:READ"}, Authenticated: Authenticated, Middlewares:
nil, ID: 42483952931},
//Tenants
{Version: api.Version{Major: 5, Minor: 0}, Method:
http.MethodGet, Path: `tenants/?$`, Handler:
api.ReadHandler(&apitenant.TOTenant{}), RequiredPrivLevel:
auth.PrivLevelReadOnly, RequiredPermissions: []string{"TENANT:READ"},
Authenticated: Authenticated, Middlewares: nil, ID: 467796781431},
diff --git a/traffic_ops/v5-client/profile_parameter.go
b/traffic_ops/v5-client/profile_parameter.go
index e0adfcbb6e..d0720520b8 100644
--- a/traffic_ops/v5-client/profile_parameter.go
+++ b/traffic_ops/v5-client/profile_parameter.go
@@ -56,8 +56,8 @@ func (to *Session) CreateProfileWithMultipleParameters(pps
tc.PostProfileParam,
}
// GetProfileParameters retrieves associations between Profiles and Parameters.
-func (to *Session) GetProfileParameters(opts RequestOptions)
(tc.ProfileParametersAPIResponse, toclientlib.ReqInf, error) {
- var data tc.ProfileParametersAPIResponse
+func (to *Session) GetProfileParameters(opts RequestOptions)
(tc.ProfileParametersAPIResponseV5, toclientlib.ReqInf, error) {
+ var data tc.ProfileParametersAPIResponseV5
reqInf, err := to.get(apiProfileParameters, opts, &data)
return data, reqInf, err
}