Skip to content
This repository was archived by the owner on Nov 24, 2025. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/).

### Fixed
- [#7608](https://github.com/apache/trafficcontrol/pull/7608) *Traffic Monitor* Use stats_over_http(plugin.system_stats.timestamp_ms) timestamp field to calculate bandwidth for TM's caches.
- [#7612](https://github.com/apache/trafficcontrol/pull/7612) *Traffic Ops* Fixes Divisions V5 apis to respond with RFC3339 date/time Format
- [#6318](https://github.com/apache/trafficcontrol/issues/6318) *Docs* Included docs for POST, PUT, DELETE (v3,v4,v5) for statuses and statuses{id}
- [#7561](https://github.com/apache/trafficcontrol/pull/7561) *Traffic Ops* *Traffic Ops* Fixed `ASN` V5 apis to respond with `RFC3339` date/time Format.
- [#7598](https://github.com/apache/trafficcontrol/pull/7598) *Traffic Ops* Fixes Server Capability V5 Type Name Minor version
Expand Down
19 changes: 14 additions & 5 deletions docs/source/api/v5/divisions.rst
Original file line number Diff line number Diff line change
Expand Up @@ -53,10 +53,19 @@ Request Structure
| | defined to make use of ``page``. |
+-----------+---------------------------------------------------------------------------------------------------------------+

.. code-block:: http
:caption: Request Example

GET /api/5.0/divisions HTTP/1.1
Host: trafficops.infra.ciab.test
User-Agent: curl/7.47.0
Accept: */*
Cookie: mojolicious=...

Response Structure
------------------
:id: An integral, unique identifier for this Division
:lastUpdated: The date and time at which this Division was last modified, in :ref:`non-rfc-datetime`
:lastUpdated: The date and time at which this Division was last modified, in :rfc:`3339`
:name: The Division name

.. code-block:: http
Expand All @@ -77,12 +86,12 @@ Response Structure
{ "response": [
{
"id": 1,
"lastUpdated": "2018-11-29 18:38:28+00",
"lastUpdated": "2018-11-29T09:39:09.761097+05:30",
"name": "Quebec"
},
{
"id": 2,
"lastUpdated": "2018-11-29 18:38:28+00",
"lastUpdated": "2018-11-29T15:29:31.872822+05:30",
"name": "USA"
}
]}
Expand Down Expand Up @@ -117,7 +126,7 @@ Request Structure
Response Structure
------------------
:id: An integral, unique identifier for this Division
:lastUpdated: The date and time at which this Division was last modified, in :ref:`non-rfc-datetime`
:lastUpdated: The date and time at which this Division was last modified, in :rfc:`3339`
:name: The Division name

.. code-block:: http
Expand All @@ -143,6 +152,6 @@ Response Structure
],
"response": {
"id": 3,
"lastUpdated": "2018-11-29 19:52:06+00",
"lastUpdated": "2018-11-29T19:52:06.872822+05:30",
"name": "test"
}}
25 changes: 13 additions & 12 deletions docs/source/api/v5/divisions_id.rst
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ Request Structure
Response Structure
------------------
:id: An integral, unique identifier for this Division
:lastUpdated: The date and time at which this Division was last modified, in :ref:`non-rfc-datetime`
:lastUpdated: The date and time at which this Division was last modified, in :rfc:`3339`
:name: The Division name

.. code-block:: http
Expand All @@ -83,7 +83,7 @@ Response Structure
],
"response": {
"id": 3,
"lastUpdated": "2018-11-29 20:10:36+00",
"lastUpdated": "2018-11-29T16:23:53.696397+05:30",
"name": "quest"
}}

Expand Down Expand Up @@ -118,13 +118,9 @@ Request Structure
Content-Length: 2
Content-Type: application/json

{}

Response Structure
------------------
:id: An integral, unique identifier for this Division
:lastUpdated: The date and time at which this Division was last modified, in :ref:`non-rfc-datetime`
:name: The Division name

.. code-block:: http
:caption: Response Example
Expand All @@ -141,9 +137,14 @@ Response Structure
Date: Thu, 29 Nov 2018 20:10:36 GMT
Content-Length: 83

{ "alerts": [
{
"text": "division was deleted.",
"level": "success"
}
]}
{
"alerts": [
{
"text": "division was deleted.",
"level": "success"
}
],
"response": {
"id": "3"
}
}
30 changes: 30 additions & 0 deletions lib/go-tc/divisions.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ package tc
* under the License.
*/

import "time"

// DivisionsResponse is a list of Divisions as a response.
// swagger:response DivisionsResponse
type DivisionsResponse struct {
Expand Down Expand Up @@ -60,3 +62,31 @@ type DivisionNullable struct {
LastUpdated *TimeNoMod `json:"lastUpdated" db:"last_updated"`
Name *string `json:"name" db:"name"`
}

// DivisionsResponseV5 is an alias for the latest minor version for the major version 5.
type DivisionsResponseV5 DivisionsResponseV50

// DivisionResponseV5 is an alias for the latest minor version for the major version 5.
type DivisionResponseV5 DivisionResponseV50

// DivisionV5 is an alias for the latest minor version for the major version 5.
type DivisionV5 DivisionV50

// DivisionsResponseV50 is a list of Divisions as a response.
type DivisionsResponseV50 struct {
Response []DivisionV5 `json:"response"`
Alerts
}

// DivisionResponseV50 is a single Division response for Update and Create to
// depict what changed.
type DivisionResponseV50 struct {
Response DivisionV5 `json:"response"`
}

// A DivisionV50 is a named collection of Regions.
type DivisionV50 struct {
ID int `json:"id" db:"id"`
LastUpdated time.Time `json:"lastUpdated" db:"last_updated"`
Name string `json:"name" db:"name"`
}
16 changes: 8 additions & 8 deletions traffic_ops/testing/api/v5/divisions_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ func TestDivisions(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.Division]{
methodTests := utils.TestCase[client.Session, client.RequestOptions, tc.DivisionV5]{
"GET": {
"NOT MODIFIED when NO CHANGES made": {
ClientSession: TOSession,
Expand Down Expand Up @@ -110,7 +110,7 @@ func TestDivisions(t *testing.T) {
"OK when VALID request": {
EndpointID: GetDivisionID(t, "cdn-div2"),
ClientSession: TOSession,
RequestBody: tc.Division{
RequestBody: tc.DivisionV5{
Name: "testdivision",
},
Expectations: utils.CkRequest(utils.NoError(), utils.HasStatus(http.StatusOK),
Expand All @@ -120,15 +120,15 @@ func TestDivisions(t *testing.T) {
EndpointID: GetDivisionID(t, "division1"),
ClientSession: TOSession,
RequestOpts: client.RequestOptions{Header: http.Header{rfc.IfUnmodifiedSince: {currentTimeRFC}}},
RequestBody: tc.Division{
RequestBody: tc.DivisionV5{
Name: "division1",
},
Expectations: utils.CkRequest(utils.HasError(), utils.HasStatus(http.StatusPreconditionFailed)),
},
"PRECONDITION FAILED when updating with IFMATCH ETAG Header": {
EndpointID: GetDivisionID(t, "division1"),
ClientSession: TOSession,
RequestBody: tc.Division{
RequestBody: tc.DivisionV5{
Name: "division1",
},
RequestOpts: client.RequestOptions{Header: http.Header{rfc.IfMatch: {rfc.ETag(currentTime)}}},
Expand Down Expand Up @@ -191,7 +191,7 @@ func TestDivisions(t *testing.T) {
func validateDivisionFields(expectedResp map[string]interface{}) utils.CkReqFunc {
return func(t *testing.T, _ toclientlib.ReqInf, resp interface{}, _ tc.Alerts, _ error) {
assert.RequireNotNil(t, resp, "Expected Division response to not be nil.")
divisionResp := resp.([]tc.Division)
divisionResp := resp.([]tc.DivisionV5)
for field, expected := range expectedResp {
for _, division := range divisionResp {
switch field {
Expand All @@ -218,7 +218,7 @@ func validateDivisionUpdateCreateFields(name string, expectedResp map[string]int

func validateDivisionPagination(paginationParam string) utils.CkReqFunc {
return func(t *testing.T, _ toclientlib.ReqInf, resp interface{}, _ tc.Alerts, _ error) {
paginationResp := resp.([]tc.Division)
paginationResp := resp.([]tc.DivisionV5)
opts := client.NewRequestOptions()
opts.QueryParameters.Set("orderby", "id")
respBase, _, err := TOSession.GetDivisions(opts)
Expand All @@ -241,7 +241,7 @@ func validateDivisionSort() utils.CkReqFunc {
return func(t *testing.T, _ toclientlib.ReqInf, resp interface{}, alerts tc.Alerts, _ error) {
assert.RequireNotNil(t, resp, "Expected Division response to not be nil.")
var divisionNames []string
divisionResp := resp.([]tc.Division)
divisionResp := resp.([]tc.DivisionV5)
for _, division := range divisionResp {
divisionNames = append(divisionNames, division.Name)
}
Expand All @@ -252,7 +252,7 @@ func validateDivisionSort() utils.CkReqFunc {
func validateDivisionDescSort() utils.CkReqFunc {
return func(t *testing.T, _ toclientlib.ReqInf, resp interface{}, alerts tc.Alerts, _ error) {
assert.RequireNotNil(t, resp, "Expected Division response to not be nil.")
divisionDescResp := resp.([]tc.Division)
divisionDescResp := resp.([]tc.DivisionV5)
var descSortedList []string
var ascSortedList []string
assert.RequireGreaterOrEqual(t, len(divisionDescResp), 2, "Need at least 2 Divisions in Traffic Ops to test desc sort, found: %d", len(divisionDescResp))
Expand Down
2 changes: 1 addition & 1 deletion traffic_ops/testing/api/v5/traffic_control_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ type TrafficControl struct {
DeliveryServicesRequiredCapabilities []tc.DeliveryServicesRequiredCapability `json:"deliveryservicesRequiredCapabilities"`
DeliveryServiceServerAssignments []tc.DeliveryServiceServers `json:"deliveryServiceServerAssignments"`
TopologyBasedDeliveryServicesRequiredCapabilities []tc.DeliveryServicesRequiredCapability `json:"topologyBasedDeliveryServicesRequiredCapabilities"`
Divisions []tc.Division `json:"divisions"`
Divisions []tc.DivisionV5 `json:"divisions"`
Federations []tc.CDNFederation `json:"federations"`
FederationResolvers []tc.FederationResolver `json:"federation_resolvers"`
Jobs []tc.InvalidationJobCreateV4 `json:"jobs"`
Expand Down
15 changes: 15 additions & 0 deletions traffic_ops/traffic_ops_golang/dbhelpers/db_helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -2217,3 +2217,18 @@ func ASNExists(tx *sql.Tx, id string) (bool, error) {
}
return true, nil
}

// DivisionExists confirms whether the division exists, and an error (if one occurs).
func DivisionExists(tx *sql.Tx, id string) (bool, error) {
var count int
if err := tx.QueryRow("SELECT count(id) FROM division AS div WHERE div.id=$1", id).Scan(&count); err != nil {
return false, fmt.Errorf("error getting divisions info: %w", err)
}
if count == 0 {
return false, nil
}
if count != 1 {
return false, fmt.Errorf("getting division info - expected row count: 1, actual: %d", count)
}
return true, nil
}
51 changes: 51 additions & 0 deletions traffic_ops/traffic_ops_golang/dbhelpers/db_helpers_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -562,3 +562,54 @@ func TestASNExists(t *testing.T) {
})
}
}

func TestDivisionExists(t *testing.T) {
var testCases = []struct {
description string
id string
expectedError error
exists bool
}{
{
description: "Success: Get valid Division",
id: "1",
expectedError: nil,
exists: true,
},
{
description: "Failure: Division not in DB",
id: "10",
expectedError: sql.ErrNoRows,
exists: false,
},
}
for _, testCase := range testCases {
t.Run(testCase.description, func(t *testing.T) {
mockDB, mock, err := sqlmock.New()
if err != nil {
t.Fatalf("an error '%s' was not expected when opening a stub database connection", err)
}
defer mockDB.Close()

db := sqlx.NewDb(mockDB, "sqlmock")
defer db.Close()

mock.ExpectBegin()
rows := sqlmock.NewRows([]string{"count"})
if testCase.exists {
rows = rows.AddRow(1)
}
mock.ExpectQuery("SELECT").WillReturnRows(rows)
mock.ExpectCommit()

divisionExists, err := DivisionExists(db.MustBegin().Tx, testCase.id)
if testCase.exists != divisionExists {
t.Errorf("Expected return exists: %t, actual %t", testCase.exists, divisionExists)
}

if !errors.Is(err, testCase.expectedError) {
t.Errorf("DivisionExists Error. expected: %s, actual: %s", testCase.expectedError, err)
}
})
}
}
Loading