traffic_ops/traffic_ops_golang/deliveryservice/request/comment/comments.go (363 lines of code) (raw):

package comment /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ import ( "encoding/json" "errors" "fmt" "net/http" "strconv" "time" "github.com/apache/trafficcontrol/v8/lib/go-log" "github.com/apache/trafficcontrol/v8/lib/go-tc" "github.com/apache/trafficcontrol/v8/lib/go-tc/tovalidate" "github.com/apache/trafficcontrol/v8/lib/go-util" "github.com/apache/trafficcontrol/v8/traffic_ops/traffic_ops_golang/api" "github.com/apache/trafficcontrol/v8/traffic_ops/traffic_ops_golang/dbhelpers" "github.com/apache/trafficcontrol/v8/traffic_ops/traffic_ops_golang/util/ims" validation "github.com/go-ozzo/ozzo-validation" ) // we need a type alias to define functions on type TODeliveryServiceRequestComment struct { api.APIInfoImpl `json:"-"` tc.DeliveryServiceRequestCommentNullable } func (v *TODeliveryServiceRequestComment) GetLastUpdated() (*time.Time, bool, error) { return api.GetLastUpdated(v.APIInfo().Tx, *v.ID, "deliveryservice_request_comment") } func (v *TODeliveryServiceRequestComment) SetLastUpdated(t tc.TimeNoMod) { v.LastUpdated = &t } func (v *TODeliveryServiceRequestComment) InsertQuery() string { return insertQuery() } func (v *TODeliveryServiceRequestComment) SelectMaxLastUpdatedQuery(where, orderBy, pagination, tableName 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 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{} } func (v *TODeliveryServiceRequestComment) SelectQuery() string { return selectQuery() } func (v *TODeliveryServiceRequestComment) ParamColumns() map[string]dbhelpers.WhereColumnInfo { return 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}, } } func (v *TODeliveryServiceRequestComment) UpdateQuery() string { return updateQuery() } func (v *TODeliveryServiceRequestComment) DeleteQuery() string { return deleteQuery() } func (comment TODeliveryServiceRequestComment) GetKeyFieldsInfo() []api.KeyFieldInfo { return []api.KeyFieldInfo{{Field: "id", Func: api.GetIntKey}} } // Implementation of the Identifier, Validator interface functions func (comment TODeliveryServiceRequestComment) GetKeys() (map[string]interface{}, bool) { if comment.ID == nil { return map[string]interface{}{"id": 0}, false } return map[string]interface{}{"id": *comment.ID}, true } func (comment *TODeliveryServiceRequestComment) SetKeys(keys map[string]interface{}) { i, _ := keys["id"].(int) //this utilizes the non panicking type assertion, if the thrown away ok variable is false i will be the zero of the type, 0 here. comment.ID = &i } func (comment TODeliveryServiceRequestComment) GetAuditName() string { if comment.ID != nil { return strconv.Itoa(*comment.ID) } return "unknown" } func (comment TODeliveryServiceRequestComment) GetType() string { return "deliveryservice_request_comment" } func (comment TODeliveryServiceRequestComment) Validate() (error, error) { errs := validation.Errors{ "deliveryServiceRequestId": validation.Validate(comment.DeliveryServiceRequestID, validation.NotNil), "value": validation.Validate(comment.Value, validation.NotNil), } return util.JoinErrs(tovalidate.ToErrors(errs)), nil } func (comment *TODeliveryServiceRequestComment) Create() (error, error, int) { au := tc.IDNoMod(comment.ReqInfo.User.ID) comment.AuthorID = &au return api.GenericCreate(comment) } func (comment *TODeliveryServiceRequestComment) Read(h http.Header, useIMS bool) ([]interface{}, error, error, int, *time.Time) { api.DefaultSort(comment.APIInfo(), "xmlId") return api.GenericRead(h, comment, useIMS) } func (comment *TODeliveryServiceRequestComment) Update(h http.Header) (error, error, int) { current := TODeliveryServiceRequestComment{} err := comment.ReqInfo.Tx.QueryRowx(selectQuery() + `WHERE dsrc.id=` + strconv.Itoa(*comment.ID)).StructScan(&current) if err != nil { return api.ParseDBError(err) } userID := tc.IDNoMod(comment.ReqInfo.User.ID) if *current.AuthorID != userID { return errors.New("Comments can only be updated by the author"), nil, http.StatusBadRequest } return api.GenericUpdate(h, comment) } func (comment *TODeliveryServiceRequestComment) Delete() (error, error, int) { var current TODeliveryServiceRequestComment err := comment.ReqInfo.Tx.QueryRowx(selectQuery() + `WHERE dsrc.id=` + strconv.Itoa(*comment.ID)).StructScan(&current) if err != nil { return nil, errors.New("querying DeliveryServiceRequestComments: " + err.Error()), http.StatusInternalServerError } if userID := tc.IDNoMod(comment.ReqInfo.User.ID); *current.AuthorID != userID { // TODO determine if users should be able to delete sub-tenant users' comments? Else, a deleted user's comments can never be removed. return errors.New("Comments can only be deleted by the author"), nil, http.StatusBadRequest } return api.GenericDelete(comment) } func insertQuery() string { query := `INSERT INTO deliveryservice_request_comment ( author_id, deliveryservice_request_id, value) VALUES ( :author_id, :deliveryservice_request_id, :value) RETURNING id,last_updated` return query } func selectQuery() string { query := `SELECT a.username AS author, dsrc.author_id, dsrc.deliveryservice_request_id, COALESCE(dsr.deliveryservice->>'xmlId', dsr.original->>'xmlId') as xml_id, dsrc.id, dsrc.last_updated, dsrc.value 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 ` return query } func updateQuery() string { query := `UPDATE deliveryservice_request_comment SET deliveryservice_request_id=:deliveryservice_request_id, value=:value WHERE id=:id RETURNING last_updated` return query } 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{ "id": validation.Validate(dsrc.ID, validation.NotNil), "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 } 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 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=` + strconv.Itoa(deliveryServiceRequestComment.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 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) }