dewrich closed pull request #1940: TO golang -- adds validation rules for ASNs,
CDNs APIs
URL: https://github.com/apache/incubator-trafficcontrol/pull/1940
This is a PR merged from a forked repository.
As GitHub hides the original diff on merge, it is displayed below for
the sake of provenance:
As this is a foreign pull request (from a fork), the diff is supplied
below (as it won't show otherwise due to GitHub magic):
diff --git a/lib/go-tc/asns.go b/lib/go-tc/asns.go
index 5341ca0efb..ac73a57dcb 100644
--- a/lib/go-tc/asns.go
+++ b/lib/go-tc/asns.go
@@ -24,9 +24,17 @@ type ASNsResponse struct {
}
type ASN struct {
- ASN int `json:"asn" db:"asn"`
- Cachegroup string `json:"cachegroup" db:"cachegroup"`
- CachegroupID int `json:"cachegroupId" db:"cachegroup_id"`
- ID int `json:"id" db:"id"`
- LastUpdated Time `json:"lastUpdated" db:"last_updated"`
+ ASN int `json:"asn" db:"asn"`
+ Cachegroup string `json:"cachegroup" db:"cachegroup"`
+ CachegroupID int `json:"cachegroupId" db:"cachegroup_id"`
+ ID int `json:"id" db:"id"`
+ LastUpdated TimeNoMod `json:"lastUpdated" db:"last_updated"`
+}
+
+type ASNNullable struct {
+ ASN *int `json:"asn" db:"asn"`
+ Cachegroup *string `json:"cachegroup" db:"cachegroup"`
+ CachegroupID *int `json:"cachegroupId" db:"cachegroup_id"`
+ ID *int `json:"id" db:"id"`
+ LastUpdated *TimeNoMod `json:"lastUpdated" db:"last_updated"`
}
diff --git a/lib/go-tc/cdns.go b/lib/go-tc/cdns.go
index 34a2974c07..5402e2c194 100644
--- a/lib/go-tc/cdns.go
+++ b/lib/go-tc/cdns.go
@@ -24,11 +24,19 @@ type CDNsResponse struct {
}
type CDN struct {
- DNSSECEnabled bool `json:"dnssecEnabled" db:"dnssec_enabled"`
- DomainName string `json:"domainName" db:"domain_name"`
- ID int `json:"id" db:"id"`
- LastUpdated Time `json:"lastUpdated" db:"last_updated"`
- Name string `json:"name" db:"name"`
+ DNSSECEnabled bool `json:"dnssecEnabled" db:"dnssec_enabled"`
+ DomainName string `json:"domainName" db:"domain_name"`
+ ID int `json:"id" db:"id"`
+ LastUpdated TimeNoMod `json:"lastUpdated" db:"last_updated"`
+ Name string `json:"name" db:"name"`
+}
+
+type CDNNullable struct {
+ DNSSECEnabled *bool `json:"dnssecEnabled" db:"dnssec_enabled"`
+ DomainName *string `json:"domainName" db:"domain_name"`
+ ID *int `json:"id" db:"id"`
+ LastUpdated *TimeNoMod `json:"lastUpdated" db:"last_updated"`
+ Name *string `json:"name" db:"name"`
}
// CDNSSLKeysResponse ...
@@ -47,4 +55,4 @@ type CDNSSLKeys struct {
type CDNSSLKeysCertificate struct {
Crt string `json:"crt"`
Key string `json:"key"`
-}
\ No newline at end of file
+}
diff --git a/traffic_ops/traffic_ops_golang/asn/asns.go
b/traffic_ops/traffic_ops_golang/asn/asns.go
index 5037e2eeed..ecc2c56f9f 100644
--- a/traffic_ops/traffic_ops_golang/asn/asns.go
+++ b/traffic_ops/traffic_ops_golang/asn/asns.go
@@ -29,6 +29,8 @@ import (
"github.com/apache/incubator-trafficcontrol/traffic_ops/traffic_ops_golang/api"
"github.com/apache/incubator-trafficcontrol/traffic_ops/traffic_ops_golang/auth"
"github.com/apache/incubator-trafficcontrol/traffic_ops/traffic_ops_golang/dbhelpers"
+
"github.com/apache/incubator-trafficcontrol/traffic_ops/traffic_ops_golang/tovalidate"
+ validation "github.com/go-ozzo/ozzo-validation"
"github.com/jmoiron/sqlx"
"github.com/lib/pq"
)
@@ -37,38 +39,45 @@ import (
const ASNsPrivLevel = 10
//we need a type alias to define functions on
-type TOASN tc.ASN
+type TOASN tc.ASNNullable
//the refType is passed into the handlers where a copy of its type is used to
decode the json.
-var refType = TOASN(tc.ASN{})
+var refType = TOASN(tc.ASNNullable{})
func GetRefType() *TOASN {
return &refType
}
//Implementation of the Identifier, Validator interface functions
-func (asn *TOASN) GetID() (int, bool) {
- return asn.ID, true
+func (asn TOASN) GetID() (int, bool) {
+ if asn.ID == nil {
+ return 0, false
+ }
+ return *asn.ID, true
}
-func (asn *TOASN) GetAuditName() string {
- return strconv.Itoa(asn.ASN)
+func (asn TOASN) GetAuditName() string {
+ if asn.ASN == nil {
+ id, _ := asn.GetID()
+ return strconv.Itoa(id)
+ }
+ return strconv.Itoa(*asn.ASN)
}
-func (asn *TOASN) GetType() string {
+func (asn TOASN) GetType() string {
return "asn"
}
func (asn *TOASN) SetID(i int) {
- asn.ID = i
+ asn.ID = &i
}
-func (asn *TOASN) Validate(db *sqlx.DB) []error {
- errs := []error{}
- if asn.ASN < 1 {
- errs = append(errs, errors.New(`ASN 'asn' is required.`))
+func (asn TOASN) Validate(db *sqlx.DB) []error {
+ errs := validation.Errors{
+ "asn": validation.Validate(asn.ASN, validation.NotNil,
validation.Min(0)),
+ "cachegroupId": validation.Validate(asn.CachegroupID,
validation.NotNil, validation.Min(0)),
}
- return errs
+ return tovalidate.ToErrors(errs)
}
//The TOASN implementation of the Creator interface
@@ -111,7 +120,7 @@ func (asn *TOASN) Create(db *sqlx.DB, user
auth.CurrentUser) (error, tc.ApiError
defer resultRows.Close()
var id int
- var lastUpdated tc.Time
+ var lastUpdated tc.TimeNoMod
rowsAffected := 0
for resultRows.Next() {
rowsAffected++
@@ -130,7 +139,7 @@ func (asn *TOASN) Create(db *sqlx.DB, user
auth.CurrentUser) (error, tc.ApiError
return tc.DBError, tc.SystemError
}
asn.SetID(id)
- asn.LastUpdated = lastUpdated
+ asn.LastUpdated = &lastUpdated
err = tx.Commit()
if err != nil {
log.Errorln("Could not commit transaction: ", err)
@@ -228,7 +237,7 @@ func (asn *TOASN) Update(db *sqlx.DB, user
auth.CurrentUser) (error, tc.ApiError
}
defer resultRows.Close()
- var lastUpdated tc.Time
+ var lastUpdated tc.TimeNoMod
rowsAffected := 0
for resultRows.Next() {
rowsAffected++
@@ -238,7 +247,7 @@ func (asn *TOASN) Update(db *sqlx.DB, user
auth.CurrentUser) (error, tc.ApiError
}
}
log.Debugf("lastUpdated: %++v", lastUpdated)
- asn.LastUpdated = lastUpdated
+ asn.LastUpdated = &lastUpdated
if rowsAffected != 1 {
if rowsAffected < 1 {
return errors.New("no asn found with this id"),
tc.DataMissingError
diff --git a/traffic_ops/traffic_ops_golang/asn/asns_test.go
b/traffic_ops/traffic_ops_golang/asn/asns_test.go
index 9d967df3cd..b1d0bb4b42 100644
--- a/traffic_ops/traffic_ops_golang/asn/asns_test.go
+++ b/traffic_ops/traffic_ops_golang/asn/asns_test.go
@@ -20,6 +20,8 @@ package asn
*/
import (
+ "errors"
+ "reflect"
"testing"
"time"
@@ -38,7 +40,7 @@ func getTestASNs() []tc.ASN {
ASN: 1,
Cachegroup: "Yukon",
ID: 1,
- LastUpdated: tc.Time{Time: time.Now()},
+ LastUpdated: tc.TimeNoMod{Time: time.Now()},
}
ASNs = append(ASNs, testCase)
@@ -107,3 +109,17 @@ func TestInterfaces(t *testing.T) {
t.Errorf("asn must be Identifier")
}
}
+
+func TestValidate(t *testing.T) {
+ i := -99
+ asn := TOASN{ASN: &i, CachegroupID: &i}
+
+ errs := asn.Validate(nil)
+ expected := []error{
+ errors.New(`'asn' must be no less than 0`),
+ errors.New(`'cachegroupId' must be no less than 0`),
+ }
+ if !reflect.DeepEqual(expected, errs) {
+ t.Errorf(`expected %v, got %v`, expected, errs)
+ }
+}
diff --git a/traffic_ops/traffic_ops_golang/cdn/cdns.go
b/traffic_ops/traffic_ops_golang/cdn/cdns.go
index c1b7510965..3501e67fa8 100644
--- a/traffic_ops/traffic_ops_golang/cdn/cdns.go
+++ b/traffic_ops/traffic_ops_golang/cdn/cdns.go
@@ -22,53 +22,86 @@ package cdn
import (
"errors"
"fmt"
+ "strconv"
+ "strings"
"github.com/apache/incubator-trafficcontrol/lib/go-log"
"github.com/apache/incubator-trafficcontrol/lib/go-tc"
-
"github.com/apache/incubator-trafficcontrol/traffic_ops/traffic_ops_golang/api"
"github.com/apache/incubator-trafficcontrol/traffic_ops/traffic_ops_golang/auth"
"github.com/apache/incubator-trafficcontrol/traffic_ops/traffic_ops_golang/dbhelpers"
+
"github.com/apache/incubator-trafficcontrol/traffic_ops/traffic_ops_golang/tovalidate"
+ "github.com/asaskevich/govalidator"
+ validation "github.com/go-ozzo/ozzo-validation"
"github.com/jmoiron/sqlx"
"github.com/lib/pq"
)
//we need a type alias to define functions on
-type TOCDN tc.CDN
+type TOCDN tc.CDNNullable
//the refType is passed into the handlers where a copy of its type is used to
decode the json.
-var refType = TOCDN(tc.CDN{})
+var refType = TOCDN{}
func GetRefType() *TOCDN {
return &refType
}
//Implementation of the Identifier, Validator interface functions
-func (cdn *TOCDN) GetID() (int, bool) {
- return cdn.ID, true
+func (cdn TOCDN) GetID() (int, bool) {
+ if cdn.ID == nil {
+ return 0, false
+ }
+ return *cdn.ID, true
}
-func (cdn *TOCDN) GetAuditName() string {
- return cdn.Name
+func (cdn TOCDN) GetAuditName() string {
+ if cdn.Name != nil {
+ return *cdn.Name
+ }
+ id, _ := cdn.GetID()
+ return strconv.Itoa(id)
}
-func (cdn *TOCDN) GetType() string {
+func (cdn TOCDN) GetType() string {
return "cdn"
}
func (cdn *TOCDN) SetID(i int) {
- cdn.ID = i
+ cdn.ID = &i
}
-func (cdn *TOCDN) Validate(db *sqlx.DB) []error {
- errs := []error{}
- if len(cdn.Name) < 1 {
- errs = append(errs, errors.New(`CDN 'name' is required.`))
+func isValidCDNchar(r rune) bool {
+ if r >= 'a' && r <= 'z' {
+ return true
+ }
+ if r >= 'A' && r <= 'Z' {
+ return true
+ }
+ if r >= '0' && r <= '9' {
+ return true
}
- if len(cdn.DomainName) < 1 {
- errs = append(errs, errors.New("Domain Name is required."))
+ if r == '.' || r == '-' {
+ return true
+ }
+ return false
+}
+
+// IsValidCDNName returns true if the name contains only characters valid for
a CDN name
+func IsValidCDNName(str string) bool {
+ i := strings.IndexFunc(str, func(r rune) bool { return
!isValidCDNchar(r) })
+ return i == -1
+}
+
+// Validate fulfills the api.Validator interface
+func (cdn TOCDN) Validate(db *sqlx.DB) []error {
+ validName := validation.NewStringRule(IsValidCDNName, "invalid
characters found - Use alphanumeric . or - .")
+ validDomainName := validation.NewStringRule(govalidator.IsDNSName, "not
a valid domain name")
+ errs := validation.Errors{
+ "name": validation.Validate(cdn.Name,
validation.Required, validName),
+ "domainName": validation.Validate(cdn.DomainName,
validation.Required, validDomainName),
}
- return errs
+ return tovalidate.ToErrors(errs)
}
//The TOCDN implementation of the Creator interface
@@ -111,7 +144,7 @@ func (cdn *TOCDN) Create(db *sqlx.DB, user
auth.CurrentUser) (error, tc.ApiError
defer resultRows.Close()
var id int
- var lastUpdated tc.Time
+ var lastUpdated tc.TimeNoMod
rowsAffected := 0
for resultRows.Next() {
rowsAffected++
@@ -130,7 +163,7 @@ func (cdn *TOCDN) Create(db *sqlx.DB, user
auth.CurrentUser) (error, tc.ApiError
return tc.DBError, tc.SystemError
}
cdn.SetID(id)
- cdn.LastUpdated = lastUpdated
+ cdn.LastUpdated = &lastUpdated
err = tx.Commit()
if err != nil {
log.Errorln("Could not commit transaction: ", err)
@@ -217,7 +250,7 @@ func (cdn *TOCDN) Update(db *sqlx.DB, user
auth.CurrentUser) (error, tc.ApiError
}
defer resultRows.Close()
- var lastUpdated tc.Time
+ var lastUpdated tc.TimeNoMod
rowsAffected := 0
for resultRows.Next() {
rowsAffected++
@@ -227,7 +260,7 @@ func (cdn *TOCDN) Update(db *sqlx.DB, user
auth.CurrentUser) (error, tc.ApiError
}
}
log.Debugf("lastUpdated: %++v", lastUpdated)
- cdn.LastUpdated = lastUpdated
+ cdn.LastUpdated = &lastUpdated
if rowsAffected != 1 {
if rowsAffected < 1 {
return errors.New("no cdn found with this id"),
tc.DataMissingError
diff --git a/traffic_ops/traffic_ops_golang/cdn/cdns_test.go
b/traffic_ops/traffic_ops_golang/cdn/cdns_test.go
index 38c9abde95..65b676fe7f 100644
--- a/traffic_ops/traffic_ops_golang/cdn/cdns_test.go
+++ b/traffic_ops/traffic_ops_golang/cdn/cdns_test.go
@@ -20,6 +20,9 @@ package cdn
*/
import (
+ "errors"
+ "reflect"
+ "strings"
"testing"
"time"
@@ -39,7 +42,7 @@ func getTestCDNs() []tc.CDN {
DomainName: "domainName",
ID: 1,
Name: "cdn1",
- LastUpdated: tc.Time{Time: time.Now()},
+ LastUpdated: tc.TimeNoMod{Time: time.Now()},
}
cdns = append(cdns, testCDN)
@@ -89,6 +92,21 @@ func TestReadCDNs(t *testing.T) {
}
}
+func TestFuncs(t *testing.T) {
+ if strings.Index(selectQuery(), "SELECT") != 0 {
+ t.Errorf("expected selectQuery to start with SELECT")
+ }
+ if strings.Index(insertQuery(), "INSERT") != 0 {
+ t.Errorf("expected insertQuery to start with INSERT")
+ }
+ if strings.Index(updateQuery(), "UPDATE") != 0 {
+ t.Errorf("expected updateQuery to start with UPDATE")
+ }
+ if strings.Index(deleteQuery(), "DELETE") != 0 {
+ t.Errorf("expected deleteQuery to start with DELETE")
+ }
+
+}
func TestInterfaces(t *testing.T) {
var i interface{}
i = &TOCDN{}
@@ -109,3 +127,30 @@ func TestInterfaces(t *testing.T) {
t.Errorf("cdn must be Identifier")
}
}
+
+func TestValidate(t *testing.T) {
+ // invalid name, empty domainname
+ n := "not_a_valid_cdn"
+ c := TOCDN{Name: &n}
+ errs := c.Validate(nil)
+
+ expectedErrs := []error{
+ errors.New(`'name' invalid characters found - Use alphanumeric
. or - .`),
+ errors.New(`'domainName' cannot be blank`),
+ }
+
+ if !reflect.DeepEqual(expectedErrs, errs) {
+ t.Errorf("expected %s, got %s", expectedErrs, errs)
+ }
+
+ // name, domainname both valid
+ n = "This.is.2.a-Valid---CDNNAME."
+ d := `awesome-cdn.example.net`
+ c = TOCDN{Name: &n, DomainName: &d}
+ expectedErrs = []error{}
+ errs = c.Validate(nil)
+ if !reflect.DeepEqual(expectedErrs, errs) {
+ t.Errorf("expected %s, got %s", expectedErrs, errs)
+ }
+
+}
----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on GitHub and use the
URL above to go to the specific comment.
For queries about this service, please contact Infrastructure at:
[email protected]
With regards,
Apache Git Services