mirror of
https://github.com/yusing/godoxy.git
synced 2026-04-19 15:01:22 +02:00
feat(api): accept rule config block string in playground
Update playground request to take rules as a string and parse either YAML list or DSL config, with tests and swagger updates.
This commit is contained in:
@@ -9,6 +9,7 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/goccy/go-yaml"
|
||||
"github.com/yusing/godoxy/internal/common"
|
||||
"github.com/yusing/godoxy/internal/route/rules"
|
||||
apitypes "github.com/yusing/goutils/apitypes"
|
||||
@@ -23,7 +24,7 @@ type RawRule struct {
|
||||
}
|
||||
|
||||
type PlaygroundRequest struct {
|
||||
Rules []RawRule `json:"rules" binding:"required"`
|
||||
Rules string `json:"rules" binding:"required"`
|
||||
MockRequest MockRequest `json:"mockRequest"`
|
||||
MockResponse MockResponse `json:"mockResponse"`
|
||||
} // @name PlaygroundRequest
|
||||
@@ -255,7 +256,35 @@ func handlerWithRecover(w http.ResponseWriter, r *http.Request, h http.HandlerFu
|
||||
h(w, r)
|
||||
}
|
||||
|
||||
func parseRules(rawRules []RawRule) ([]ParsedRule, rules.Rules, error) {
|
||||
func parseRules(config string) ([]ParsedRule, rules.Rules, error) {
|
||||
config = strings.TrimSpace(config)
|
||||
if config == "" {
|
||||
return []ParsedRule{}, nil, nil
|
||||
}
|
||||
|
||||
var rawRules []RawRule
|
||||
if err := yaml.Unmarshal([]byte(config), &rawRules); err == nil && len(rawRules) > 0 {
|
||||
return parseRawRules(rawRules)
|
||||
}
|
||||
|
||||
var rulesList rules.Rules
|
||||
if err := rulesList.Parse(config); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
parsedRules := make([]ParsedRule, 0, len(rulesList))
|
||||
for _, rule := range rulesList {
|
||||
parsedRules = append(parsedRules, ParsedRule{
|
||||
Name: rule.Name,
|
||||
On: rule.On.String(),
|
||||
Do: rule.Do.String(),
|
||||
})
|
||||
}
|
||||
|
||||
return parsedRules, rulesList, nil
|
||||
}
|
||||
|
||||
func parseRawRules(rawRules []RawRule) ([]ParsedRule, rules.Rules, error) {
|
||||
parsedRules := make([]ParsedRule, 0, len(rawRules))
|
||||
rulesList := make(rules.Rules, 0, len(rawRules))
|
||||
|
||||
|
||||
@@ -22,13 +22,10 @@ func TestPlayground(t *testing.T) {
|
||||
{
|
||||
name: "simple path matching rule",
|
||||
request: PlaygroundRequest{
|
||||
Rules: []RawRule{
|
||||
{
|
||||
Name: "test rule",
|
||||
On: "path /api",
|
||||
Do: "pass",
|
||||
},
|
||||
},
|
||||
Rules: `- name: test rule
|
||||
on: path /api
|
||||
do: pass
|
||||
`,
|
||||
MockRequest: MockRequest{
|
||||
Method: "GET",
|
||||
Path: "/api",
|
||||
@@ -53,13 +50,10 @@ func TestPlayground(t *testing.T) {
|
||||
{
|
||||
name: "header matching rule",
|
||||
request: PlaygroundRequest{
|
||||
Rules: []RawRule{
|
||||
{
|
||||
Name: "check user agent",
|
||||
On: "header User-Agent Chrome",
|
||||
Do: "error 403 Forbidden",
|
||||
},
|
||||
},
|
||||
Rules: `- name: check user agent
|
||||
on: header User-Agent Chrome
|
||||
do: error 403 Forbidden
|
||||
`,
|
||||
MockRequest: MockRequest{
|
||||
Method: "GET",
|
||||
Path: "/",
|
||||
@@ -90,13 +84,10 @@ func TestPlayground(t *testing.T) {
|
||||
{
|
||||
name: "invalid rule syntax",
|
||||
request: PlaygroundRequest{
|
||||
Rules: []RawRule{
|
||||
{
|
||||
Name: "bad rule",
|
||||
On: "invalid_checker something",
|
||||
Do: "pass",
|
||||
},
|
||||
},
|
||||
Rules: `- name: bad rule
|
||||
on: invalid_checker something
|
||||
do: pass
|
||||
`,
|
||||
MockRequest: MockRequest{
|
||||
Method: "GET",
|
||||
Path: "/",
|
||||
@@ -115,13 +106,10 @@ func TestPlayground(t *testing.T) {
|
||||
{
|
||||
name: "rewrite path rule",
|
||||
request: PlaygroundRequest{
|
||||
Rules: []RawRule{
|
||||
{
|
||||
Name: "rewrite rule",
|
||||
On: "path glob(/api/*)",
|
||||
Do: "rewrite /api/ /v1/",
|
||||
},
|
||||
},
|
||||
Rules: `- name: rewrite rule
|
||||
on: path glob(/api/*)
|
||||
do: rewrite /api/ /v1/
|
||||
`,
|
||||
MockRequest: MockRequest{
|
||||
Method: "GET",
|
||||
Path: "/api/users",
|
||||
@@ -148,13 +136,10 @@ func TestPlayground(t *testing.T) {
|
||||
{
|
||||
name: "method matching rule",
|
||||
request: PlaygroundRequest{
|
||||
Rules: []RawRule{
|
||||
{
|
||||
Name: "block POST",
|
||||
On: "method POST",
|
||||
Do: `error "405" "Method Not Allowed"`,
|
||||
},
|
||||
},
|
||||
Rules: `- name: block POST
|
||||
on: method POST
|
||||
do: error "405" "Method Not Allowed"
|
||||
`,
|
||||
MockRequest: MockRequest{
|
||||
Method: "POST",
|
||||
Path: "/api",
|
||||
@@ -173,6 +158,63 @@ func TestPlayground(t *testing.T) {
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "block syntax default rule",
|
||||
request: PlaygroundRequest{
|
||||
Rules: `default {
|
||||
pass
|
||||
}`,
|
||||
MockRequest: MockRequest{
|
||||
Method: "GET",
|
||||
Path: "/",
|
||||
},
|
||||
},
|
||||
wantStatusCode: http.StatusOK,
|
||||
checkResponse: func(t *testing.T, resp PlaygroundResponse) {
|
||||
if len(resp.ParsedRules) != 1 {
|
||||
t.Errorf("expected 1 parsed rule, got %d", len(resp.ParsedRules))
|
||||
}
|
||||
if resp.ParsedRules[0].ValidationError != nil {
|
||||
t.Errorf("expected rule to be valid, got error: %v", resp.ParsedRules[0].ValidationError)
|
||||
}
|
||||
if !resp.UpstreamCalled {
|
||||
t.Error("expected upstream to be called")
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "block syntax conditional rule",
|
||||
request: PlaygroundRequest{
|
||||
Rules: `header User-Agent Chrome {
|
||||
error 403 Forbidden
|
||||
}`,
|
||||
MockRequest: MockRequest{
|
||||
Method: "GET",
|
||||
Path: "/",
|
||||
Headers: map[string][]string{
|
||||
"User-Agent": {"Chrome"},
|
||||
},
|
||||
},
|
||||
},
|
||||
wantStatusCode: http.StatusOK,
|
||||
checkResponse: func(t *testing.T, resp PlaygroundResponse) {
|
||||
if len(resp.ParsedRules) != 1 {
|
||||
t.Errorf("expected 1 parsed rule, got %d", len(resp.ParsedRules))
|
||||
}
|
||||
if resp.ParsedRules[0].ValidationError != nil {
|
||||
t.Errorf("expected rule to be valid, got error: %v", resp.ParsedRules[0].ValidationError)
|
||||
}
|
||||
if len(resp.MatchedRules) != 1 {
|
||||
t.Errorf("expected 1 matched rule, got %d", len(resp.MatchedRules))
|
||||
}
|
||||
if resp.FinalResponse.StatusCode != http.StatusForbidden {
|
||||
t.Errorf("expected status 403, got %d", resp.FinalResponse.StatusCode)
|
||||
}
|
||||
if resp.UpstreamCalled {
|
||||
t.Error("expected upstream not to be called")
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
|
||||
Reference in New Issue
Block a user