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

Reply via email to