mirror of
https://github.com/StackExchange/dnscontrol.git
synced 2024-11-10 09:12:47 +08:00
REFACTOR: Pave args (not validate) (#3045)
This commit is contained in:
parent
0ed31efdc9
commit
95c7a70434
9 changed files with 83 additions and 90 deletions
|
@ -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
50
pkg/rtypecontrol/pave.go
Normal 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
|
||||||
|
}
|
|
@ -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)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
|
@ -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
|
|
||||||
}
|
|
|
@ -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
|
||||||
|
|
|
@ -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"`
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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",
|
||||||
|
|
Loading…
Reference in a new issue