kdamichie commented on code in PR #7698:
URL: https://github.com/apache/trafficcontrol/pull/7698#discussion_r1286076862
##########
docs/source/api/v5/regions.rst:
##########
@@ -41,8 +41,7 @@ Request Structure
+-----------+----------+---------------------------------------------------------------------------------------------------------------+
| name | no | Filter :term:`Regions` by name
|
+-----------+----------+---------------------------------------------------------------------------------------------------------------+
- | orderby | no | Choose the ordering of the results - must be
the name of one of the fields of the objects in the ``response`` |
- | | | array
|
+ | orderby | no | Choose the ordering of the results - either
one of them "division", "id", "name"
|
Review Comment:
Missing ending pipe
##########
docs/source/api/v5/regions_id.rst:
##########
@@ -39,7 +39,6 @@ Request Structure
+------+---------------------------------------------------------+
:division: The new integral, unique identifier of the division which shall
contain the region\ [1]_
Review Comment:
Target "1" was removed, so his does not refer to anything anymore. Needs
removed
##########
docs/source/api/v5/regions.rst:
##########
@@ -111,7 +110,6 @@ Creates a new region
Request Structure
-----------------
:division: The integral, unique identifier of the division which shall
contain the new region\ [1]_
Review Comment:
Target "1" was removed, so his does not refer to anything anymore. Needs
removed
##########
traffic_ops/traffic_ops_golang/region/regions.go:
##########
@@ -192,3 +200,230 @@ func deleteQuery() string {
query := deleteQueryBase() + ` WHERE id=:id`
return query
}
+
+// Read is the handler for GET requests to Regions of APIv5
+func Read(w http.ResponseWriter, r *http.Request) {
+ var maxTime time.Time
+ var runSecond bool
+
+ inf, sysErr, userErr, errCode := api.NewInfo(r, nil, nil)
+ tx := inf.Tx
+ if sysErr != nil || userErr != 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{
+ "division": {Column: "r.division"},
+ "id": {Column: "r.id", Checker: api.IsInt},
+ "name": {Column: "r.name"},
+ }
+ if _, ok := inf.Params["orderby"]; !ok {
+ inf.Params["orderby"] = "name"
+ }
+ where, orderBy, pagination, queryValues, errs :=
dbhelpers.BuildWhereAndOrderByAndPagination(inf.Params, queryParamsToQueryCols)
+ if len(errs) > 0 {
+ api.HandleErr(w, r, tx.Tx, http.StatusBadRequest,
util.JoinErrs(errs), nil)
+ }
+ 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 := `SELECT r.division, d.name as divisionname, r.id,
r.last_updated, r.name FROM region r
+ JOIN division d ON r.division = d.id` + where +
orderBy + pagination
+ rows, err := tx.NamedQuery(query, queryValues)
+ if err != nil {
+ api.HandleErr(w, r, tx.Tx, http.StatusInternalServerError, nil,
fmt.Errorf("region get: error getting region(s): %w", err))
+ }
+ defer log.Close(rows, "unable to close DB connection")
+
+ typ := tc.RegionV5{}
+ regionList := []tc.RegionV5{}
+ for rows.Next() {
+ if err = rows.Scan(&typ.Division, &typ.DivisionName, &typ.ID,
&typ.LastUpdated, &typ.Name); err != nil {
+ api.HandleErr(w, r, tx.Tx,
http.StatusInternalServerError, nil, fmt.Errorf("error getting region(s): %w",
err))
+ }
+ regionList = append(regionList, typ)
+ }
+
+ api.WriteResp(w, r, regionList)
+ return
+}
+
+// Create region with the passed data for APIv5.
+func Create(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
+ rg, readValErr := readAndValidateJsonStruct(r)
+ if readValErr != nil {
+ api.HandleErr(w, r, tx, http.StatusBadRequest, readValErr, nil)
+ return
+ }
+
+ var exists bool
+ existErr := tx.QueryRow(`SELECT EXISTS(SELECT * from region where name
= $1)`, rg.Name).Scan(&exists)
+
+ if existErr != nil {
+ api.HandleErr(w, r, tx, http.StatusInternalServerError, nil,
fmt.Errorf("error: %w, when checking if region with name %s exists", existErr,
rg.Name))
+ return
+ }
+ if exists {
+ api.HandleErr(w, r, tx, http.StatusBadRequest,
fmt.Errorf("region name '%s' already exists", rg.Name), nil)
+ return
+ }
+
+ query := `INSERT INTO region ( division, name) VALUES ( $1, $2 )
+ RETURNING
+ id,last_updated,
+ (SELECT d.name FROM division d WHERE
d.id = region.division)`
+ err := tx.QueryRow(query, rg.Division, rg.Name).Scan(&rg.ID,
&rg.LastUpdated, &rg.DivisionName)
+ if err != nil {
+ if errors.Is(err, sql.ErrNoRows) {
+ api.HandleErr(w, r, tx, http.StatusInternalServerError,
fmt.Errorf("error: %w in creating region with name: %s", err, rg.Name), nil)
+ return
+ }
+ usrErr, sysErr, code := api.ParseDBError(err)
+ api.HandleErr(w, r, tx, code, usrErr, sysErr)
+ return
+ }
+ alerts := tc.CreateAlerts(tc.SuccessLevel, "region is created.")
+ api.WriteAlertsObj(w, r, http.StatusCreated, alerts, rg)
+ return
+}
+
+// Update a Region for APIv5
+func Update(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, inf.Tx.Tx, errCode, userErr, sysErr)
+ return
+ }
+ defer inf.Close()
+
+ tx := inf.Tx.Tx
+ rg, readValErr := readAndValidateJsonStruct(r)
+ if readValErr != nil {
+ api.HandleErr(w, r, tx, http.StatusBadRequest, readValErr, nil)
+ return
+ }
+
+ requestedRegionId := inf.IntParams["id"]
+ // check if the entity was already updated
+ userErr, sysErr, errCode = api.CheckIfUnModified(r.Header, inf.Tx,
requestedRegionId, "region")
+ if userErr != nil || sysErr != nil {
+ api.HandleErr(w, r, tx, errCode, userErr, sysErr)
+ return
+ }
+
+ query := `UPDATE region SET division= $1, name= $2 WHERE id= $3
+ RETURNING
+ id,last_updated,
+ (SELECT d.name FROM division d WHERE
d.id = region.division)`
+
+ err := tx.QueryRow(query, rg.Division, rg.Name,
requestedRegionId).Scan(&rg.ID, &rg.LastUpdated, &rg.DivisionName)
+ if err != nil {
+ if errors.Is(err, sql.ErrNoRows) {
+ api.HandleErr(w, r, tx, http.StatusBadRequest,
fmt.Errorf("region: %d not found", requestedRegionId), nil)
+ return
+ }
+ usrErr, sysErr, code := api.ParseDBError(err)
+ api.HandleErr(w, r, tx, code, usrErr, sysErr)
+ return
+ }
+
+ alerts := tc.CreateAlerts(tc.SuccessLevel, "region was updated")
+ api.WriteAlertsObj(w, r, http.StatusOK, alerts, rg)
+ return
+}
+
+// Delete an Region for APIv5
+func Delete(w http.ResponseWriter, r *http.Request) {
+ inf, userErr, sysErr, errCode := api.NewInfo(r, nil, nil)
+ tx := inf.Tx.Tx
+ if userErr != nil || sysErr != nil {
+ api.HandleErr(w, r, tx, errCode, userErr, sysErr)
+ return
+ }
+ defer inf.Close()
+
+ requestedRegionId := inf.Params["id"]
+ if requestedRegionId == "" {
+ api.HandleErr(w, r, tx, http.StatusBadRequest, fmt.Errorf("no
region exists for empty id"), nil)
+ return
+ }
+
+ var exists bool
+ existErr := tx.QueryRow(`SELECT EXISTS(SELECT * from region where id =
$1)`, requestedRegionId).Scan(&exists)
+
+ if existErr != nil {
+ api.HandleErr(w, r, tx, http.StatusInternalServerError, nil,
fmt.Errorf("error: %w, when checking if region with id %s exists", existErr,
requestedRegionId))
+ return
+ }
+ if !exists {
+ api.HandleErr(w, r, tx, http.StatusNotFound, fmt.Errorf("no
region exists by id: %s", requestedRegionId), nil)
+ return
+ }
+
+ res, err := tx.Exec("DELETE FROM region WHERE id=$1", requestedRegionId)
+ if err != nil {
+ api.HandleErr(w, r, tx, http.StatusInternalServerError, nil,
err)
+ return
+ }
+ rowsAffected, err := res.RowsAffected()
+ if err != nil {
+ api.HandleErr(w, r, tx, http.StatusInternalServerError, nil,
fmt.Errorf("determining rows affected for delete region: %w", err))
+ return
+ }
+ if rowsAffected == 0 {
+ api.HandleErr(w, r, tx, http.StatusInternalServerError,
fmt.Errorf("no rows deleted for region"), nil)
+ return
+ }
+ alerts := tc.CreateAlerts(tc.SuccessLevel, "region was deleted.")
+ api.WriteAlerts(w, r, http.StatusOK, alerts)
+ return
+}
+
+// readAndValidateJsonStruct reads json body and validates json fields
+func readAndValidateJsonStruct(r *http.Request) (tc.RegionV5, error) {
+ var region tc.RegionV5
+ if err := json.NewDecoder(r.Body).Decode(®ion); err != nil {
+ userErr := fmt.Errorf("error decoding POST request body into
RegionV5 struct %w", err)
+ return region, userErr
+ }
+
+ // validate JSON body
+ errs := tovalidate.ToErrors(validation.Errors{
+ "division": validation.Validate(region.Division,
validation.NotNil, validation.Min(0)),
+ "name": validation.Validate(region.Name,
validation.Required),
+ })
+ if len(errs) > 0 {
+ userErr := util.JoinErrs(errs)
+ return region, userErr
+ }
+ return region, nil
+}
+
+// SelectMaxLastUpdatedQuery used for TryIfModifiedSinceQuery()
+func SelectMaxLastUpdatedQuery(where string) string {
+ return `SELECT max(t) from (
+ SELECT max(r.last_updated) as t FROM region r
+JOIN division d ON r.division = d.id ` + where +
+ ` UNION ALL
+ select max(last_updated) as t from last_deleted l where
l.table_name='region') as res`
Review Comment:
Any reason to keep this commented out statement?
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]