You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@trafficcontrol.apache.org by rs...@apache.org on 2023/06/23 16:19:34 UTC

[trafficcontrol] branch master updated: Update timestamp for Delivery service Request Comment to match RFC3339 format (#7570)

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 b2ca0f2c78 Update timestamp for Delivery service Request Comment to match RFC3339 format (#7570)
b2ca0f2c78 is described below

commit b2ca0f2c78f282886b9ef49513ca296aac4fe35f
Author: Srijeet Chatterjee <30...@users.noreply.github.com>
AuthorDate: Fri Jun 23 10:19:29 2023 -0600

    Update timestamp for Delivery service Request Comment to match RFC3339 format (#7570)
    
    * wip
    
    * add remaining methods
    
    * Add changelog, fix formatting
    
    * code review comments
    
    * add nano
---
 CHANGELOG.md                                       |   1 +
 .../api/v5/deliveryservice_request_comments.rst    |  14 +-
 lib/go-tc/deliveryservice_request_comments.go      |  31 +++
 .../v5/deliveryservice_request_comments_test.go    |  10 +-
 traffic_ops/testing/api/v5/traffic_control_test.go |   2 +-
 .../deliveryservice/request/comment/comments.go    | 259 +++++++++++++++++++++
 traffic_ops/traffic_ops_golang/routing/routes.go   |   8 +-
 .../v5-client/deliveryservice_request_comments.go  |   8 +-
 8 files changed, 312 insertions(+), 21 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 2e578712b8..a207fe4dd2 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -57,6 +57,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/).
 - [#7469](https://github.com/apache/trafficcontrol/pull/7469) *Traffic Ops* Changed logic to not report empty or missing cookies into TO error.log.
 
 ### Fixed
+- [#7570](https://github.com/apache/trafficcontrol/pull/7570) *Traffic Ops* Fixes `deliveryservice_request_comments` v5 apis to respond with `RFC3339` date/time Format.
 - [#7312](https://github.com/apache/trafficcontrol/issues/7312) *Docs* Changing docs for CDN locks for DELETE response structure v4 and v5. 
 - [#7572](https://github.com/apache/trafficcontrol/pull/7572) *Traffic Ops* Fixes Delivery Service Requests V5 apis docs with RFC3339 date/time Format
 - [#7539](https://github.com/apache/trafficcontrol/pull/7539) *Traffic Monitor* Use stats_over_http timestamp to calculate bandwidth for TM's health. 
diff --git a/docs/source/api/v5/deliveryservice_request_comments.rst b/docs/source/api/v5/deliveryservice_request_comments.rst
index d546ecbcc1..f06d2c0ef0 100644
--- a/docs/source/api/v5/deliveryservice_request_comments.rst
+++ b/docs/source/api/v5/deliveryservice_request_comments.rst
@@ -61,7 +61,7 @@ Response Structure
 :authorId:                      The integral, unique identifier of the user who created the comment.
 :deliveryServiceRequestId:      The integral, unique identifier of the :term:`Delivery Service Request` that the comment was posted on.
 :id:                            The integral, unique identifier of the :term:`DSR` comment.
-:lastUpdated:                   The date and time at which the user was last modified, in :ref:`non-rfc-datetime`
+:lastUpdated:                   The date and time at which the user was last modified, in :rfc:`3339`
 :value:                         The text of the comment that was posted.
 :xmlId:                         This is the ``xmlId`` value that you provided in the request.
 
@@ -88,7 +88,7 @@ Response Structure
 				"author": "admin",
 				"deliveryServiceRequestId": 2,
 				"id": 3,
-				"lastUpdated": "2020-02-24 19:59:46+00",
+				"lastUpdated": "2020-02-24T19:59:46.682939-06:00",
 				"value": "Changing to a different origin for now.",
 				"xmlId": "demo1"
 			},
@@ -97,7 +97,7 @@ Response Structure
 				"author": "admin",
 				"deliveryServiceRequestId": 2,
 				"id": 4,
-				"lastUpdated": "2020-02-24 19:59:55+00",
+				"lastUpdated": "2020-02-24T19:59:55.782431-06:00",
 				"value": "Using HTTPS.",
 				"xmlId": "demo1"
 			}
@@ -141,7 +141,7 @@ Response Structure
 :authorId:                      The integral, unique identifier of the user who created the comment.
 :deliveryServiceRequestId:      The integral, unique identifier of the :term:`Delivery Service Request` that the comment was posted on.
 :id:                            The integral, unique identifier of the :term:`DSR` comment.
-:lastUpdated:                   The date and time at which the user was last modified, in :ref:`non-rfc-datetime`
+:lastUpdated:                   The date and time at which the user was last modified, in :rfc:`3339`
 :value:                         The text of the comment that was posted.
 :xmlId:                         This is the ``xmlId`` value that you provided in the request.
 
@@ -173,7 +173,7 @@ Response Structure
 			"author": null,
 			"deliveryServiceRequestId": 2,
 			"id": 6,
-			"lastUpdated": "2020-02-24 20:02:20+00",
+			"lastUpdated": "2020-02-24T20:02:20.583524-06:00",
 			"value": "Does anyone have time to review my delivery service request?",
 			"xmlId": null
 		}
@@ -226,7 +226,7 @@ Response Structure
 :authorId:                      The integral, unique identifier of the user who created the comment.
 :deliveryServiceRequestId:      The integral, unique identifier of the :term:`Delivery Service Request` that the comment was posted on.
 :id:                            The integral, unique identifier of the :term:`DSR` comment.
-:lastUpdated:                   The date and time at which the user was last modified, in :ref:`non-rfc-datetime`
+:lastUpdated:                   The date and time at which the user was last modified, in :rfc:`3339`
 :value:                         The text of the comment that was posted.
 :xmlId:                         This is the ``xmlId`` value that you provided in the request.
 
@@ -258,7 +258,7 @@ Response Structure
 			"author": null,
 			"deliveryServiceRequestId": 2,
 			"id": 6,
-			"lastUpdated": "2020-02-24 20:05:46+00",
+			"lastUpdated": "2020-02-24T20:05:46.124229-06:00",
 			"value": "Update: We no longer need this, feel free to reject.\n\nDoes anyone have time to review my delivery service request?",
 			"xmlId": null
 		}
diff --git a/lib/go-tc/deliveryservice_request_comments.go b/lib/go-tc/deliveryservice_request_comments.go
index dce1f77434..02314b8e07 100644
--- a/lib/go-tc/deliveryservice_request_comments.go
+++ b/lib/go-tc/deliveryservice_request_comments.go
@@ -19,6 +19,37 @@ package tc
  * under the License.
  */
 
+import (
+	"time"
+)
+
+// DeliveryServiceRequestCommentV5 is a Delivery Service Request Comment as it appears in version 5 of the
+// Traffic Ops API - it always points to the highest minor version in APIv5.
+type DeliveryServiceRequestCommentV5 DeliveryServiceRequestCommentV50
+
+// DeliveryServiceRequestCommentV50 is a struct containing the fields for a delivery
+// service request comment, for API version 5.0.
+type DeliveryServiceRequestCommentV50 struct {
+	AuthorID                 IDNoMod   `json:"authorId" db:"author_id"`
+	Author                   string    `json:"author"`
+	DeliveryServiceRequestID int       `json:"deliveryServiceRequestId" db:"deliveryservice_request_id"`
+	ID                       int       `json:"id" db:"id"`
+	LastUpdated              time.Time `json:"lastUpdated" db:"last_updated"`
+	Value                    string    `json:"value" db:"value"`
+	XMLID                    string    `json:"xmlId" db:"xml_id"`
+}
+
+// DeliveryServiceRequestCommentsResponseV5 is a Delivery Service Request Comment Response as it appears in version 5
+// of the Traffic Ops API - it always points to the highest minor version in APIv5.
+type DeliveryServiceRequestCommentsResponseV5 DeliveryServiceRequestCommentsResponseV50
+
+// DeliveryServiceRequestCommentsResponseV50 is a list of
+// DeliveryServiceRequestCommentsV5 as a response, for API version 5.0.
+type DeliveryServiceRequestCommentsResponseV50 struct {
+	Response []DeliveryServiceRequestCommentV5 `json:"response"`
+	Alerts
+}
+
 // DeliveryServiceRequestCommentsResponse is a list of
 // DeliveryServiceRequestComments as a response.
 type DeliveryServiceRequestCommentsResponse struct {
diff --git a/traffic_ops/testing/api/v5/deliveryservice_request_comments_test.go b/traffic_ops/testing/api/v5/deliveryservice_request_comments_test.go
index 1b3c8ead37..94d49c8b71 100644
--- a/traffic_ops/testing/api/v5/deliveryservice_request_comments_test.go
+++ b/traffic_ops/testing/api/v5/deliveryservice_request_comments_test.go
@@ -38,7 +38,7 @@ func TestDeliveryServiceRequestComments(t *testing.T) {
 		currentTimeRFC := currentTime.Format(time.RFC1123)
 		tomorrow := currentTime.AddDate(0, 0, 1).Format(time.RFC1123)
 
-		methodTests := utils.TestCase[client.Session, client.RequestOptions, tc.DeliveryServiceRequestComment]{
+		methodTests := utils.TestCase[client.Session, client.RequestOptions, tc.DeliveryServiceRequestCommentV5]{
 			"GET": {
 				"NOT MODIFIED when NO CHANGES made": {
 					ClientSession: TOSession,
@@ -68,7 +68,7 @@ func TestDeliveryServiceRequestComments(t *testing.T) {
 				"OK when VALID request": {
 					EndpointID:    GetDSRequestCommentId(t, "admin"),
 					ClientSession: TOSession,
-					RequestBody: tc.DeliveryServiceRequestComment{
+					RequestBody: tc.DeliveryServiceRequestCommentV5{
 						DeliveryServiceRequestID: GetDSRequestId(t, "test-ds1")(),
 						Value:                    "updated comment",
 					},
@@ -78,13 +78,13 @@ func TestDeliveryServiceRequestComments(t *testing.T) {
 					EndpointID:    GetDSRequestCommentId(t, "admin"),
 					ClientSession: TOSession,
 					RequestOpts:   client.RequestOptions{Header: http.Header{rfc.IfUnmodifiedSince: {currentTimeRFC}}},
-					RequestBody:   tc.DeliveryServiceRequestComment{},
+					RequestBody:   tc.DeliveryServiceRequestCommentV5{},
 					Expectations:  utils.CkRequest(utils.HasError(), utils.HasStatus(http.StatusPreconditionFailed)),
 				},
 				"PRECONDITION FAILED when updating with IFMATCH ETAG Header": {
 					EndpointID:    GetDSRequestCommentId(t, "admin"),
 					ClientSession: TOSession,
-					RequestBody:   tc.DeliveryServiceRequestComment{},
+					RequestBody:   tc.DeliveryServiceRequestCommentV5{},
 					RequestOpts:   client.RequestOptions{Header: http.Header{rfc.IfMatch: {rfc.ETag(currentTime)}}},
 					Expectations:  utils.CkRequest(utils.HasError(), utils.HasStatus(http.StatusPreconditionFailed)),
 				},
@@ -147,7 +147,7 @@ func GetDSRequestId(t *testing.T, xmlId string) func() int {
 func validateSortedDSRequestComments() utils.CkReqFunc {
 	return func(t *testing.T, _ toclientlib.ReqInf, resp interface{}, _ tc.Alerts, err error) {
 		var sortedList []string
-		dsReqComments := resp.([]tc.DeliveryServiceRequestComment)
+		dsReqComments := resp.([]tc.DeliveryServiceRequestCommentV5)
 
 		for _, comment := range dsReqComments {
 			sortedList = append(sortedList, comment.XMLID)
diff --git a/traffic_ops/testing/api/v5/traffic_control_test.go b/traffic_ops/testing/api/v5/traffic_control_test.go
index 5fd0eea97f..d9df3d0bad 100644
--- a/traffic_ops/testing/api/v5/traffic_control_test.go
+++ b/traffic_ops/testing/api/v5/traffic_control_test.go
@@ -29,7 +29,7 @@ type TrafficControl struct {
 	Coordinates                                       []tc.CoordinateV5                       `json:"coordinates"`
 	DeliveryServicesRegexes                           []tc.DeliveryServiceRegexesTest         `json:"deliveryServicesRegexes"`
 	DeliveryServiceRequests                           []tc.DeliveryServiceRequestV5           `json:"deliveryServiceRequests"`
-	DeliveryServiceRequestComments                    []tc.DeliveryServiceRequestComment      `json:"deliveryServiceRequestComments"`
+	DeliveryServiceRequestComments                    []tc.DeliveryServiceRequestCommentV5    `json:"deliveryServiceRequestComments"`
 	DeliveryServices                                  []tc.DeliveryServiceV5                  `json:"deliveryservices"`
 	DeliveryServicesRequiredCapabilities              []tc.DeliveryServicesRequiredCapability `json:"deliveryservicesRequiredCapabilities"`
 	DeliveryServiceServerAssignments                  []tc.DeliveryServiceServers             `json:"deliveryServiceServerAssignments"`
diff --git a/traffic_ops/traffic_ops_golang/deliveryservice/request/comment/comments.go b/traffic_ops/traffic_ops_golang/deliveryservice/request/comment/comments.go
index 60414f342b..19d761cfdf 100644
--- a/traffic_ops/traffic_ops_golang/deliveryservice/request/comment/comments.go
+++ b/traffic_ops/traffic_ops_golang/deliveryservice/request/comment/comments.go
@@ -20,16 +20,20 @@ package comment
  */
 
 import (
+	"encoding/json"
 	"errors"
+	"fmt"
 	"net/http"
 	"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"
 )
@@ -55,6 +59,15 @@ JOIN deliveryservice_request dsr ON dsrc.deliveryservice_request_id = dsr.id ` +
 	select max(last_updated) as t from last_deleted l where l.table_name='deliveryservice_request_comment') as res`
 }
 
+func selectMaxLastUpdatedQuery(where, orderBy, pagination string) string {
+	return `SELECT max(t) from (
+		SELECT max(dsrc.last_updated) as t from deliveryservice_request_comment dsrc
+JOIN tm_user a ON dsrc.author_id = a.id
+JOIN deliveryservice_request dsr ON dsrc.deliveryservice_request_id = dsr.id ` + where + orderBy + pagination +
+		` UNION ALL
+	select max(last_updated) as t from last_deleted l where l.table_name='deliveryservice_request_comment') as res`
+}
+
 func (v *TODeliveryServiceRequestComment) NewReadObj() interface{} {
 	return &tc.DeliveryServiceRequestCommentNullable{}
 }
@@ -186,3 +199,249 @@ WHERE id=:id RETURNING last_updated`
 func deleteQuery() string {
 	return `DELETE FROM deliveryservice_request_comment WHERE id = :id`
 }
+
+// Get is used to read the DeliveryServiceRequestCommentV5 entities from the database.
+func Get(w http.ResponseWriter, r *http.Request) {
+	var maxTime time.Time
+	var runSecond bool
+	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()
+
+	api.DefaultSort(inf, "xmlId")
+	cols := map[string]dbhelpers.WhereColumnInfo{
+		"authorId":                 dbhelpers.WhereColumnInfo{Column: "dsrc.author_id"},
+		"author":                   dbhelpers.WhereColumnInfo{Column: "a.username"},
+		"deliveryServiceRequestId": dbhelpers.WhereColumnInfo{Column: "dsrc.deliveryservice_request_id"},
+		"id":                       dbhelpers.WhereColumnInfo{Column: "dsrc.id", Checker: api.IsInt},
+	}
+
+	where, orderBy, pagination, queryValues, errs := dbhelpers.BuildWhereAndOrderByAndPagination(inf.Params, cols)
+	if len(errs) > 0 {
+		errCode = http.StatusBadRequest
+		userErr = util.JoinErrs(errs)
+		api.HandleErr(w, r, tx, errCode, userErr, nil)
+		return
+	}
+
+	if inf.Config.UseIMS {
+		runSecond, maxTime = ims.TryIfModifiedSinceQuery(inf.Tx, r.Header, queryValues, selectMaxLastUpdatedQuery(where, orderBy, pagination))
+		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")
+	}
+
+	deliveryServiceRequestComments := []tc.DeliveryServiceRequestCommentV5{}
+	query := selectQuery() + where + orderBy + pagination
+	rows, err := inf.Tx.NamedQuery(query, queryValues)
+	if err != nil {
+		api.HandleErr(w, r, tx, http.StatusInternalServerError, nil, errors.New("querying cdn locks: "+err.Error()))
+		return
+	}
+	defer rows.Close()
+
+	for rows.Next() {
+		var deliveryServiceRequestComment tc.DeliveryServiceRequestCommentV5
+		if err = rows.Scan(&deliveryServiceRequestComment.Author, &deliveryServiceRequestComment.AuthorID, &deliveryServiceRequestComment.DeliveryServiceRequestID, &deliveryServiceRequestComment.XMLID, &deliveryServiceRequestComment.ID, &deliveryServiceRequestComment.LastUpdated, &deliveryServiceRequestComment.Value); err != nil {
+			api.HandleErr(w, r, tx, http.StatusInternalServerError, nil, errors.New("scanning delivery service request comments: "+err.Error()))
+			return
+		}
+		deliveryServiceRequestComments = append(deliveryServiceRequestComments, deliveryServiceRequestComment)
+	}
+	api.WriteResp(w, r, deliveryServiceRequestComments)
+}
+
+// Validate is used to ensure that the DeliveryServiceRequestCommentV5 struct passed in to the function is valid.
+func Validate(dsrc tc.DeliveryServiceRequestCommentV5) error {
+	errs := validation.Errors{
+		"deliveryServiceRequestId": validation.Validate(dsrc.DeliveryServiceRequestID, validation.NotNil),
+		"value":                    validation.Validate(dsrc.Value, validation.NotNil),
+	}
+	return util.JoinErrs(tovalidate.ToErrors(errs))
+}
+
+// Update is used to modify an existing DeliveryServiceRequestCommentV5 in the database.
+func Update(w http.ResponseWriter, r *http.Request) {
+	var deliveryServiceRequestComment tc.DeliveryServiceRequestCommentV5
+
+	inf, userErr, sysErr, errCode := api.NewInfo(r, []string{"id"}, nil)
+	if userErr != nil || sysErr != nil {
+		api.HandleErr(w, r, inf.Tx.Tx, errCode, userErr, sysErr)
+		return
+	}
+	defer inf.Close()
+
+	tx := inf.Tx.Tx
+	if err := json.NewDecoder(r.Body).Decode(&deliveryServiceRequestComment); err != nil {
+		api.HandleErr(w, r, tx, http.StatusBadRequest, err, nil)
+		return
+	}
+
+	if err := Validate(deliveryServiceRequestComment); err != nil {
+		api.HandleErr(w, r, tx, http.StatusBadRequest, err, nil)
+		return
+	}
+
+	var current tc.DeliveryServiceRequestCommentV5
+	err := inf.Tx.QueryRowx(selectQuery() + `WHERE dsrc.id=` + inf.Params["id"]).StructScan(&current)
+	if err != nil {
+		api.HandleErr(w, r, tx, http.StatusInternalServerError, nil, errors.New("scanning deliveryservice_request_comment: "+err.Error()))
+		return
+	}
+
+	userID := tc.IDNoMod(inf.User.ID)
+	if current.AuthorID != userID {
+		api.HandleErr(w, r, tx, http.StatusBadRequest, errors.New("comments can only be updated by the author"), nil)
+		return
+	}
+	deliveryServiceRequestComment.AuthorID = current.AuthorID
+	idParam := inf.Params["id"]
+	id, parseErr := strconv.Atoi(idParam)
+	if parseErr != nil {
+		api.HandleErr(w, r, inf.Tx.Tx, http.StatusBadRequest, errors.New("id must be an integer"), nil)
+		return
+	}
+	deliveryServiceRequestComment.ID = id
+	userErr, sysErr, sc := api.CheckIfUnModified(r.Header, inf.Tx, id, "deliveryservice_request_comment")
+	if userErr != nil || sysErr != nil {
+		api.HandleErr(w, r, tx, sc, userErr, sysErr)
+		return
+	}
+
+	rows, err := inf.Tx.NamedQuery(updateQuery(), deliveryServiceRequestComment)
+	if err != nil {
+		userErr, sysErr, sc := api.ParseDBError(err)
+		api.HandleErr(w, r, tx, sc, userErr, sysErr)
+		return
+	}
+	defer rows.Close()
+
+	if !rows.Next() {
+		api.HandleErr(w, r, tx, http.StatusNotFound, errors.New("no deliveryservice_request_comment found with this id"), nil)
+		return
+	}
+	lastUpdated := time.Time{}
+	if err := rows.Scan(&lastUpdated); err != nil {
+		api.HandleErr(w, r, tx, http.StatusInternalServerError, nil, errors.New("scanning lastUpdated from deliveryservice_request_comment insert: "+err.Error()))
+		return
+	}
+	deliveryServiceRequestComment.LastUpdated = time.Now()
+
+	if rows.Next() {
+		api.HandleErr(w, r, tx, http.StatusInternalServerError, nil, errors.New("deliveryservice_request_comment update affected too many rows: >1"))
+		return
+	}
+
+	alerts := tc.CreateAlerts(tc.SuccessLevel, "deliveryservice_request_comment was updated.")
+	api.WriteAlertsObj(w, r, http.StatusOK, alerts, deliveryServiceRequestComment)
+	changeLogMsg := fmt.Sprintf("DELIVERYSERVICE_REQUEST_COMMENT: %d, ID: %d, ACTION: Updated deliveryservice_request_comment", deliveryServiceRequestComment.ID, deliveryServiceRequestComment.ID)
+	api.CreateChangeLogRawTx(api.ApiChange, changeLogMsg, inf.User, tx)
+}
+
+// Create is used to add a new DeliveryServiceRequestCommentV5 to the database.
+func Create(w http.ResponseWriter, r *http.Request) {
+	var deliveryServiceRequestComment tc.DeliveryServiceRequestCommentV5
+
+	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
+
+	if err := json.NewDecoder(r.Body).Decode(&deliveryServiceRequestComment); err != nil {
+		api.HandleErr(w, r, tx, http.StatusBadRequest, err, nil)
+		return
+	}
+
+	if err := Validate(deliveryServiceRequestComment); err != nil {
+		api.HandleErr(w, r, tx, http.StatusBadRequest, err, nil)
+		return
+	}
+
+	au := tc.IDNoMod(inf.User.ID)
+	deliveryServiceRequestComment.AuthorID = au
+
+	resultRows, err := inf.Tx.NamedQuery(insertQuery(), deliveryServiceRequestComment)
+	if err != nil {
+		userErr, sysErr, errCode := api.ParseDBError(err)
+		api.HandleErr(w, r, tx, errCode, userErr, sysErr)
+		return
+	}
+	defer resultRows.Close()
+
+	rowsAffected := 0
+	for resultRows.Next() {
+		rowsAffected++
+		if err := resultRows.Scan(&deliveryServiceRequestComment.ID, &deliveryServiceRequestComment.LastUpdated); err != nil {
+			api.HandleErr(w, r, tx, http.StatusInternalServerError, nil, errors.New("deliveryservice_request_comment create: scanning comment: "+err.Error()))
+			return
+		}
+	}
+	if rowsAffected == 0 {
+		api.HandleErr(w, r, tx, http.StatusInternalServerError, nil, errors.New("deliveryservice_request_comment create: comment couldn't be created"))
+		return
+	} else if rowsAffected > 1 {
+		api.HandleErr(w, r, tx, http.StatusInternalServerError, nil, errors.New("deliveryservice_request_comment create: comment couldn't be created: too many ids returned from insert"))
+		return
+	}
+
+	alerts := tc.CreateAlerts(tc.SuccessLevel, "deliveryservice_request_comment was created.")
+	api.WriteAlertsObj(w, r, http.StatusOK, alerts, deliveryServiceRequestComment)
+	changeLogMsg := fmt.Sprintf("DELIVERYSERVICE_REQUEST_COMMENT: %d, ID: %d, ACTION: Created deliveryservice_request_comment", deliveryServiceRequestComment.ID, deliveryServiceRequestComment.ID)
+	api.CreateChangeLogRawTx(api.ApiChange, changeLogMsg, inf.User, tx)
+}
+
+// Delete is used to remove an existing DeliveryServiceRequestCommentV5 from the database.
+func Delete(w http.ResponseWriter, r *http.Request) {
+	inf, userErr, sysErr, errCode := api.NewInfo(r, []string{"id"}, nil)
+	if userErr != nil || sysErr != nil {
+		api.HandleErr(w, r, inf.Tx.Tx, errCode, userErr, sysErr)
+		return
+	}
+	defer inf.Close()
+
+	tx := inf.Tx.Tx
+
+	idParam := inf.Params["id"]
+	id, parseErr := strconv.Atoi(idParam)
+	if parseErr != nil {
+		api.HandleErr(w, r, inf.Tx.Tx, http.StatusBadRequest, errors.New("id must be an integer "+parseErr.Error()), nil)
+		return
+	}
+
+	var current tc.DeliveryServiceRequestCommentV5
+	err := inf.Tx.QueryRowx(selectQuery() + `WHERE dsrc.id=` + idParam).StructScan(&current)
+	if err != nil {
+		api.HandleErr(w, r, tx, http.StatusInternalServerError, nil, errors.New("scanning deliveryservice_request_comment: "+err.Error()))
+		return
+	}
+
+	if userID := tc.IDNoMod(inf.User.ID); current.AuthorID != userID {
+		api.HandleErr(w, r, tx, http.StatusBadRequest, errors.New("comments can only be deleted by the author"), nil)
+		return
+	}
+
+	rows, err := inf.Tx.NamedQuery(deleteQuery(), current)
+	if err != nil {
+		api.HandleErr(w, r, tx, http.StatusInternalServerError, nil, fmt.Errorf("deleting deliveryservice_request_comment: %w", err))
+		return
+	}
+	rows.Close()
+	alerts := tc.CreateAlerts(tc.SuccessLevel, "deliveryservice_request_comment was deleted.")
+	api.WriteAlerts(w, r, http.StatusOK, alerts)
+	changeLogMsg := fmt.Sprintf("DELIVERYSERVICE_REQUEST_COMMENT: %d, ACTION: Deleted Deliveryservice_request_comment", id)
+	api.CreateChangeLogRawTx(api.ApiChange, changeLogMsg, inf.User, tx)
+}
diff --git a/traffic_ops/traffic_ops_golang/routing/routes.go b/traffic_ops/traffic_ops_golang/routing/routes.go
index 618002e4a6..90c6b53358 100644
--- a/traffic_ops/traffic_ops_golang/routing/routes.go
+++ b/traffic_ops/traffic_ops_golang/routing/routes.go
@@ -385,10 +385,10 @@ func Routes(d ServerData) ([]Route, http.Handler, error) {
 		{Version: api.Version{Major: 5, Minor: 0}, Method: http.MethodPut, Path: `deliveryservice_requests/{id}/status$`, Handler: dsrequest.PutStatus, RequiredPrivLevel: auth.PrivLevelPortal, RequiredPermissions: []string{"DS-REQUEST:UPDATE", "DS-REQUEST:READ"}, Authenticated: Authenticated, Middlewares: nil, ID: 46841509931},
 
 		//Delivery service request comment: CRUD
-		{Version: api.Version{Major: 5, Minor: 0}, Method: http.MethodGet, Path: `deliveryservice_request_comments/?$`, Handler: api.ReadHandler(&comment.TODeliveryServiceRequestComment{}), RequiredPrivLevel: auth.PrivLevelReadOnly, RequiredPermissions: []string{"DS-REQUEST:READ", "DELIVERY-SERVICE:READ", "USER:READ"}, Authenticated: Authenticated, Middlewares: nil, ID: 403265073731},
-		{Version: api.Version{Major: 5, Minor: 0}, Method: http.MethodPut, Path: `deliveryservice_request_comments/?$`, Handler: api.UpdateHandler(&comment.TODeliveryServiceRequestComment{}), RequiredPrivLevel: auth.PrivLevelPortal, RequiredPermissions: []string{"DS-REQUEST:UPDATE", "DELIVERY-SERVICE:READ", "USER:READ"}, Authenticated: Authenticated, Middlewares: nil, ID: 46048784731},
-		{Version: api.Version{Major: 5, Minor: 0}, Method: http.MethodPost, Path: `deliveryservice_request_comments/?$`, Handler: api.CreateHandler(&comment.TODeliveryServiceRequestComment{}), RequiredPrivLevel: auth.PrivLevelPortal, RequiredPermissions: []string{"DS-REQUEST:UPDATE", "DELIVERY-SERVICE:READ", "USER:READ"}, Authenticated: Authenticated, Middlewares: nil, ID: 42722767231},
-		{Version: api.Version{Major: 5, Minor: 0}, Method: http.MethodDelete, Path: `deliveryservice_request_comments/?$`, Handler: api.DeleteHandler(&comment.TODeliveryServiceRequestComment{}), RequiredPrivLevel: auth.PrivLevelPortal, RequiredPermissions: []string{"DS-REQUEST:UPDATE", "DELIVERY-SERVICE:READ", "USER:READ"}, Authenticated: Authenticated, Middlewares: nil, ID: 49950466831},
+		{Version: api.Version{Major: 5, Minor: 0}, Method: http.MethodGet, Path: `deliveryservice_request_comments/?$`, Handler: comment.Get, RequiredPrivLevel: auth.PrivLevelReadOnly, RequiredPermissions: []string{"DS-REQUEST:READ", "DELIVERY-SERVICE:READ", "USER:READ"}, Authenticated: Authenticated, Middlewares: nil, ID: 403265073731},
+		{Version: api.Version{Major: 5, Minor: 0}, Method: http.MethodPut, Path: `deliveryservice_request_comments/?$`, Handler: comment.Update, RequiredPrivLevel: auth.PrivLevelPortal, RequiredPermissions: []string{"DS-REQUEST:UPDATE", "DELIVERY-SERVICE:READ", "USER:READ"}, Authenticated: Authenticated, Middlewares: nil, ID: 46048784731},
+		{Version: api.Version{Major: 5, Minor: 0}, Method: http.MethodPost, Path: `deliveryservice_request_comments/?$`, Handler: comment.Create, RequiredPrivLevel: auth.PrivLevelPortal, RequiredPermissions: []string{"DS-REQUEST:UPDATE", "DELIVERY-SERVICE:READ", "USER:READ"}, Authenticated: Authenticated, Middlewares: nil, ID: 42722767231},
+		{Version: api.Version{Major: 5, Minor: 0}, Method: http.MethodDelete, Path: `deliveryservice_request_comments/?$`, Handler: comment.Delete, RequiredPrivLevel: auth.PrivLevelPortal, RequiredPermissions: []string{"DS-REQUEST:UPDATE", "DELIVERY-SERVICE:READ", "USER:READ"}, Authenticated: Authenticated, Middlewares: nil, ID: 49950466831},
 
 		//Delivery service uri signing keys: CRUD
 		{Version: api.Version{Major: 5, Minor: 0}, Method: http.MethodGet, Path: `deliveryservices/{xmlID}/urisignkeys$`, Handler: urisigning.GetURIsignkeysHandler, RequiredPrivLevel: auth.PrivLevelAdmin, RequiredPermissions: []string{"DS-SECURITY-KEY:READ"}, Authenticated: Authenticated, Middlewares: nil, ID: 429307855831},
diff --git a/traffic_ops/v5-client/deliveryservice_request_comments.go b/traffic_ops/v5-client/deliveryservice_request_comments.go
index e7d8f945ae..d089d32bbe 100644
--- a/traffic_ops/v5-client/deliveryservice_request_comments.go
+++ b/traffic_ops/v5-client/deliveryservice_request_comments.go
@@ -29,7 +29,7 @@ const apiDeliveryServiceRequestComments = "/deliveryservice_request_comments"
 
 // CreateDeliveryServiceRequestComment creates the given Delivery Service
 // Request comment.
-func (to *Session) CreateDeliveryServiceRequestComment(comment tc.DeliveryServiceRequestComment, opts RequestOptions) (tc.Alerts, toclientlib.ReqInf, error) {
+func (to *Session) CreateDeliveryServiceRequestComment(comment tc.DeliveryServiceRequestCommentV5, opts RequestOptions) (tc.Alerts, toclientlib.ReqInf, error) {
 	var alerts tc.Alerts
 	reqInf, err := to.post(apiDeliveryServiceRequestComments, opts, comment, &alerts)
 	return alerts, reqInf, err
@@ -37,7 +37,7 @@ func (to *Session) CreateDeliveryServiceRequestComment(comment tc.DeliveryServic
 
 // UpdateDeliveryServiceRequestComment replaces the Delivery Service Request
 // comment identified by 'id' with the one provided.
-func (to *Session) UpdateDeliveryServiceRequestComment(id int, comment tc.DeliveryServiceRequestComment, opts RequestOptions) (tc.Alerts, toclientlib.ReqInf, error) {
+func (to *Session) UpdateDeliveryServiceRequestComment(id int, comment tc.DeliveryServiceRequestCommentV5, opts RequestOptions) (tc.Alerts, toclientlib.ReqInf, error) {
 	if opts.QueryParameters == nil {
 		opts.QueryParameters = url.Values{}
 	}
@@ -49,8 +49,8 @@ func (to *Session) UpdateDeliveryServiceRequestComment(id int, comment tc.Delive
 
 // GetDeliveryServiceRequestComments retrieves all comments on all Delivery
 // Service Requests.
-func (to *Session) GetDeliveryServiceRequestComments(opts RequestOptions) (tc.DeliveryServiceRequestCommentsResponse, toclientlib.ReqInf, error) {
-	var data tc.DeliveryServiceRequestCommentsResponse
+func (to *Session) GetDeliveryServiceRequestComments(opts RequestOptions) (tc.DeliveryServiceRequestCommentsResponseV5, toclientlib.ReqInf, error) {
+	var data tc.DeliveryServiceRequestCommentsResponseV5
 	reqInf, err := to.get(apiDeliveryServiceRequestComments, opts, &data)
 	return data, reqInf, err
 }