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 8a08e3b187 To types rfc format (#7575)
8a08e3b187 is described below

commit 8a08e3b187de43bd78188feda005dc75a445fd6d
Author: Kannan.G.B <[email protected]>
AuthorDate: Thu Jul 6 23:20:32 2023 +0530

    To types rfc format (#7575)
    
    * types rfc time changes
    
    * types rfc time changes fixes
    
    * type get api scan order fix
    
    * types golang rfc changes
    
    types golang rfc changes
    
    * DOC UPDATED FOR TYPE
    
    * removed unwanted code
    
    * test case fix
    
    error in test case fixed
    
    * revert code
    
    * revert old change
    
    * align correction
    
    * Delete admin
    
    * comments addresed
    
    * typo changes
    
    * comment addressing
    
    * comment changes
    
    * comments addressed
    
    * removed unused TypeNullableV5
    
    * comments corrected as per fnc name
    
    * test case fail fix
    
    * correct method used
    
    * type reference correction
    
    * changelog fixed
---
 CHANGELOG.md                                       |   1 +
 docs/source/api/v5/types.rst                       |   8 +-
 docs/source/api/v5/types_id.rst                    |   2 +-
 lib/go-tc/types.go                                 |  23 ++
 traffic_ops/testing/api/v5/traffic_control_test.go |   2 +-
 traffic_ops/testing/api/v5/types_test.go           |  16 +-
 traffic_ops/traffic_ops_golang/routing/routes.go   |   8 +-
 traffic_ops/traffic_ops_golang/types/types.go      | 238 +++++++++++++++++++++
 traffic_ops/v5-client/type.go                      |   8 +-
 9 files changed, 284 insertions(+), 22 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 98871c886a..7c44cfab32 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -126,6 +126,7 @@ The format is based on [Keep a 
Changelog](http://keepachangelog.com/en/1.0.0/).
 - [#7471](https://github.com/apache/trafficcontrol/pull/7471) *Traffic Control 
Cache Config (t3c)* Fixed issue with MSO non topo origins from multiple cache 
groups.
 - [#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.
 - [#7590](https://github.com/apache/trafficcontrol/issues/7590) *Traffic 
Control Cache Config (t3c)* Fixed issue with git detected dubious ownership in 
repository.
+- [#7575](https://github.com/apache/trafficcontrol/pull/7575) *Traffic Ops* 
Fixes `types` v5 apis to respond with `RFC3339` date/time Format.
 
 ### Removed
 - [#7271](https://github.com/apache/trafficcontrol/pull/7271) Remove 
components in `infrastructre/docker/`, not in use as cdn-in-a-box performs the 
same functionality.
diff --git a/docs/source/api/v5/types.rst b/docs/source/api/v5/types.rst
index d45201d789..d00066ce4a 100644
--- a/docs/source/api/v5/types.rst
+++ b/docs/source/api/v5/types.rst
@@ -55,7 +55,7 @@ Response Structure
 ------------------
 :description: A short description of this type
 :id:          An integral, unique identifier for this type
-:lastUpdated: The date and time at which this type was last updated, in 
:ref:`non-rfc-datetime`
+:lastUpdated: The date and time at which this type was last updated, in 
:rfc:`3339`
 :name:        The name of this type
 :useInTable:  The name of the Traffic Ops database table that contains objects 
which are grouped, identified, or described by this type
 
@@ -77,7 +77,7 @@ Response Structure
        { "response": [
                {
                        "id": 48,
-                       "lastUpdated": "2018-12-12 16:26:41+00",
+                       "lastUpdated": "2018-12-12T10:59:07.962423+05:30",
                        "name": "TC_LOC",
                        "description": "Location for Traffic Control Component 
Servers",
                        "useInTable": "cachegroup"
@@ -124,7 +124,7 @@ Response Structure
 
 :description: A short description of this type
 :id:          An integral, unique identifier for this type
-:lastUpdated: The date and time at which this type was last updated, in 
:ref:`non-rfc-datetime`
+:lastUpdated: The date and time at which this type was last updated, in 
:rfc:`3339`
 :name:        The name of this type
 :useInTable:  The name of the Traffic Ops database table that contains objects 
which are grouped, identified, or described by this type
 
@@ -152,7 +152,7 @@ Response Structure
                "response": [
                {
                        "id": 3004,
-                       "lastUpdated": "2020-02-26 18:58:41+00",
+                       "lastUpdated": "2020-02-26T10:59:07.962423+05:30",
                        "name": "Example01",
                        "description": "Example"
                        "useInTable": "server"
diff --git a/docs/source/api/v5/types_id.rst b/docs/source/api/v5/types_id.rst
index 598476d81c..1581080533 100644
--- a/docs/source/api/v5/types_id.rst
+++ b/docs/source/api/v5/types_id.rst
@@ -93,7 +93,7 @@ Response Structure
                "response": [
                {
                        "id": 3004,
-                       "lastUpdated": "2020-02-26 18:58:41+00",
+                       "lastUpdated": "2020-02-26T10:59:07.962423+05:30",
                        "name": "Example02",
                        "description": "Example"
                        "useInTable": "server"
diff --git a/lib/go-tc/types.go b/lib/go-tc/types.go
index f6e286c0c9..3d189aa6fe 100644
--- a/lib/go-tc/types.go
+++ b/lib/go-tc/types.go
@@ -22,6 +22,7 @@ package tc
 import (
        "database/sql"
        "errors"
+       "time"
 )
 
 // TypesResponse is the type of a response from Traffic Ops to a GET request
@@ -49,6 +50,28 @@ type TypeNullable struct {
        UseInTable  *string    `json:"useInTable" db:"use_in_table"`
 }
 
+// TypesResponseV5 is type struct response used for the latest minor version 
associated with api major version 5.
+type TypesResponseV5 = TypesResponseV50
+
+// TypesResponseV50 is the type of response (for RFC3339) from Traffic Ops to 
a GET Request
+// made to its /types API endpoint.
+type TypesResponseV50 struct {
+       Response []TypeV50 `json:"response"`
+       Alerts
+}
+
+// TypeV5 contains information about a given Type in Traffic Ops used for the 
latest minor version associated with api major version 5.
+type TypeV5 = TypeV50
+
+// TypeV50 contains information about a given Type in Traffic Ops.
+type TypeV50 struct {
+       ID          int       `json:"id"`
+       LastUpdated time.Time `json:"lastUpdated"`
+       Name        string    `json:"name"`
+       Description string    `json:"description"`
+       UseInTable  string    `json:"useInTable"`
+}
+
 // GetTypeData returns the type's name and use_in_table, true/false if the
 // query returned data, and any error.
 //
diff --git a/traffic_ops/testing/api/v5/traffic_control_test.go 
b/traffic_ops/testing/api/v5/traffic_control_test.go
index 409da97887..4279332f36 100644
--- a/traffic_ops/testing/api/v5/traffic_control_test.go
+++ b/traffic_ops/testing/api/v5/traffic_control_test.go
@@ -55,7 +55,7 @@ type TrafficControl struct {
        Tenants                                           []tc.Tenant           
                  `json:"tenants"`
        ServerCheckExtensions                             
[]tc.ServerCheckExtensionNullable       `json:"servercheck_extensions"`
        Topologies                                        []tc.Topology         
                  `json:"topologies"`
-       Types                                             []tc.Type             
                  `json:"types"`
+       Types                                             []tc.TypeV5           
                  `json:"types"`
        SteeringTargets                                   
[]tc.SteeringTargetNullable             `json:"steeringTargets"`
        Serverchecks                                      
[]tc.ServercheckRequestNullable         `json:"serverchecks"`
        Users                                             []tc.UserV4           
                  `json:"users"`
diff --git a/traffic_ops/testing/api/v5/types_test.go 
b/traffic_ops/testing/api/v5/types_test.go
index e23166b56d..307744eee0 100644
--- a/traffic_ops/testing/api/v5/types_test.go
+++ b/traffic_ops/testing/api/v5/types_test.go
@@ -38,7 +38,7 @@ func TestTypes(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.Type]{
+               methodTests := utils.TestCase[client.Session, 
client.RequestOptions, tc.TypeV5]{
                        "GET": {
                                "NOT MODIFIED when NO CHANGES made": {
                                        ClientSession: TOSession,
@@ -65,7 +65,7 @@ func TestTypes(t *testing.T) {
                        "POST": {
                                "BAD REQUEST when INVALID useInTable NOT 
server": {
                                        ClientSession: TOSession,
-                                       RequestBody: tc.Type{
+                                       RequestBody: tc.TypeV5{
                                                Description: "Host header 
regular expression-Test",
                                                Name:        "TEST_1",
                                                UseInTable:  "regex",
@@ -74,12 +74,12 @@ func TestTypes(t *testing.T) {
                                },
                                "OK when VALID request when useInTable=server": 
{
                                        ClientSession: TOSession,
-                                       RequestBody: tc.Type{
+                                       RequestBody: tc.TypeV5{
                                                Description: "Host header 
regular expression-Test",
                                                Name:        "TEST_4",
                                                UseInTable:  "server",
                                        },
-                                       Expectations: 
utils.CkRequest(utils.NoError(), utils.HasStatus(http.StatusOK),
+                                       Expectations: 
utils.CkRequest(utils.NoError(), utils.HasStatus(http.StatusCreated),
                                                
validateTypeUpdateCreateFields("TEST_4", map[string]interface{}{"Name": 
"TEST_4"})),
                                },
                        },
@@ -87,7 +87,7 @@ func TestTypes(t *testing.T) {
                                "BAD REQUEST when useInTable NOT server": {
                                        EndpointID:    GetTypeID(t, 
"ACTIVE_DIRECTORY"),
                                        ClientSession: TOSession,
-                                       RequestBody: tc.Type{
+                                       RequestBody: tc.TypeV5{
                                                Description: "Active Directory 
User",
                                                Name:        "TEST_3",
                                                UseInTable:  "cachegroup",
@@ -97,7 +97,7 @@ func TestTypes(t *testing.T) {
                                "OK when VALID request when useInTable=server": 
{
                                        EndpointID:    GetTypeID(t, "RIAK"),
                                        ClientSession: TOSession,
-                                       RequestBody: tc.Type{
+                                       RequestBody: tc.TypeV5{
                                                Description: "riak type",
                                                Name:        "TEST_5",
                                                UseInTable:  "server",
@@ -158,7 +158,7 @@ func validateTypeSort() utils.CkReqFunc {
        return func(t *testing.T, _ toclientlib.ReqInf, resp interface{}, 
alerts tc.Alerts, _ error) {
                assert.RequireNotNil(t, resp, "Expected Type response to not be 
nil.")
                var typeNames []string
-               typeResp := resp.([]tc.Type)
+               typeResp := resp.([]tc.TypeV5)
                for _, typ := range typeResp {
                        typeNames = append(typeNames, typ.Name)
                }
@@ -169,7 +169,7 @@ func validateTypeSort() utils.CkReqFunc {
 func validateTypeFields(expectedResp map[string]interface{}) utils.CkReqFunc {
        return func(t *testing.T, _ toclientlib.ReqInf, resp interface{}, _ 
tc.Alerts, _ error) {
                assert.RequireNotNil(t, resp, "Expected Type response to not be 
nil.")
-               typeResp := resp.([]tc.Type)
+               typeResp := resp.([]tc.TypeV5)
                for field, expected := range expectedResp {
                        for _, typ := range typeResp {
                                switch field {
diff --git a/traffic_ops/traffic_ops_golang/routing/routes.go 
b/traffic_ops/traffic_ops_golang/routing/routes.go
index fd92566d82..396db1766c 100644
--- a/traffic_ops/traffic_ops_golang/routing/routes.go
+++ b/traffic_ops/traffic_ops_golang/routing/routes.go
@@ -345,10 +345,10 @@ func Routes(d ServerData) ([]Route, http.Handler, error) {
                {Version: api.Version{Major: 5, Minor: 0}, Method: 
http.MethodGet, Path: `system/info/?$`, Handler: systeminfo.Get, 
RequiredPrivLevel: auth.PrivLevelReadOnly, RequiredPermissions: nil, 
Authenticated: Authenticated, Middlewares: nil, ID: 42104747531},
 
                //Type: CRUD
-               {Version: api.Version{Major: 5, Minor: 0}, Method: 
http.MethodGet, Path: `types/?$`, Handler: api.ReadHandler(&types.TOType{}), 
RequiredPrivLevel: auth.PrivLevelReadOnly, RequiredPermissions: 
[]string{"TYPE:READ"}, Authenticated: Authenticated, Middlewares: nil, ID: 
422670182331},
-               {Version: api.Version{Major: 5, Minor: 0}, Method: 
http.MethodPut, Path: `types/{id}$`, Handler: 
api.UpdateHandler(&types.TOType{}), RequiredPrivLevel: 
auth.PrivLevelOperations, RequiredPermissions: []string{"TYPE:UPDATE", 
"TYPE:READ"}, Authenticated: Authenticated, Middlewares: nil, ID: 4886011531},
-               {Version: api.Version{Major: 5, Minor: 0}, Method: 
http.MethodPost, Path: `types/?$`, Handler: api.CreateHandler(&types.TOType{}), 
RequiredPrivLevel: auth.PrivLevelOperations, RequiredPermissions: 
[]string{"TYPE:CREATE", "TYPE:READ"}, Authenticated: Authenticated, 
Middlewares: nil, ID: 451330819531},
-               {Version: api.Version{Major: 5, Minor: 0}, Method: 
http.MethodDelete, Path: `types/{id}$`, Handler: 
api.DeleteHandler(&types.TOType{}), RequiredPrivLevel: 
auth.PrivLevelOperations, RequiredPermissions: []string{"TYPE:DELETE", 
"TYPE:READ"}, Authenticated: Authenticated, Middlewares: nil, ID: 4317577331},
+               {Version: api.Version{Major: 5, Minor: 0}, Method: 
http.MethodGet, Path: `types/?$`, Handler: types.Read, RequiredPrivLevel: 
auth.PrivLevelReadOnly, RequiredPermissions: []string{"TYPE:READ"}, 
Authenticated: Authenticated, Middlewares: nil, ID: 422670182331},
+               {Version: api.Version{Major: 5, Minor: 0}, Method: 
http.MethodPut, Path: `types/{id}$`, Handler: types.Update, RequiredPrivLevel: 
auth.PrivLevelOperations, RequiredPermissions: []string{"TYPE:UPDATE", 
"TYPE:READ"}, Authenticated: Authenticated, Middlewares: nil, ID: 4886011531},
+               {Version: api.Version{Major: 5, Minor: 0}, Method: 
http.MethodPost, Path: `types/?$`, Handler: types.Create, RequiredPrivLevel: 
auth.PrivLevelOperations, RequiredPermissions: []string{"TYPE:CREATE", 
"TYPE:READ"}, Authenticated: Authenticated, Middlewares: nil, ID: 451330819531},
+               {Version: api.Version{Major: 5, Minor: 0}, Method: 
http.MethodDelete, Path: `types/{id}$`, Handler: types.Delete, 
RequiredPrivLevel: auth.PrivLevelOperations, RequiredPermissions: 
[]string{"TYPE:DELETE", "TYPE:READ"}, Authenticated: Authenticated, 
Middlewares: nil, ID: 4317577331},
 
                //About
                {Version: api.Version{Major: 5, Minor: 0}, Method: 
http.MethodGet, Path: `about/?$`, Handler: about.Handler(), RequiredPrivLevel: 
auth.PrivLevelReadOnly, RequiredPermissions: nil, Authenticated: Authenticated, 
Middlewares: nil, ID: 431750116631},
diff --git a/traffic_ops/traffic_ops_golang/types/types.go 
b/traffic_ops/traffic_ops_golang/types/types.go
index 858715ebc8..ed77f4b941 100644
--- a/traffic_ops/traffic_ops_golang/types/types.go
+++ b/traffic_ops/traffic_ops_golang/types/types.go
@@ -21,17 +21,20 @@ package types
 
 import (
        "database/sql"
+       "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"
 )
@@ -211,3 +214,238 @@ func deleteQuery() string {
 WHERE id=:id`
        return query
 }
+
+// Read [V5] - gets a list of types for APIv5
+func Read(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{
+               "name":       {Column: "typ.name"},
+               "id":         {Column: "typ.id", Checker: api.IsInt},
+               "useInTable": {Column: "typ.use_in_table"},
+       }
+       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 := selectQuery() + where + orderBy + pagination
+       rows, err := tx.NamedQuery(query, queryValues)
+       if err != nil {
+               api.HandleErr(w, r, tx.Tx, http.StatusInternalServerError, nil, 
fmt.Errorf("type get: error getting type(s): %w", err))
+       }
+       defer log.Close(rows, "unable to close DB connection")
+
+       typ := tc.TypeV5{}
+       typeList := []tc.TypeV5{}
+       for rows.Next() {
+               if err = rows.Scan(&typ.ID, &typ.Name, &typ.Description, 
&typ.UseInTable, &typ.LastUpdated); err != nil {
+                       api.HandleErr(w, r, tx.Tx, 
http.StatusInternalServerError, nil, fmt.Errorf("error getting type(s): %w", 
err))
+               }
+               typeList = append(typeList, typ)
+       }
+
+       api.WriteResp(w, r, typeList)
+       return
+}
+
+// Create [V5] -  creates the type with the passed data fpr APIv5.
+func Create(w http.ResponseWriter, r *http.Request) {
+       typ := tc.TypeV5{}
+
+       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
+
+       typ, readValErr := readAndValidateJsonStructV5(r)
+       if readValErr != nil {
+               api.HandleErr(w, r, tx, http.StatusBadRequest, readValErr, nil)
+               return
+       }
+
+       if typ.UseInTable != "server" {
+               api.HandleErr(w, r, tx, http.StatusBadRequest, fmt.Errorf("can 
not create type."), nil)
+               return
+       }
+
+       // check if type already exists
+       var exists bool
+       err := tx.QueryRow(`SELECT EXISTS(SELECT * from type where name = $1)`, 
typ.Name).Scan(&exists)
+
+       if err != nil {
+               api.HandleErr(w, r, tx, http.StatusInternalServerError, nil, 
fmt.Errorf("error: %w, when checking if type with name %s exists", err, 
typ.Name))
+               return
+       }
+       if exists {
+               api.HandleErr(w, r, tx, http.StatusBadRequest, fmt.Errorf("type 
name '%s' already exists.", typ.Name), nil)
+               return
+       }
+
+       // create type
+       query := `INSERT INTO type (name, description, use_in_table) VALUES 
($1, $2, $3) RETURNING id,last_updated`
+       err = tx.QueryRow(query, typ.Name, typ.Description, 
typ.UseInTable).Scan(&typ.ID, &typ.LastUpdated)
+       if err != nil {
+               if errors.Is(err, sql.ErrNoRows) {
+                       api.HandleErr(w, r, tx, http.StatusInternalServerError, 
fmt.Errorf("error: %w in creating type with name: %s", err, typ.Name), nil)
+                       return
+               }
+               usrErr, sysErr, code := api.ParseDBError(err)
+               api.HandleErr(w, r, tx, code, usrErr, sysErr)
+               return
+       }
+       alerts := tc.CreateAlerts(tc.SuccessLevel, "type was created.")
+       w.Header().Set("Location", fmt.Sprintf("/api/%d.%d/type?name=%s", 
inf.Version.Major, inf.Version.Minor, typ.Name))
+       api.WriteAlertsObj(w, r, http.StatusCreated, alerts, typ)
+       return
+}
+
+// Update [V5] - updates name & description of the type passed 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
+
+       typ, readValErr := readAndValidateJsonStructV5(r)
+       if readValErr != nil {
+               api.HandleErr(w, r, tx, http.StatusBadRequest, readValErr, nil)
+               return
+       }
+
+       requestedId := inf.IntParams["id"]
+       // check if the entity was already updated
+       userErr, sysErr, errCode = api.CheckIfUnModified(r.Header, inf.Tx, 
requestedId, "type")
+       if userErr != nil || sysErr != nil {
+               api.HandleErr(w, r, tx, errCode, userErr, sysErr)
+               return
+       }
+
+       if typ.UseInTable != "server" {
+               api.HandleErr(w, r, tx, http.StatusBadRequest, fmt.Errorf("can 
not update type."), nil)
+               return
+       }
+
+       //update type query
+       query := `UPDATE type typ SET name= $1, description= $2, use_in_table= 
$3 WHERE typ.id=$4 RETURNING typ.id, typ.last_updated`
+
+       err := tx.QueryRow(query, typ.Name, typ.Description, typ.UseInTable, 
requestedId).Scan(&typ.ID, &typ.LastUpdated)
+       if err != nil {
+               if errors.Is(err, sql.ErrNoRows) {
+                       api.HandleErr(w, r, tx, http.StatusNotFound, 
fmt.Errorf("type with id: %d not found", requestedId), nil)
+                       return
+               }
+               usrErr, sysErr, code := api.ParseDBError(err)
+               api.HandleErr(w, r, tx, code, usrErr, sysErr)
+               return
+       }
+       alerts := tc.CreateAlerts(tc.SuccessLevel, "type was updated")
+       api.WriteAlertsObj(w, r, http.StatusOK, alerts, typ)
+       return
+}
+
+// Delete [V5] - deletes the type passed 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()
+
+       id := inf.Params["id"]
+       // check if type already exists
+       var exists bool
+       err := tx.QueryRow(`SELECT EXISTS(SELECT * from type where id = $1)`, 
id).Scan(&exists)
+
+       if err != nil {
+               api.HandleErr(w, r, tx, http.StatusInternalServerError, nil, 
err)
+               return
+       }
+       if !exists {
+               api.HandleErr(w, r, tx, http.StatusNotFound, fmt.Errorf("can 
not delete type"), nil)
+               return
+       }
+
+       res, err := tx.Exec("DELETE FROM type AS typ WHERE typ.id=$1", id)
+       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 type: %w", err))
+               return
+       }
+       if rowsAffected == 0 {
+               api.HandleErr(w, r, tx, http.StatusInternalServerError, nil, 
fmt.Errorf("no rows deleted for type"))
+               return
+       }
+
+       alertMessage := fmt.Sprintf("type was deleted.")
+       alerts := tc.CreateAlerts(tc.SuccessLevel, alertMessage)
+       api.WriteAlerts(w, r, http.StatusOK, alerts)
+       return
+}
+
+// readAndValidateJsonStructV5 [V5] - validates the JSON object passed.
+func readAndValidateJsonStructV5(r *http.Request) (tc.TypeV5, error) {
+       var typ tc.TypeV5
+       if err := json.NewDecoder(r.Body).Decode(&typ); err != nil {
+               userErr := fmt.Errorf("error decoding POST request body into 
TypeV5 struct %w", err)
+               return typ, userErr
+       }
+
+       // validate JSON body
+       rule := 
validation.NewStringRule(tovalidate.IsAlphanumericUnderscoreDash, "must consist 
of only alphanumeric, dash, or underscore characters")
+       errs := tovalidate.ToErrors(validation.Errors{
+               "name":         validation.Validate(typ.Name, 
validation.Required, rule),
+               "description":  validation.Validate(typ.Description, 
validation.Required),
+               "use_in_table": validation.Validate(typ.UseInTable, 
validation.Required),
+       })
+       if len(errs) > 0 {
+               userErr := util.JoinErrs(errs)
+               return typ, userErr
+       }
+       return typ, nil
+}
+
+// SelectMaxLastUpdatedQuery used for TryIfModifiedSinceQuery()
+func SelectMaxLastUpdatedQuery(where string) string {
+       return `SELECT max(t) from (
+               SELECT max(last_updated) as t from type typ ` + where +
+               ` UNION ALL
+       select max(last_updated) as t from last_deleted l where 
l.table_name='type') as res`
+}
diff --git a/traffic_ops/v5-client/type.go b/traffic_ops/v5-client/type.go
index 521f1d8a94..6572c51a5a 100644
--- a/traffic_ops/v5-client/type.go
+++ b/traffic_ops/v5-client/type.go
@@ -26,14 +26,14 @@ import (
 const apiTypes = "/types"
 
 // CreateType creates the given Type. There should be a very good reason for 
doing this.
-func (to *Session) CreateType(typ tc.Type, opts RequestOptions) (tc.Alerts, 
toclientlib.ReqInf, error) {
+func (to *Session) CreateType(typ tc.TypeV5, opts RequestOptions) (tc.Alerts, 
toclientlib.ReqInf, error) {
        var alerts tc.Alerts
        reqInf, err := to.post(apiTypes, opts, typ, &alerts)
        return alerts, reqInf, err
 }
 
 // UpdateType replaces the Type identified by 'id' with the one provided.
-func (to *Session) UpdateType(id int, typ tc.Type, opts RequestOptions) 
(tc.Alerts, toclientlib.ReqInf, error) {
+func (to *Session) UpdateType(id int, typ tc.TypeV5, opts RequestOptions) 
(tc.Alerts, toclientlib.ReqInf, error) {
        route := fmt.Sprintf("%s/%d", apiTypes, id)
        var alerts tc.Alerts
        reqInf, err := to.put(route, opts, typ, &alerts)
@@ -44,8 +44,8 @@ func (to *Session) UpdateType(id int, typ tc.Type, opts 
RequestOptions) (tc.Aler
 // If a 'useInTable' parameter is passed, the returned Types are restricted to 
those with
 // that exact 'useInTable' property. Only exactly 1 or exactly 0 'useInTable' 
parameters may
 // be passed; passing more will result in an error being returned.
-func (to *Session) GetTypes(opts RequestOptions) (tc.TypesResponse, 
toclientlib.ReqInf, error) {
-       var data tc.TypesResponse
+func (to *Session) GetTypes(opts RequestOptions) (tc.TypesResponseV5, 
toclientlib.ReqInf, error) {
+       var data tc.TypesResponseV5
        reqInf, err := to.get(apiTypes, opts, &data)
        return data, reqInf, err
 }

Reply via email to