Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[TT-12634] Add oas contract and migrations to/from classic apidef #6416

Merged
merged 3 commits into from
Jul 15, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions apidef/oas/event.go
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ type WebhookEvent struct {
// An empty value is interpreted as "0s", implying no cool-down.
// It's important to format the string correctly, as invalid formats will
// be considered as 0s/empty.
CoolDownPeriod time.ReadableDuration `json:"cooldownPeriod" bson:"cooldownPeriod"`
CoolDownPeriod ReadableDuration `json:"cooldownPeriod" bson:"cooldownPeriod"`
// BodyTemplate is the template to be used for request payload.
BodyTemplate string `json:"bodyTemplate,omitempty" bson:"bodyTemplate,omitempty"`
// Headers are the list of request headers to be used.
Expand Down Expand Up @@ -148,7 +148,7 @@ func (e *EventHandlers) Fill(api apidef.APIDefinition) {
Method: whConf.Method,
Headers: NewHeaders(whConf.HeaderList),
BodyTemplate: whConf.TemplatePath,
CoolDownPeriod: time.ReadableDuration(time.Duration(whConf.EventTimeout) * time.Second),
CoolDownPeriod: ReadableDuration(time.Duration(whConf.EventTimeout) * time.Second),
},
}

Expand Down
14 changes: 7 additions & 7 deletions apidef/oas/event_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ func TestEventHandlers(t *testing.T) {
URL: "https://webhook.site/uuid",
Headers: Headers{{Name: "Auth", Value: "key"}},
BodyTemplate: "/path/to/template",
CoolDownPeriod: time.ReadableDuration(time.Second * 20),
CoolDownPeriod: ReadableDuration(time.Second * 20),
Method: http.MethodPost,
},
},
Expand All @@ -48,7 +48,7 @@ func TestEventHandlers(t *testing.T) {
URL: "https://webhook.site/uuid",
Headers: Headers{{Name: "Auth", Value: "key"}},
BodyTemplate: "/path/to/template",
CoolDownPeriod: time.ReadableDuration(time.Second * 20),
CoolDownPeriod: ReadableDuration(time.Second * 20),
Method: http.MethodPost,
},
},
Expand Down Expand Up @@ -101,7 +101,7 @@ func TestEventHandlers(t *testing.T) {
URL: "https://webhook.site/uuid",
Headers: Headers{{Name: "Auth", Value: "key"}},
BodyTemplate: "/path/to/template",
CoolDownPeriod: time.ReadableDuration(time.Second * 20),
CoolDownPeriod: ReadableDuration(time.Second * 20),
Method: http.MethodPost,
},
},
Expand Down Expand Up @@ -196,7 +196,7 @@ func TestEventHandlers(t *testing.T) {
URL: "https://webhook.site/uuid",
Headers: Headers{{Name: "Auth", Value: "key"}},
BodyTemplate: "/path/to/template",
CoolDownPeriod: time.ReadableDuration(time.Second * 20),
CoolDownPeriod: ReadableDuration(time.Second * 20),
Method: http.MethodPost,
},
},
Expand All @@ -210,7 +210,7 @@ func TestEventHandlers(t *testing.T) {
URL: "https://webhook.site/uuid",
Headers: Headers{{Name: "Auth", Value: "key"}},
BodyTemplate: "/path/to/template",
CoolDownPeriod: time.ReadableDuration(time.Second * 20),
CoolDownPeriod: ReadableDuration(time.Second * 20),
Method: http.MethodPost,
},
},
Expand Down Expand Up @@ -265,7 +265,7 @@ func TestEventHandler_MarshalJSON(t *testing.T) {
URL: "https://webhook.site/uuid",
Headers: Headers{{Name: "Auth", Value: "key"}},
BodyTemplate: "/path/to/template",
CoolDownPeriod: time.ReadableDuration(time.Second * 20),
CoolDownPeriod: ReadableDuration(time.Second * 20),
Method: http.MethodPost,
},
}
Expand Down Expand Up @@ -331,7 +331,7 @@ func TestEventHandler_UnmarshalJSON(t *testing.T) {
URL: "https://webhook.site/uuid",
Headers: Headers{{Name: "Auth", Value: "key"}},
BodyTemplate: "/path/to/template",
CoolDownPeriod: time.ReadableDuration(time.Second * 20),
CoolDownPeriod: ReadableDuration(time.Second * 20),
Method: http.MethodPost,
},
}
Expand Down
7 changes: 5 additions & 2 deletions apidef/oas/linter_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,9 @@ func TestXTykGateway_Lint(t *testing.T) {
if op.TransformResponseBody != nil {
op.TransformResponseBody.Format = "json"
}
if op.RateLimit != nil {
op.RateLimit.Per = ReadableDuration(time.Minute)
}
if op.URLRewrite != nil {
triggers := []*URLRewriteTrigger{}
for _, cond := range URLRewriteConditions {
Expand Down Expand Up @@ -72,14 +75,14 @@ func TestXTykGateway_Lint(t *testing.T) {
settings.Server.EventHandlers[i].Kind = event.WebhookKind
settings.Server.EventHandlers[i].Webhook.Method = http.MethodPost
settings.Server.EventHandlers[i].Trigger = event.QuotaExceeded
settings.Server.EventHandlers[i].Webhook.CoolDownPeriod = time.ReadableDuration(time.Second * 20)
settings.Server.EventHandlers[i].Webhook.CoolDownPeriod = ReadableDuration(time.Second * 20)
}

for idx, _ := range settings.Middleware.Operations {
settings.Middleware.Operations[idx].CircuitBreaker.Threshold = 0.5
}

settings.Upstream.RateLimit.Per = time.ReadableDuration(10 * time.Second)
settings.Upstream.RateLimit.Per = ReadableDuration(10 * time.Second)
}

// Encode data to json
Expand Down
30 changes: 30 additions & 0 deletions apidef/oas/operation.go
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,9 @@ type Operation struct {

// RequestSizeLimit limits the maximum allowed size of the request body in bytes.
RequestSizeLimit *RequestSizeLimit `bson:"requestSizeLimit,omitempty" json:"requestSizeLimit,omitempty"`

// RateLimit contains endpoint level rate limit configuration.
RateLimit *RateLimitEndpoint `bson:"rateLimit,omitempty" json:"rateLimit,omitempty"`
}

// AllowanceType holds the valid allowance types values.
Expand Down Expand Up @@ -158,6 +161,7 @@ func (s *OAS) fillPathsAndOperations(ep apidef.ExtendedPathsSet) {
s.fillTrackEndpoint(ep.TrackEndpoints)
s.fillDoNotTrackEndpoint(ep.DoNotTrackEndpoints)
s.fillRequestSizeLimit(ep.SizeLimit)
s.fillRateLimitEndpoints(ep.RateLimit)
}

func (s *OAS) extractPathsAndOperations(ep *apidef.ExtendedPathsSet) {
Expand Down Expand Up @@ -191,6 +195,7 @@ func (s *OAS) extractPathsAndOperations(ep *apidef.ExtendedPathsSet) {
tykOp.extractTrackEndpointTo(ep, path, method)
tykOp.extractDoNotTrackEndpointTo(ep, path, method)
tykOp.extractRequestSizeLimitTo(ep, path, method)
tykOp.extractRateLimitEndpointTo(ep, path, method)
break found
}
}
Expand Down Expand Up @@ -786,6 +791,31 @@ func (o *Operation) extractVirtualEndpointTo(ep *apidef.ExtendedPathsSet, path s
ep.Virtual = append(ep.Virtual, meta)
}

func (s *OAS) fillRateLimitEndpoints(endpointMetas []apidef.RateLimitMeta) {
for _, em := range endpointMetas {
operationID := s.getOperationID(em.Path, em.Method)
operation := s.GetTykExtension().getOperation(operationID)
if operation.RateLimit == nil {
operation.RateLimit = &RateLimitEndpoint{}
}

operation.RateLimit.Fill(em)
if ShouldOmit(operation.RateLimit) {
operation.RateLimit = nil
}
}
}

func (o *Operation) extractRateLimitEndpointTo(ep *apidef.ExtendedPathsSet, path string, method string) {
if o.RateLimit == nil {
return
}

meta := apidef.RateLimitMeta{Path: path, Method: method}
o.RateLimit.ExtractTo(&meta)
ep.RateLimit = append(ep.RateLimit, meta)
}

func (s *OAS) fillEndpointPostPlugins(endpointMetas []apidef.GoPluginMeta) {
for _, em := range endpointMetas {
operationID := s.getOperationID(em.Path, em.Method)
Expand Down
4 changes: 4 additions & 0 deletions apidef/oas/operation_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"github.com/stretchr/testify/assert"

"github.com/TykTechnologies/tyk/apidef"
"github.com/TykTechnologies/tyk/internal/time"
)

func minimumValidOAS() OAS {
Expand Down Expand Up @@ -52,6 +53,9 @@ func TestOAS_PathsAndOperations(t *testing.T) {
operation.VirtualEndpoint.Name = "" // Name is deprecated.
operation.PostPlugins = operation.PostPlugins[:1] // only 1 post plugin is considered at this point, ignore others.
operation.PostPlugins[0].Name = "" // Name is deprecated.

operation.RateLimit.Per = ReadableDuration(time.Minute)

xTykAPIGateway := &XTykAPIGateway{
Middleware: &Middleware{
Operations: Operations{
Expand Down
3 changes: 3 additions & 0 deletions apidef/oas/schema/x-tyk-api-gateway.json
Original file line number Diff line number Diff line change
Expand Up @@ -1059,6 +1059,9 @@
},
"requestSizeLimit": {
"$ref": "#/definitions/X-Tyk-RequestSizeLimit"
},
"rateLimit": {
"$ref": "#/definitions/X-Tyk-RateLimit"
}
}
},
Expand Down
21 changes: 19 additions & 2 deletions apidef/oas/upstream.go
Original file line number Diff line number Diff line change
Expand Up @@ -496,14 +496,14 @@ type RateLimit struct {
// be considered as 0s/empty.
//
// Tyk classic API definition: `global_rate_limit.per`.
Per time.ReadableDuration `json:"per" bson:"per"`
Per ReadableDuration `json:"per" bson:"per"`
}

// Fill fills *RateLimit from apidef.APIDefinition.
func (r *RateLimit) Fill(api apidef.APIDefinition) {
r.Enabled = !api.GlobalRateLimit.Disabled
r.Rate = int(api.GlobalRateLimit.Rate)
r.Per = time.ReadableDuration(time.Duration(api.GlobalRateLimit.Per) * time.Second)
r.Per = ReadableDuration(time.Duration(api.GlobalRateLimit.Per) * time.Second)
}

// ExtractTo extracts *Ratelimit into *apidef.APIDefinition.
Expand All @@ -512,3 +512,20 @@ func (r *RateLimit) ExtractTo(api *apidef.APIDefinition) {
api.GlobalRateLimit.Rate = float64(r.Rate)
api.GlobalRateLimit.Per = r.Per.Seconds()
}

// RateLimitEndpoint carries same settings as RateLimit but for endpoints.
type RateLimitEndpoint RateLimit

// Fill fills *RateLimit from apidef.RateLimitMeta.
func (r *RateLimitEndpoint) Fill(api apidef.RateLimitMeta) {
r.Enabled = !api.Disabled
r.Rate = int(api.Rate)
r.Per = ReadableDuration(time.Duration(api.Per) * time.Second)
}

// ExtractTo extracts *Ratelimit into *apidef.RateLimitMeta.
func (r *RateLimitEndpoint) ExtractTo(meta *apidef.RateLimitMeta) {
meta.Disabled = !r.Enabled
meta.Rate = float64(r.Rate)
meta.Per = r.Per.Seconds()
}
2 changes: 1 addition & 1 deletion apidef/oas/upstream_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ func TestUpstream(t *testing.T) {
RateLimit: &RateLimit{
Enabled: true,
Rate: 10,
Per: time.ReadableDuration(time.Hour + 20*time.Minute + 10*time.Second),
Per: ReadableDuration(time.Hour + 20*time.Minute + 10*time.Second),
},
}

Expand Down
Loading