REFACTOR: Pave args (not validate) (#3045)

This commit is contained in:
Tom Limoncelli 2024-07-09 21:44:38 -04:00 committed by GitHub
parent 0ed31efdc9
commit 95c7a70434
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
9 changed files with 83 additions and 90 deletions

View file

@ -152,7 +152,7 @@ type RecordConfig struct {
// When these are used, .target is set to a human-readable version (only to be used for display purposes). // When these are used, .target is set to a human-readable version (only to be used for display purposes).
type CloudflareSingleRedirectConfig struct { type CloudflareSingleRedirectConfig struct {
// //
Code int `json:"code,omitempty"` // 301 or 302 Code uint16 `json:"code,omitempty"` // 301 or 302
// PR == PageRule // PR == PageRule
PRDisplay string `json:"pr_display,omitempty"` // How is this displayed to the user PRDisplay string `json:"pr_display,omitempty"` // How is this displayed to the user
PRWhen string `json:"pr_when,omitempty"` PRWhen string `json:"pr_when,omitempty"`

50
pkg/rtypecontrol/pave.go Normal file
View file

@ -0,0 +1,50 @@
package rtypecontrol
import (
"fmt"
"strconv"
)
// PaveArgs converts each arg to its desired type, or returns an error if conversion fails or if the number of arguments is wrong.
// argTypes is a string where each rune specifies the desired type of the arg in the same position:
// 'i': uinet16 (will convert strings, truncate floats, etc)
// 's': Valid only if string.
func PaveArgs(args []any, argTypes string) error {
if len(args) != len(argTypes) {
return fmt.Errorf("wrong number of arguments. Expected %v, got %v", len(argTypes), len(args))
}
for i, at := range argTypes {
arg := args[i]
switch at {
case 'i': // uint16
if s, ok := arg.(string); ok { // Is this a string-encoded int?
ni, err := strconv.Atoi(s)
if err != nil {
return fmt.Errorf("value %q is not a number (uint16 wanted)", arg)
}
args[i] = uint16(ni)
} else if _, ok := arg.(float64); ok {
args[i] = uint16(arg.(float64))
} else if _, ok := arg.(uint16); ok {
args[i] = arg.(uint16)
} else if _, ok := arg.(int); ok {
args[i] = uint16(arg.(int))
} else {
return fmt.Errorf("value %q is type %T, expected uint16", arg, arg)
}
case 's':
if _, ok := arg.(string); ok {
args[i] = arg.(string)
} else {
args[i] = fmt.Sprintf("%v", arg)
}
}
}
return nil
}

View file

@ -2,7 +2,7 @@ package rtypecontrol
import "testing" import "testing"
func TestValidateArgs(t *testing.T) { func TestPaveArgs(t *testing.T) {
tests := []struct { tests := []struct {
name string name string
dataArgs []any dataArgs []any
@ -19,23 +19,35 @@ func TestValidateArgs(t *testing.T) {
name: "int to string", name: "int to string",
dataArgs: []any{100}, dataArgs: []any{100},
dataRule: "s", dataRule: "s",
wantErr: true, wantErr: false,
}, },
{ {
name: "int", name: "uint16",
dataArgs: []any{int(1)}, dataArgs: []any{uint16(1)},
dataRule: "i", dataRule: "i",
wantErr: false, wantErr: false,
}, },
{ {
name: "string to int", name: "float to uint16",
dataArgs: []any{float64(2)},
dataRule: "i",
wantErr: false,
},
{
name: "int uint16",
dataArgs: []any{int(3)},
dataRule: "i",
wantErr: false,
},
{
name: "string to uint16",
dataArgs: []any{"111"}, dataArgs: []any{"111"},
dataRule: "i", dataRule: "i",
wantErr: false, wantErr: false,
}, },
{ {
name: "txt to int", name: "txt to uint16",
dataArgs: []any{"one"}, dataArgs: []any{"one"},
dataRule: "i", dataRule: "i",
wantErr: true, wantErr: true,
@ -43,8 +55,8 @@ func TestValidateArgs(t *testing.T) {
} }
for _, tt := range tests { for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) { t.Run(tt.name, func(t *testing.T) {
if err := CheckArgTypes(tt.dataArgs, tt.dataRule); (err != nil) != tt.wantErr { if err := PaveArgs(tt.dataArgs, tt.dataRule); (err != nil) != tt.wantErr {
t.Errorf("ValidateArgs() error = %v, wantErr %v", err, tt.wantErr) t.Errorf("PaveArgs() error = %v, wantErr %v", err, tt.wantErr)
} }
}) })
} }

View file

@ -1,43 +0,0 @@
package rtypecontrol
import (
"fmt"
"strconv"
)
// CheckArgTypes validates that the items in args are of appropriate types. argTypes is a string: "ssi" means the args should be string, string, int.
// 's': Valid only if string.
// 'i': Valid only if int, float64, or a string that Atoi() can convert to an int.
func CheckArgTypes(args []any, argTypes string) error {
if len(args) != len(argTypes) {
return fmt.Errorf("wrong number of arguments. Expected %v, got %v", len(argTypes), len(args))
}
for i, at := range argTypes {
arg := args[i]
switch at {
case 'i':
if s, ok := arg.(string); ok { // Is this a string-encoded int?
ni, err := strconv.Atoi(s)
if err != nil {
return fmt.Errorf("value %q is type %T, expected INT", arg, arg)
}
args[i] = ni
} else if _, ok := arg.(float64); ok {
args[i] = int(arg.(float64))
} else if _, ok := arg.(int); !ok {
return fmt.Errorf("value %q is type %T, expected INT", arg, arg)
}
case 's':
if _, ok := arg.(string); !ok {
return fmt.Errorf("value %q is type %T, expected STRING", arg, arg)
}
}
}
return nil
}

View file

@ -568,7 +568,7 @@ func (c *cloudflareProvider) preprocessConfig(dc *models.DomainConfig) error {
if !c.manageRedirects && !c.manageSingleRedirects { if !c.manageRedirects && !c.manageSingleRedirects {
return fmt.Errorf("you must add 'manage_single_redirects: true' metadata to cloudflare provider to use CF_REDIRECT/CF_TEMP_REDIRECT records") return fmt.Errorf("you must add 'manage_single_redirects: true' metadata to cloudflare provider to use CF_REDIRECT/CF_TEMP_REDIRECT records")
} }
code := 301 code := uint16(301)
if rec.Type == "CF_TEMP_REDIRECT" { if rec.Type == "CF_TEMP_REDIRECT" {
code = 302 code = 302
} }
@ -844,13 +844,13 @@ func uint16Zero(value interface{}) uint16 {
return 0 return 0
} }
// intZero converts value to int or returns 0. // intZero converts value to uint16 or returns 0.
func intZero(value interface{}) int { func intZero(value interface{}) uint16 {
switch v := value.(type) { switch v := value.(type) {
case float64: case float64:
return int(v) return uint16(v)
case int: case int:
return v return uint16(v)
case nil: case nil:
} }
return 0 return 0

View file

@ -306,7 +306,7 @@ func (c *cloudflareProvider) getSingleRedirects(id string, domain string) ([]*mo
// Extract the valuables from the rule, use it to make the sr: // Extract the valuables from the rule, use it to make the sr:
srWhen := pr.Expression srWhen := pr.Expression
srThen := pr.ActionParameters.FromValue.TargetURL.Expression srThen := pr.ActionParameters.FromValue.TargetURL.Expression
code := int(pr.ActionParameters.FromValue.StatusCode) code := uint16(pr.ActionParameters.FromValue.StatusCode)
sr := cfsingleredirect.FromAPIData(srWhen, srThen, code) sr := cfsingleredirect.FromAPIData(srWhen, srThen, code)
//sr.SRRRuleList = rulelist //sr.SRRRuleList = rulelist
//printer.Printf("DEBUG: DESCRIPTION = %v\n", pr.Description) //printer.Printf("DEBUG: DESCRIPTION = %v\n", pr.Description)
@ -475,15 +475,6 @@ func (c *cloudflareProvider) updatePageRule(recordID, domainID string, cfr model
} }
func (c *cloudflareProvider) createPageRule(domainID string, cfr models.CloudflareSingleRedirectConfig) error { func (c *cloudflareProvider) createPageRule(domainID string, cfr models.CloudflareSingleRedirectConfig) error {
//printer.Printf("DEBUG: called createPageRule(%s, %+v)\n", domainID, cfr)
// from to priority code
// parts := strings.Split(target, ",")
// priority, _ := strconv.Atoi(parts[2])
// code, _ := strconv.Atoi(parts[3])
// printer.Printf("DEBUG: pr.PageRule target = %v\n", target)
// printer.Printf("DEBUG: pr.PageRule target = %v\n", parts[0])
// printer.Printf("DEBUG: pr.PageRule url = %v\n", parts[1])
// printer.Printf("DEBUG: pr.PageRule code = %v\n", code)
priority := cfr.PRPriority priority := cfr.PRPriority
code := cfr.Code code := cfr.Code
prWhen := cfr.PRWhen prWhen := cfr.PRWhen
@ -570,5 +561,5 @@ type pageRuleConstraint struct {
type pageRuleFwdInfo struct { type pageRuleFwdInfo struct {
URL string `json:"url"` URL string `json:"url"`
StatusCode int `json:"status_code"` StatusCode uint16 `json:"status_code"`
} }

View file

@ -2,7 +2,6 @@ package cfsingleredirect
import ( import (
"fmt" "fmt"
"strconv"
"github.com/StackExchange/dnscontrol/v4/models" "github.com/StackExchange/dnscontrol/v4/models"
"github.com/StackExchange/dnscontrol/v4/pkg/rtypecontrol" "github.com/StackExchange/dnscontrol/v4/pkg/rtypecontrol"
@ -14,34 +13,18 @@ func init() {
func FromRaw(rc *models.RecordConfig, items []any) error { func FromRaw(rc *models.RecordConfig, items []any) error {
var err error
// Validate types. // Validate types.
if err := rtypecontrol.CheckArgTypes(items, "siss"); err != nil { if err := rtypecontrol.PaveArgs(items, "siss"); err != nil {
return err return err
} }
// Unpack the args: // Unpack the args:
var name, when, then string var name, when, then string
var code int var code uint16
name = items[0].(string) name = items[0].(string)
ucode := items[1] code = items[1].(uint16)
switch v := ucode.(type) {
case int:
code = v
case float64:
code = int(v)
case string:
code, err = strconv.Atoi(v)
if err != nil {
return err
}
default:
return fmt.Errorf("code %q unexpected type %T", ucode, v)
}
if code != 301 && code != 302 { if code != 301 && code != 302 {
return fmt.Errorf("code (%03d) is not 301 or 302", code) return fmt.Errorf("code (%03d) is not 301 or 302", code)
} }

View file

@ -9,7 +9,7 @@ import (
"github.com/StackExchange/dnscontrol/v4/models" "github.com/StackExchange/dnscontrol/v4/models"
) )
func FromUserInput(target string, code int, priority int) (*models.CloudflareSingleRedirectConfig, error) { func FromUserInput(target string, code uint16, priority int) (*models.CloudflareSingleRedirectConfig, error) {
// target: matcher,replacement,priority,code // target: matcher,replacement,priority,code
// target: cable.slackoverflow.com/*,https://change.cnn.com/$1,1,302 // target: cable.slackoverflow.com/*,https://change.cnn.com/$1,1,302

View file

@ -6,7 +6,7 @@ import (
"github.com/StackExchange/dnscontrol/v4/models" "github.com/StackExchange/dnscontrol/v4/models"
) )
func FromAPIData(sm, sr string, code int) *models.CloudflareSingleRedirectConfig { func FromAPIData(sm, sr string, code uint16) *models.CloudflareSingleRedirectConfig {
r := &models.CloudflareSingleRedirectConfig{ r := &models.CloudflareSingleRedirectConfig{
PRWhen: "UNKNOWABLE", PRWhen: "UNKNOWABLE",
PRThen: "UNKNOWABLE", PRThen: "UNKNOWABLE",