Skip to content

Commit 9e0525b

Browse files
committed
allow additionalProperties in oas object 2
1 parent a517d43 commit 9e0525b

File tree

4 files changed

+5
-227
lines changed

4 files changed

+5
-227
lines changed

apidef/oas/schema/3.0.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@
4949
"^x-": {
5050
}
5151
},
52-
"additionalProperties": false,
52+
"additionalProperties": true,
5353
"definitions": {
5454
"Reference": {
5555
"type": "object",

apidef/oas/validator.go

Lines changed: 3 additions & 163 deletions
Original file line numberDiff line numberDiff line change
@@ -4,22 +4,17 @@ import (
44
"embed"
55
"errors"
66
"fmt"
7-
"github.com/TykTechnologies/tyk/pkg/utils"
87
"path/filepath"
9-
"slices"
108
"sort"
119
"strings"
1210
"sync"
1311

14-
"github.com/buger/jsonparser"
15-
"github.com/hashicorp/go-multierror"
16-
pkgver "github.com/hashicorp/go-version"
17-
lru "github.com/hashicorp/golang-lru"
18-
19-
"github.com/TykTechnologies/tyk/common/option"
2012
tykerrors "github.com/TykTechnologies/tyk/internal/errors"
2113
"github.com/TykTechnologies/tyk/internal/service/gojsonschema"
2214
logger "github.com/TykTechnologies/tyk/log"
15+
"github.com/buger/jsonparser"
16+
"github.com/hashicorp/go-multierror"
17+
pkgver "github.com/hashicorp/go-version"
2318
)
2419

2520
//go:embed schema/*
@@ -31,12 +26,6 @@ const (
3126
keyRequired = "required"
3227
keyAnyOf = "anyOf"
3328
oasSchemaVersionNotFoundFmt = "Schema not found for version %q"
34-
35-
defaultLruCacheSize = 1 << 10
36-
)
37-
38-
var (
39-
errUnexpected = errors.New("unexpected: invariant broken")
4029
)
4130

4231
var (
@@ -47,8 +36,6 @@ var (
4736
oasJSONSchemas map[string][]byte
4837

4938
defaultVersion string
50-
51-
defaultSchemaCache = utils.Must(NewLruSchemaCache(defaultLruCacheSize))
5239
)
5340

5441
func loadOASSchema() error {
@@ -148,27 +135,6 @@ func ValidateOASObject(documentBody []byte, oasVersion string) error {
148135
return validateJSON(oasSchema, documentBody)
149136
}
150137

151-
// ValidateOASObjectWithOptions validates an OAS documents due to schema with additional options.
152-
func ValidateOASObjectWithOptions(documentBody []byte, oasVersion string, opts ...option.Option[schemaModifier]) error {
153-
oasSchema, err := GetOASSchema(oasVersion)
154-
if err != nil {
155-
return err
156-
}
157-
158-
modifier := option.New(opts).Build(schemaModifier{
159-
cache: defaultSchemaCache,
160-
version: oasVersion,
161-
schema: oasSchema,
162-
})
163-
164-
oasSchema, err = modifier.getSchema()
165-
if err != nil {
166-
return err
167-
}
168-
169-
return validateJSON(oasSchema, documentBody)
170-
}
171-
172138
// ValidateOASTemplate checks a Tyk OAS API template for necessary fields,
173139
// acknowledging that some standard Tyk OAS API fields are optional in templates.
174140
func ValidateOASTemplate(documentBody []byte, oasVersion string) error {
@@ -235,20 +201,6 @@ func GetOASSchema(version string) ([]byte, error) {
235201
return oasSchema, nil
236202
}
237203

238-
// AllowRootFields extends list of allowed fields on fly if field is not described
239-
func AllowRootFields(fields ...string) option.Option[schemaModifier] {
240-
return func(s *schemaModifier) {
241-
s.allowedFields = fields
242-
}
243-
}
244-
245-
// WithCache allows pass optional cache
246-
func WithCache(cache SchemaCache) option.Option[schemaModifier] {
247-
return func(s *schemaModifier) {
248-
s.cache = cache
249-
}
250-
}
251-
252204
func findDefaultVersion(rawVersions []string) string {
253205
versions := make([]*pkgver.Version, len(rawVersions))
254206
for i, raw := range rawVersions {
@@ -280,115 +232,3 @@ func getMinorVersion(version string) (string, error) {
280232
segments := v.Segments()
281233
return fmt.Sprintf("%d.%d", segments[0], segments[1]), nil
282234
}
283-
284-
type schemaModifier struct {
285-
version string
286-
schema []byte
287-
allowedFields []string
288-
cache SchemaCache
289-
}
290-
291-
func (c *schemaModifier) cacheKey() string {
292-
allowedFields := slices.Clone(c.allowedFields)
293-
slices.SortFunc(allowedFields, strings.Compare)
294-
return c.version + ":" + strings.Join(allowedFields, ":")
295-
}
296-
297-
func (c *schemaModifier) getSchema() ([]byte, error) {
298-
if len(c.allowedFields) == 0 {
299-
return c.schema, nil
300-
}
301-
302-
key := c.cacheKey()
303-
304-
if schema, err := c.cache.Get(key, func() ([]byte, error) {
305-
var schema = copyBytes(c.schema)
306-
307-
for _, field := range c.allowedFields {
308-
_, _, _, err := jsonparser.Get(schema, keyProperties, field)
309-
310-
if errors.Is(err, jsonparser.KeyPathNotFoundError) {
311-
if schema, err = jsonparser.Set(schema, []byte(`{}`), keyProperties, field); err != nil {
312-
return nil, err
313-
}
314-
} else if err != nil {
315-
return nil, err
316-
}
317-
}
318-
319-
return schema, nil
320-
}); err != nil {
321-
return nil, err
322-
} else {
323-
return schema, nil
324-
}
325-
}
326-
327-
// SchemaCache json schema cache.
328-
type SchemaCache interface {
329-
Get(key string, create func() ([]byte, error)) ([]byte, error)
330-
}
331-
332-
// interface created for acceptance tests purposes
333-
type extendedSchemaCache interface {
334-
SchemaCache
335-
Purge()
336-
Len() int
337-
}
338-
339-
var _ SchemaCache = (*LruSchemaCache)(nil)
340-
341-
// LruSchemaCache schema caching object which implements LRU algorithm under the hood.
342-
type LruSchemaCache struct {
343-
capacity int
344-
cache *lru.Cache
345-
}
346-
347-
// NewLruSchemaCache create new cache instance.
348-
func NewLruSchemaCache(capacity int) (*LruSchemaCache, error) {
349-
lruCache, err := lru.New(capacity)
350-
351-
if err != nil {
352-
return nil, err
353-
}
354-
355-
return &LruSchemaCache{
356-
capacity: capacity,
357-
cache: lruCache,
358-
}, nil
359-
}
360-
361-
func (l *LruSchemaCache) Get(key string, create func() ([]byte, error)) ([]byte, error) {
362-
if res, ok := l.cache.Get(key); ok {
363-
bRes, okCast := res.([]byte)
364-
365-
if !okCast {
366-
return nil, errUnexpected
367-
}
368-
369-
return bRes, nil
370-
}
371-
372-
newEntry, err := create()
373-
if err != nil {
374-
return nil, err
375-
}
376-
377-
_ = l.cache.Add(key, newEntry)
378-
379-
return newEntry, nil
380-
}
381-
382-
func (l *LruSchemaCache) Purge() {
383-
l.cache.Purge()
384-
}
385-
386-
func (l *LruSchemaCache) Len() int {
387-
return l.cache.Len()
388-
}
389-
390-
func copyBytes(in []byte) []byte {
391-
out := make([]byte, len(in))
392-
copy(out, in)
393-
return out
394-
}

apidef/oas/validator_test.go

Lines changed: 0 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,7 @@ package oas
22

33
import (
44
"embed"
5-
"encoding/json"
65
"fmt"
7-
jsonpatch "github.com/evanphx/json-patch"
86
"strings"
97
"testing"
108

@@ -294,62 +292,3 @@ func TestGetOASSchema(t *testing.T) {
294292
assert.Equal(t, expectedErr, err)
295293
})
296294
}
297-
298-
func TestValidateOASObjectWithLruCache(t *testing.T) {
299-
schemaCache, err := NewLruSchemaCache(10)
300-
require.NoError(t, err)
301-
acceptanceTestsValidateOASObjectWithOptions(t, schemaCache)
302-
}
303-
304-
// acceptanceTestsValidateOASObjectWithCache acceptance tests validator + cache
305-
func acceptanceTestsValidateOASObjectWithOptions(t *testing.T, cache extendedSchemaCache) {
306-
t.Helper()
307-
308-
const oasVersion = "3.0.3"
309-
310-
cache.Purge()
311-
assert.Equal(t, 0, cache.Len())
312-
313-
t.Run("fails if additional field exists but not allowed", func(t *testing.T) {
314-
spec := extendDefaultValidOas(t, `{"additional": true}`)
315-
err := ValidateOASObjectWithOptions(spec, oasVersion, WithCache(cache))
316-
assert.Error(t, err)
317-
assert.Equal(t, 0, cache.Len())
318-
})
319-
320-
t.Run("does not fail if additional field is allowed", func(t *testing.T) {
321-
spec := extendDefaultValidOas(t, `{"additional": true}`)
322-
err := ValidateOASObjectWithOptions(spec, oasVersion, AllowRootFields("additional"), WithCache(cache))
323-
assert.NoError(t, err)
324-
assert.Equal(t, 1, cache.Len(), "puts one entry into cache")
325-
326-
spec2 := extendDefaultValidOas(t, `{"additional": true}`)
327-
err2 := ValidateOASObjectWithOptions(spec2, oasVersion, AllowRootFields("additional"), WithCache(cache))
328-
assert.NoError(t, err2)
329-
assert.Equal(t, 1, cache.Len(), "does not extend cache")
330-
})
331-
332-
t.Run("does not allow to override default validation rules", func(t *testing.T) {
333-
spec := extendDefaultValidOas(t, `{"paths": true}`)
334-
err := ValidateOASObjectWithOptions(spec, oasVersion, AllowRootFields("paths"), WithCache(cache))
335-
assert.Error(t, err)
336-
assert.Equal(t, 2, cache.Len(), "extends cache once more")
337-
})
338-
}
339-
340-
func extendDefaultValidOas(tb testing.TB, patch string) []byte {
341-
tb.Helper()
342-
343-
var spec openapi3.T
344-
spec.OpenAPI = "3.0.3"
345-
spec.Paths = openapi3.NewPaths()
346-
spec.Info = &openapi3.Info{}
347-
348-
data, err := json.Marshal(spec)
349-
require.NoError(tb, err)
350-
351-
data, err = jsonpatch.MergePatch(data, []byte(patch))
352-
require.NoError(tb, err)
353-
354-
return data
355-
}

go.mod

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -99,13 +99,11 @@ require (
9999
github.com/TykTechnologies/opentelemetry v0.0.22
100100
github.com/alecthomas/kingpin/v2 v2.4.0
101101
github.com/eclipse/paho.mqtt.golang v1.4.3
102-
github.com/evanphx/json-patch v4.12.0+incompatible
103102
github.com/getkin/kin-openapi v0.132.0
104103
github.com/go-redis/redismock/v9 v9.2.0
105104
github.com/goccy/go-json v0.10.4
106105
github.com/goccy/go-yaml v1.15.23
107106
github.com/google/go-cmp v0.7.0
108-
github.com/hashicorp/golang-lru v0.5.4
109107
github.com/hashicorp/golang-lru/v2 v2.0.7
110108
github.com/huandu/go-clone/generic v1.7.2
111109
github.com/mccutchen/go-httpbin/v2 v2.18.2
@@ -350,6 +348,7 @@ require (
350348
github.com/hashicorp/go-secure-stdlib/strutil v0.1.2 // indirect
351349
github.com/hashicorp/go-sockaddr v1.0.2 // indirect
352350
github.com/hashicorp/go-uuid v1.0.3 // indirect
351+
github.com/hashicorp/golang-lru v0.5.4 // indirect
353352
github.com/hashicorp/golang-lru/arc/v2 v2.0.7 // indirect
354353
github.com/hashicorp/hcl v1.0.0 // indirect
355354
github.com/hashicorp/raft v1.7.1 // indirect

0 commit comments

Comments
 (0)