You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@trafficcontrol.apache.org by zr...@apache.org on 2023/05/18 18:07:41 UTC
[trafficcontrol] branch master updated: Fixes Server Capabilities apis to respond with RFC3339 date/time Format #7408 (#7482)
This is an automated email from the ASF dual-hosted git repository.
zrhoffman 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 147db8d363 Fixes Server Capabilities apis to respond with RFC3339 date/time Format #7408 (#7482)
147db8d363 is described below
commit 147db8d363859de9595c1cd5105d6f0f16277ff7
Author: Jagan Parthiban <33...@users.noreply.github.com>
AuthorDate: Thu May 18 23:37:34 2023 +0530
Fixes Server Capabilities apis to respond with RFC3339 date/time Format #7408 (#7482)
* Fixes Server Capabilities V5 Traffic Ops API to use and respond with RFC3339 date/time strings
* Update CHANGELOG.md with issue details
* Update .rst doc files for RF3339 in server capabilities.
Issue: https://github.com/apache/trafficcontrol/issues/7465
* Updated error message
---
CHANGELOG.md | 1 +
docs/source/api/v5/server_capabilities.rst | 20 +--
lib/go-tc/server_capabilities.go | 30 ++++
.../testing/api/v5/server_capabilities_test.go | 54 ++++----
traffic_ops/testing/api/v5/traffic_control_test.go | 2 +-
traffic_ops/traffic_ops_golang/routing/routes.go | 6 +-
.../servercapability/servercapability.go | 154 +++++++++++++++++++++
traffic_ops/v5-client/servercapability.go | 18 +--
8 files changed, 235 insertions(+), 50 deletions(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 55737cb8fc..1c63c242c7 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -56,6 +56,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/).
### Fixed
- [#7511](https://github.com/apache/trafficcontrol/pull/7511) *Traffic Ops* Fixed the changelog registration message to include the username instead of duplicate email entry.
- [#7505](https://github.com/apache/trafficcontrol/pull/7505) *Traffic Portal* Fix an issue where a Delivery Service with Geo Limit Countries Set was unable to be updated.
+- [#7465](https://github.com/apache/trafficcontrol/issues/7465) *Traffic Ops* Fixes server_capabilities v5 apis to respond with RFC3339 date/time Format
- [#7441](https://github.com/apache/trafficcontrol/pull/7441) *Traffic Ops* Fixed the invalidation jobs endpoint to respect CDN locks.
- [#7413](https://github.com/apache/trafficcontrol/issues/7413) *Traffic Ops* Fixes service_category apis to respond with RFC3339 date/time Format
- [#7414](https://github.com/apache/trafficcontrol/pull/7414) * Traffic Portal* Fixed DSR difference for DS required capability.
diff --git a/docs/source/api/v5/server_capabilities.rst b/docs/source/api/v5/server_capabilities.rst
index 4827ca8ad7..ad68ca501d 100644
--- a/docs/source/api/v5/server_capabilities.rst
+++ b/docs/source/api/v5/server_capabilities.rst
@@ -51,7 +51,7 @@ Response Structure
------------------
:name: The name of this :term:`Server Capability`
:description: The description of this :term:`Server Capability`
-:lastUpdated: The date and time at which this :term:`Server Capability` was last updated, in ISO-like format
+:lastUpdated: The date and time at which this :term:`Server Capability` was last updated, in :rfc:`3339` Format
.. code-block:: http
:caption: Response Example
@@ -65,7 +65,7 @@ Response Structure
Set-Cookie: mojolicious=...; Path=/; Expires=Mon, 18 Nov 2019 17:40:54 GMT; Max-Age=3600; HttpOnly
Whole-Content-Sha512: EH8jo8OrCu79Tz9xpgT3YRyKJ/p2NcTmbS3huwtqRByHz9H6qZLQjA59RIPaVSq3ZxsU6QhTaox5nBkQ9LPSAA==
X-Server-Name: traffic_ops_golang/
- Date: Mon, 07 Oct 2019 21:36:13 GMT
+ Date: Wed, 03 May 2023 07:03:45 GMT
Content-Length: 68
{
@@ -73,7 +73,7 @@ Response Structure
{
"name": "RAM",
"description": "ram server capability",
- "lastUpdated": "2019-10-07 20:38:24+00"
+ "lastUpdated": "2023-05-03T12:24:40.409579+05:30"
}
]
}
@@ -112,7 +112,7 @@ Response Structure
------------------
:name: The name of this :term:`Server Capability`
:description: The description of this :term:`Server Capability`
-:lastUpdated: The date and time at which this :term:`Server Capability` was last updated, in ISO-like format
+:lastUpdated: The date and time at which this :term:`Server Capability` was last updated, in :rfc:`3339` Format
.. code-block:: http
:caption: Response Example
@@ -126,7 +126,7 @@ Response Structure
Set-Cookie: mojolicious=...; Path=/; Expires=Mon, 18 Nov 2019 17:40:54 GMT; Max-Age=3600; HttpOnly
Whole-Content-Sha512: ysdopC//JQI79BRUa61s6M2HzHxYHpo5RdcuauOoqCYxiVOoUhNZfOVydVkv8zDN2qA374XKnym4kWj3VzQIXg==
X-Server-Name: traffic_ops_golang/
- Date: Mon, 07 Oct 2019 22:10:00 GMT
+ Date: Wed, 03 May 2023 07:02:02 GMT
Content-Length: 137
{
@@ -139,7 +139,7 @@ Response Structure
"response": {
"name": "RAM",
"description": "ram server capability",
- "lastUpdated": "2019-10-07 22:10:00+00"
+ "lastUpdated": "2023-05-03T12:24:40.409579+05:30"
}
}
@@ -177,7 +177,7 @@ Response Structure
------------------
:name: The name of this :term:`Server Capability`
:description: The description of this :term:`Server Capability`
-:lastUpdated: The date and time at which this :term:`Server Capability` was last updated, in ISO-like format
+:lastUpdated: The date and time at which this :term:`Server Capability` was last updated, in :rfc:`3339` Format
.. code-block:: http
:caption: Response Example
@@ -191,7 +191,7 @@ Response Structure
Set-Cookie: mojolicious=...; Path=/; Expires=Mon, 18 Nov 2019 17:40:54 GMT; Max-Age=3600; HttpOnly
Whole-Content-Sha512: ysdopC//JQI79BRUa61s6M2HzHxYHpo5RdcuauOoqCYxiVOoUhNZfOVydVkv8zDN2qA374XKnym4kWj3VzQIXg==
X-Server-Name: traffic_ops_golang/
- Date: Wed, 03 March 2021 21:22:08 GMT
+ Date: Wed, 03 May 2023 07:02:02 GMT
Content-Length: 137
{
@@ -204,7 +204,7 @@ Response Structure
"response": {
"name": "HDD",
"description": "HDD server capability",
- "lastUpdated": "2021-03-03 21:22:08+00"
+ "lastUpdated": "2023-05-03T12:24:40.409579+05:30"
}
}
@@ -251,7 +251,7 @@ Response Structure
Set-Cookie: mojolicious=...; Path=/; Expires=Mon, 18 Nov 2019 17:40:54 GMT; Max-Age=3600; HttpOnly
Whole-Content-Sha512: 8zCAATbCzcqiqigGVBy7WF1duDuXu1Wg2DBe9yfqTw/c+yhE2eUk73hFTA/Oqt0kocaN7+1GkbFdPkQPvbnRaA==
X-Server-Name: traffic_ops_golang/
- Date: Mon, 07 Oct 2019 20:44:40 GMT
+ Date: Wed, 03 May 2023 07:02:02 GMT
Content-Length: 72
{
diff --git a/lib/go-tc/server_capabilities.go b/lib/go-tc/server_capabilities.go
index 328cb6933f..943118e2a7 100644
--- a/lib/go-tc/server_capabilities.go
+++ b/lib/go-tc/server_capabilities.go
@@ -19,6 +19,8 @@ package tc
* under the License.
*/
+import "time"
+
// ServerCapabilitiesResponse contains the result data from a GET /server_capabilities request.
type ServerCapabilitiesResponse struct {
Response []ServerCapability `json:"response"`
@@ -57,3 +59,31 @@ type ServerCapabilityDetailResponseV41 struct {
Response ServerCapabilityV41 `json:"response"`
Alerts
}
+
+// ServerCapabilityV5 is an alias for the latest minor version for the major version 5.
+type ServerCapabilityV5 ServerCapabilityV51
+
+// ServerCapabilityV51 contains information about a given serverCapability in Traffic Ops V5.
+type ServerCapabilityV51 struct {
+ Name string `json:"name" db:"name"`
+ LastUpdated time.Time `json:"lastUpdated" db:"last_updated"`
+ Description string `json:"description" db:"description"`
+}
+
+// ServerCapabilitiesResponseV5 is an alias for the latest minor version for the major version 5.
+type ServerCapabilitiesResponseV5 ServerCapabilitiesResponseV51
+
+// ServerCapabilitiesResponseV51 contains the result data from a GET(v5.1 and above) /server_capabilities request.
+type ServerCapabilitiesResponseV51 struct {
+ Response []ServerCapabilityV5 `json:"response"`
+ Alerts
+}
+
+// ServerCapabilityDetailResponseV5 is an alias for the latest minor version for the major version 5.
+type ServerCapabilityDetailResponseV5 ServerCapabilityDetailResponseV51
+
+// ServerCapabilityDetailResponseV51 contains the result data from a POST(v5.1 and above) /server_capabilities request.
+type ServerCapabilityDetailResponseV51 struct {
+ Response ServerCapabilityV5 `json:"response"`
+ Alerts
+}
diff --git a/traffic_ops/testing/api/v5/server_capabilities_test.go b/traffic_ops/testing/api/v5/server_capabilities_test.go
index b5f678356d..36645126b1 100644
--- a/traffic_ops/testing/api/v5/server_capabilities_test.go
+++ b/traffic_ops/testing/api/v5/server_capabilities_test.go
@@ -36,7 +36,7 @@ func TestServerCapabilities(t *testing.T) {
currentTime := time.Now().UTC().Add(-15 * time.Second)
currentTimeRFC := currentTime.Format(time.RFC1123)
- methodTests := utils.TestCase[client.Session, client.RequestOptions, tc.ServerCapabilityV41]{
+ methodTests := utils.TestCase[client.Session, client.RequestOptions, tc.ServerCapabilityV5]{
"GET": {
"OK when VALID request": {
ClientSession: TOSession,
@@ -57,17 +57,17 @@ func TestServerCapabilities(t *testing.T) {
"POST": {
"BAD REQUEST when ALREADY EXISTS": {
ClientSession: TOSession,
- RequestBody: tc.ServerCapabilityV41{
- ServerCapability: tc.ServerCapability{Name: "foo"},
- Description: "foo servers",
+ RequestBody: tc.ServerCapabilityV5{
+ Name: "foo",
+ Description: "foo servers",
},
Expectations: utils.CkRequest(utils.HasError(), utils.HasStatus(http.StatusBadRequest)),
},
"BAD REQUEST when INVALID NAME": {
ClientSession: TOSession,
- RequestBody: tc.ServerCapabilityV41{
- ServerCapability: tc.ServerCapability{Name: "b@dname"},
- Description: "Server Capability",
+ RequestBody: tc.ServerCapabilityV5{
+ Name: "b@dname",
+ Description: "Server Capability",
},
Expectations: utils.CkRequest(utils.HasError(), utils.HasStatus(http.StatusBadRequest)),
},
@@ -76,9 +76,9 @@ func TestServerCapabilities(t *testing.T) {
"OK when VALID request": {
ClientSession: TOSession,
RequestOpts: client.RequestOptions{QueryParameters: url.Values{"name": {"blah"}}},
- RequestBody: tc.ServerCapabilityV41{
- ServerCapability: tc.ServerCapability{Name: "newname"},
- Description: "Server Capability for new name",
+ RequestBody: tc.ServerCapabilityV5{
+ Name: "newname",
+ Description: "Server Capability for new name",
},
Expectations: utils.CkRequest(utils.NoError(), utils.HasStatus(http.StatusOK),
validateServerCapabilitiesUpdateFields(map[string]interface{}{"Name": "newname"}),
@@ -87,9 +87,9 @@ func TestServerCapabilities(t *testing.T) {
"BAD REQUEST when NAME DOESNT EXIST": {
ClientSession: TOSession,
RequestOpts: client.RequestOptions{QueryParameters: url.Values{"name": {"invalid"}}},
- RequestBody: tc.ServerCapabilityV41{
- ServerCapability: tc.ServerCapability{Name: "newname"},
- Description: "Server Capability for new name",
+ RequestBody: tc.ServerCapabilityV5{
+ Name: "newname",
+ Description: "Server Capability for new name",
},
Expectations: utils.CkRequest(utils.HasError(), utils.HasStatus(http.StatusBadRequest)),
},
@@ -99,9 +99,9 @@ func TestServerCapabilities(t *testing.T) {
QueryParameters: url.Values{"name": {"disk"}},
Header: http.Header{rfc.IfUnmodifiedSince: {currentTimeRFC}},
},
- RequestBody: tc.ServerCapabilityV41{
- ServerCapability: tc.ServerCapability{Name: "newname"},
- Description: "Server Capability for new name",
+ RequestBody: tc.ServerCapabilityV5{
+ Name: "newname",
+ Description: "Server Capability for new name",
},
Expectations: utils.CkRequest(utils.HasError(), utils.HasStatus(http.StatusPreconditionFailed)),
},
@@ -111,9 +111,9 @@ func TestServerCapabilities(t *testing.T) {
QueryParameters: url.Values{"name": {"disk"}},
Header: http.Header{rfc.IfMatch: {rfc.ETag(currentTime)}},
},
- RequestBody: tc.ServerCapabilityV41{
- ServerCapability: tc.ServerCapability{Name: "newname"},
- Description: "Server Capability for new name",
+ RequestBody: tc.ServerCapabilityV5{
+ Name: "newname",
+ Description: "Server Capability for new name",
},
Expectations: utils.CkRequest(utils.HasError(), utils.HasStatus(http.StatusPreconditionFailed)),
},
@@ -138,21 +138,21 @@ func TestServerCapabilities(t *testing.T) {
switch method {
case "GET":
t.Run(name, func(t *testing.T) {
- resp, reqInf, err := testCase.ClientSession.GetServerCapabilities(testCase.RequestOpts)
+ resp, reqInf, err := testCase.ClientSession.GetServerCapabilitiesV5(testCase.RequestOpts)
for _, check := range testCase.Expectations {
check(t, reqInf, resp.Response, resp.Alerts, err)
}
})
case "POST":
t.Run(name, func(t *testing.T) {
- resp, reqInf, err := testCase.ClientSession.CreateServerCapability(testCase.RequestBody, testCase.RequestOpts)
+ resp, reqInf, err := testCase.ClientSession.CreateServerCapabilityV5(testCase.RequestBody, testCase.RequestOpts)
for _, check := range testCase.Expectations {
check(t, reqInf, resp.Response, resp.Alerts, err)
}
})
case "PUT":
t.Run(name, func(t *testing.T) {
- resp, reqInf, err := testCase.ClientSession.UpdateServerCapability(testCase.RequestOpts.QueryParameters["name"][0], testCase.RequestBody, testCase.RequestOpts)
+ resp, reqInf, err := testCase.ClientSession.UpdateServerCapabilityV5(testCase.RequestOpts.QueryParameters["name"][0], testCase.RequestBody, testCase.RequestOpts)
for _, check := range testCase.Expectations {
check(t, reqInf, resp.Response, resp.Alerts, err)
}
@@ -174,7 +174,7 @@ func TestServerCapabilities(t *testing.T) {
func validateServerCapabilitiesUpdateFields(expectedResp map[string]interface{}) utils.CkReqFunc {
return func(t *testing.T, _ toclientlib.ReqInf, resp interface{}, _ tc.Alerts, _ error) {
assert.RequireNotNil(t, resp, "Expected Server Capabilities response to not be nil.")
- serverCapabilitiesResp := resp.(tc.ServerCapabilityV41)
+ serverCapabilitiesResp := resp.(tc.ServerCapabilityV5)
for field, expected := range expectedResp {
switch field {
case "Name":
@@ -190,7 +190,7 @@ func validateServerCapabilitiesSort() utils.CkReqFunc {
return func(t *testing.T, _ toclientlib.ReqInf, resp interface{}, alerts tc.Alerts, _ error) {
assert.RequireNotNil(t, resp, "Expected Server Capabilities response to not be nil.")
var serverCapabilityNames []string
- serverCapabilitiesResp := resp.([]tc.ServerCapabilityV41)
+ serverCapabilitiesResp := resp.([]tc.ServerCapabilityV5)
for _, serverCapability := range serverCapabilitiesResp {
serverCapabilityNames = append(serverCapabilityNames, serverCapability.Name)
}
@@ -200,13 +200,13 @@ func validateServerCapabilitiesSort() utils.CkReqFunc {
func CreateTestServerCapabilities(t *testing.T) {
for _, sc := range testData.ServerCapabilities {
- resp, _, err := TOSession.CreateServerCapability(sc, client.RequestOptions{})
+ resp, _, err := TOSession.CreateServerCapabilityV5(sc, client.RequestOptions{})
assert.RequireNoError(t, err, "Unexpected error creating Server Capability '%s': %v - alerts: %+v", sc.Name, err, resp.Alerts)
}
}
func DeleteTestServerCapabilities(t *testing.T) {
- serverCapabilities, _, err := TOSession.GetServerCapabilities(client.RequestOptions{})
+ serverCapabilities, _, err := TOSession.GetServerCapabilitiesV5(client.RequestOptions{})
assert.NoError(t, err, "Cannot get Server Capabilities: %v - alerts: %+v", err, serverCapabilities.Alerts)
for _, serverCapability := range serverCapabilities.Response {
@@ -215,7 +215,7 @@ func DeleteTestServerCapabilities(t *testing.T) {
// Retrieve the Server Capability to see if it got deleted
opts := client.NewRequestOptions()
opts.QueryParameters.Set("name", serverCapability.Name)
- getServerCapability, _, err := TOSession.GetServerCapabilities(opts)
+ getServerCapability, _, err := TOSession.GetServerCapabilitiesV5(opts)
assert.NoError(t, err, "Error getting Server Capability '%s' after deletion: %v - alerts: %+v", serverCapability.Name, err, getServerCapability.Alerts)
assert.Equal(t, 0, len(getServerCapability.Response), "Expected Server Capability '%s' to be deleted, but it was found in Traffic Ops", serverCapability.Name)
}
diff --git a/traffic_ops/testing/api/v5/traffic_control_test.go b/traffic_ops/testing/api/v5/traffic_control_test.go
index b6673951c8..bd18b2b446 100644
--- a/traffic_ops/testing/api/v5/traffic_control_test.go
+++ b/traffic_ops/testing/api/v5/traffic_control_test.go
@@ -47,7 +47,7 @@ type TrafficControl struct {
Roles []tc.RoleV4 `json:"roles"`
Servers []tc.ServerV4 `json:"servers"`
ServerServerCapabilities []tc.ServerServerCapability `json:"serverServerCapabilities"`
- ServerCapabilities []tc.ServerCapabilityV41 `json:"serverCapabilities"`
+ ServerCapabilities []tc.ServerCapabilityV5 `json:"serverCapabilities"`
ServiceCategories []tc.ServiceCategoryV5 `json:"serviceCategories"`
Statuses []tc.StatusNullable `json:"statuses"`
StaticDNSEntries []tc.StaticDNSEntry `json:"staticdnsentries"`
diff --git a/traffic_ops/traffic_ops_golang/routing/routes.go b/traffic_ops/traffic_ops_golang/routing/routes.go
index c5fbb8ba47..8a993a4752 100644
--- a/traffic_ops/traffic_ops_golang/routing/routes.go
+++ b/traffic_ops/traffic_ops_golang/routing/routes.go
@@ -325,9 +325,9 @@ func Routes(d ServerData) ([]Route, http.Handler, error) {
{Version: api.Version{Major: 5, Minor: 0}, Method: http.MethodDelete, Path: `servers/{id}$`, Handler: server.Delete, RequiredPrivLevel: auth.PrivLevelOperations, RequiredPermissions: []string{"SERVER:DELETE", "SERVER:READ", "DELIVERY-SERVICE:READ", "CDN:READ", "PHYSICAL-LOCATION:READ", "CACHE-GROUP:READ", "TYPE:READ", "PROFILE:READ"}, Authenticated: Authenticated, Middlewares: nil, ID: 49232223331},
//Server Capability
- {Version: api.Version{Major: 5, Minor: 0}, Method: http.MethodGet, Path: `server_capabilities$`, Handler: servercapability.GetServerCapability, RequiredPrivLevel: auth.PrivLevelReadOnly, RequiredPermissions: []string{"SERVER-CAPABILITY:READ"}, Authenticated: Authenticated, Middlewares: nil, ID: 41040739131},
- {Version: api.Version{Major: 5, Minor: 0}, Method: http.MethodPost, Path: `server_capabilities$`, Handler: servercapability.CreateServerCapability, RequiredPrivLevel: auth.PrivLevelOperations, RequiredPermissions: []string{"SERVER-CAPABILITY:CREATE", "SERVER-CAPABILITY:READ"}, Authenticated: Authenticated, Middlewares: nil, ID: 407447070831},
- {Version: api.Version{Major: 5, Minor: 0}, Method: http.MethodPut, Path: `server_capabilities$`, Handler: servercapability.UpdateServerCapability, RequiredPrivLevel: auth.PrivLevelOperations, RequiredPermissions: []string{"SERVER-CAPABILITY:UPDATE", "SERVER-CAPABILITY:READ"}, Authenticated: Authenticated, Middlewares: nil, ID: 425437701091},
+ {Version: api.Version{Major: 5, Minor: 0}, Method: http.MethodGet, Path: `server_capabilities$`, Handler: servercapability.GetServerCapabilityV5, RequiredPrivLevel: auth.PrivLevelReadOnly, RequiredPermissions: []string{"SERVER-CAPABILITY:READ"}, Authenticated: Authenticated, Middlewares: nil, ID: 41040739131},
+ {Version: api.Version{Major: 5, Minor: 0}, Method: http.MethodPost, Path: `server_capabilities$`, Handler: servercapability.CreateServerCapabilityV5, RequiredPrivLevel: auth.PrivLevelOperations, RequiredPermissions: []string{"SERVER-CAPABILITY:CREATE", "SERVER-CAPABILITY:READ"}, Authenticated: Authenticated, Middlewares: nil, ID: 407447070831},
+ {Version: api.Version{Major: 5, Minor: 0}, Method: http.MethodPut, Path: `server_capabilities$`, Handler: servercapability.UpdateServerCapabilityV5, RequiredPrivLevel: auth.PrivLevelOperations, RequiredPermissions: []string{"SERVER-CAPABILITY:UPDATE", "SERVER-CAPABILITY:READ"}, Authenticated: Authenticated, Middlewares: nil, ID: 425437701091},
{Version: api.Version{Major: 5, Minor: 0}, Method: http.MethodDelete, Path: `server_capabilities$`, Handler: servercapability.DeleteServerCapability, RequiredPrivLevel: auth.PrivLevelOperations, RequiredPermissions: []string{"SERVER-CAPABILITY:DELETE", "SERVER-CAPABILITY:READ"}, Authenticated: Authenticated, Middlewares: nil, ID: 43641503831},
{Version: api.Version{Major: 5, Minor: 0}, Method: http.MethodPost, Path: `multiple_servers_capabilities/?$`, Handler: server.AssignMultipleServersCapabilities, RequiredPrivLevel: auth.PrivLevelOperations, RequiredPermissions: []string{"SERVER:READ", "SERVER:CREATE", "SERVER-CAPABILITY:READ", "SERVER-CAPABILITY:CREATE"}, Authenticated: Authenticated, Middlewares: nil, ID: 407924192581},
{Version: api.Version{Major: 5, Minor: 0}, Method: http.MethodDelete, Path: `multiple_servers_capabilities/?$`, Handler: server.DeleteMultipleServersCapabilities, RequiredPrivLevel: auth.PrivLevelOperations, RequiredPermissions: []string{"SERVER:READ", "SERVER:DELETE", "SERVER-CAPABILITY:READ", "SERVER-CAPABILITY:DELETE"}, Authenticated: Authenticated, Middlewares: nil, ID: 407924192781},
diff --git a/traffic_ops/traffic_ops_golang/servercapability/servercapability.go b/traffic_ops/traffic_ops_golang/servercapability/servercapability.go
index 2f92436805..834a11e0e9 100644
--- a/traffic_ops/traffic_ops_golang/servercapability/servercapability.go
+++ b/traffic_ops/traffic_ops_golang/servercapability/servercapability.go
@@ -367,3 +367,157 @@ func (v *TOServerCapability) SelectMaxLastUpdatedQuery(where, orderBy, paginatio
func (v *TOServerCapability) Create() (error, error, int) { return api.GenericCreateNameBasedID(v) }
func (v *TOServerCapability) Delete() (error, error, int) { return api.GenericDelete(v) }
+
+func GetServerCapabilityV5(w http.ResponseWriter, r *http.Request) {
+ var sc tc.ServerCapabilityV5
+ 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: "sc.name", Checker: nil},
+ }
+ 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)
+ return
+ }
+
+ selectQuery := "SELECT name, description, last_updated FROM server_capability sc"
+ 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("server capability read: error getting server capability(ies): %w", err))
+ return
+ }
+ defer log.Close(rows, "unable to close DB connection")
+
+ scList := []tc.ServerCapabilityV5{}
+ for rows.Next() {
+ if err = rows.Scan(&sc.Name, &sc.Description, &sc.LastUpdated); err != nil {
+ api.HandleErr(w, r, tx.Tx, http.StatusInternalServerError, nil, fmt.Errorf("error getting server capability(ies): %w", err))
+ return
+ }
+ scList = append(scList, sc)
+ }
+
+ api.WriteResp(w, r, scList)
+ return
+}
+
+func CreateServerCapabilityV5(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
+
+ sc, readValErr := readAndValidateJsonStructV5(r)
+ if readValErr != nil {
+ api.HandleErr(w, r, tx, http.StatusBadRequest, readValErr, nil)
+ return
+ }
+
+ // check if capability already exists
+ var count int
+ err := tx.QueryRow("SELECT count(*) from server_capability where name = $1", sc.Name).Scan(&count)
+ if err != nil {
+ api.HandleErr(w, r, tx, http.StatusInternalServerError, nil, fmt.Errorf("error: %w, when checking if server capability with name %s exists", err, sc.Name))
+ return
+ }
+ if count == 1 {
+ api.HandleErr(w, r, tx, http.StatusBadRequest, fmt.Errorf("server_capability name '%s' already exists.", sc.Name), nil)
+ return
+ }
+
+ // create server capability
+ query := `INSERT INTO server_capability (name, description) VALUES ($1, $2) RETURNING last_updated`
+ err = tx.QueryRow(query, sc.Name, sc.Description).Scan(&sc.LastUpdated)
+ if err != nil {
+ if errors.Is(err, sql.ErrNoRows) {
+ api.HandleErr(w, r, tx, http.StatusInternalServerError, fmt.Errorf("error: %w in creating server capability with name: %s", err, sc.Name), nil)
+ return
+ }
+ usrErr, sysErr, code := api.ParseDBError(err)
+ api.HandleErr(w, r, tx, code, usrErr, sysErr)
+ return
+ }
+ alerts := tc.CreateAlerts(tc.SuccessLevel, "server capability was created.")
+ w.Header().Set("Location", fmt.Sprintf("/api/%d.%d/server_capabilities?name=%s", inf.Version.Major, inf.Version.Minor, sc.Name))
+ api.WriteAlertsObj(w, r, http.StatusCreated, alerts, sc)
+ return
+}
+
+func UpdateServerCapabilityV5(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
+ sc, readValErr := readAndValidateJsonStructV5(r)
+ if readValErr != nil {
+ api.HandleErr(w, r, tx, http.StatusBadRequest, readValErr, nil)
+ return
+ }
+
+ requestedName := inf.Params["name"]
+ // check if the entity was already updated
+ userErr, sysErr, errCode = api.CheckIfUnModifiedByName(r.Header, inf.Tx, requestedName, "server_capability")
+ if userErr != nil || sysErr != nil {
+ api.HandleErr(w, r, tx, errCode, userErr, sysErr)
+ return
+ }
+
+ //update name and description of a capability
+ query := `UPDATE server_capability sc SET
+ name = $1,
+ description = $2
+ WHERE sc.name = $3
+ RETURNING sc.name, sc.description, sc.last_updated`
+
+ err := tx.QueryRow(query, sc.Name, sc.Description, requestedName).Scan(&sc.Name, &sc.Description, &sc.LastUpdated)
+ if err != nil {
+ if errors.Is(err, sql.ErrNoRows) {
+ api.HandleErr(w, r, tx, http.StatusBadRequest, fmt.Errorf("server capability with name: %s not found", sc.Name), nil)
+ return
+ }
+ usrErr, sysErr, code := api.ParseDBError(err)
+ api.HandleErr(w, r, tx, code, usrErr, sysErr)
+ return
+ }
+ alerts := tc.CreateAlerts(tc.SuccessLevel, "server capability was updated")
+ api.WriteAlertsObj(w, r, http.StatusOK, alerts, sc)
+ return
+}
+
+func readAndValidateJsonStructV5(r *http.Request) (tc.ServerCapabilityV5, error) {
+ var sc tc.ServerCapabilityV5
+ if err := json.NewDecoder(r.Body).Decode(&sc); err != nil {
+ userErr := fmt.Errorf("error decoding POST request body into ServerCapabilityV5 struct %w", err)
+ return sc, 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(sc.Name, validation.Required, rule),
+ })
+ if len(errs) > 0 {
+ userErr := util.JoinErrs(errs)
+ return sc, userErr
+ }
+ return sc, nil
+}
diff --git a/traffic_ops/v5-client/servercapability.go b/traffic_ops/v5-client/servercapability.go
index 9e9d8567c3..766dec468e 100644
--- a/traffic_ops/v5-client/servercapability.go
+++ b/traffic_ops/v5-client/servercapability.go
@@ -26,27 +26,27 @@ import (
// endpoint.
const apiServerCapabilities = "/server_capabilities"
-// CreateServerCapability creates the given Server Capability.
-func (to *Session) CreateServerCapability(sc tc.ServerCapabilityV41, opts RequestOptions) (tc.ServerCapabilityDetailResponseV41, toclientlib.ReqInf, error) {
- var scResp tc.ServerCapabilityDetailResponseV41
+// CreateServerCapabilityV5 creates the given Server Capability.
+func (to *Session) CreateServerCapabilityV5(sc tc.ServerCapabilityV5, opts RequestOptions) (tc.ServerCapabilityDetailResponseV5, toclientlib.ReqInf, error) {
+ var scResp tc.ServerCapabilityDetailResponseV5
reqInf, err := to.post(apiServerCapabilities, opts, sc, &scResp)
return scResp, reqInf, err
}
-// GetServerCapabilities returns all the Server Capabilities in Traffic Ops.
-func (to *Session) GetServerCapabilities(opts RequestOptions) (tc.ServerCapabilitiesResponseV41, toclientlib.ReqInf, error) {
- var data tc.ServerCapabilitiesResponseV41
+// GetServerCapabilitiesV5 returns all the Server Capabilities in Traffic Ops.
+func (to *Session) GetServerCapabilitiesV5(opts RequestOptions) (tc.ServerCapabilitiesResponseV5, toclientlib.ReqInf, error) {
+ var data tc.ServerCapabilitiesResponseV5
reqInf, err := to.get(apiServerCapabilities, opts, &data)
return data, reqInf, err
}
-// UpdateServerCapability updates a Server Capability by name.
-func (to *Session) UpdateServerCapability(name string, sc tc.ServerCapabilityV41, opts RequestOptions) (tc.ServerCapabilityDetailResponseV41, toclientlib.ReqInf, error) {
+// UpdateServerCapabilityV5 updates a Server Capability by name.
+func (to *Session) UpdateServerCapabilityV5(name string, sc tc.ServerCapabilityV5, opts RequestOptions) (tc.ServerCapabilityDetailResponseV5, toclientlib.ReqInf, error) {
if opts.QueryParameters == nil {
opts.QueryParameters = url.Values{}
}
opts.QueryParameters.Set("name", name)
- var data tc.ServerCapabilityDetailResponseV41
+ var data tc.ServerCapabilityDetailResponseV5
reqInf, err := to.put(apiServerCapabilities, opts, sc, &data)
return data, reqInf, err
}