Skip to content

Commit 77cd0cd

Browse files
committed
feat: contextualize methods and add ability to load custom HTTP loader
1 parent b530fb4 commit 77cd0cd

20 files changed

+190
-80
lines changed

base64loader/base64loader.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,11 @@
66
// To use base64loader, link this package into your program:
77
// import _ "github.com/ory/jsonschema/v3/base64loader"
88
//
9-
package fileloader
9+
package base64loader
1010

1111
import (
1212
"bytes"
13+
"context"
1314
"encoding/base64"
1415
"fmt"
1516
"io"
@@ -20,7 +21,7 @@ import (
2021
)
2122

2223
// Load implements jsonschema.Loader
23-
func Load(url string) (_ io.ReadCloser, err error) {
24+
func Load(ctx context.Context, url string) (_ io.ReadCloser, err error) {
2425
encoded := strings.TrimPrefix(url, "base64://")
2526

2627
var raw []byte

base64loader/base64loader_test.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
1-
package fileloader_test
1+
package base64loader_test
22

33
import (
44
"bytes"
5+
"context"
56
"encoding/base64"
67
"testing"
78

89
"github.com/stretchr/testify/require"
910

1011
"github.com/ory/jsonschema/v3"
11-
_ "github.com/ory/jsonschema/v3/base64loader"
1212
)
1313

1414
func TestLoad(t *testing.T) {
@@ -31,7 +31,7 @@ func TestLoad(t *testing.T) {
3131
base64.RawURLEncoding,
3232
base64.RawStdEncoding,
3333
} {
34-
c, err := jsonschema.Compile("base64://" + enc.EncodeToString([]byte(schema)))
34+
c, err := jsonschema.Compile(context.Background(), "base64://"+enc.EncodeToString([]byte(schema)))
3535
require.NoError(t, err)
3636
require.EqualError(t, c.Validate(bytes.NewBufferString(`{"bar": 1234}`)), "I[#/bar] S[#/properties/bar/type] expected string, but got number")
3737
}

cmd/jv/main.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
package main
66

77
import (
8+
"context"
89
"fmt"
910
"os"
1011

@@ -18,14 +19,14 @@ func main() {
1819
os.Exit(1)
1920
}
2021

21-
schema, err := jsonschema.Compile(os.Args[1])
22+
schema, err := jsonschema.Compile(context.Background(), os.Args[1])
2223
if err != nil {
2324
fmt.Fprintln(os.Stderr, err)
2425
os.Exit(1)
2526
}
2627

2728
for _, f := range os.Args[2:] {
28-
r, err := jsonschema.LoadURL(f)
29+
r, err := jsonschema.LoadURL(context.Background(), f)
2930
if err != nil {
3031
fmt.Fprintf(os.Stderr, "error in reading %q. reason: \n%v\n", f, err)
3132
os.Exit(1)

compiler.go

Lines changed: 27 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
package jsonschema
66

77
import (
8+
"context"
89
"encoding/json"
910
"fmt"
1011
"io"
@@ -44,7 +45,7 @@ type Compiler struct {
4445
// LoadURL loads the document at given URL.
4546
//
4647
// If nil, package global LoadURL is used.
47-
LoadURL func(s string) (io.ReadCloser, error)
48+
LoadURL func(ctx context.Context, s string) (io.ReadCloser, error)
4849
}
4950

5051
// NewCompiler returns a json-schema Compiler object.
@@ -81,8 +82,8 @@ func (c *Compiler) AddResource(url string, r io.Reader) error {
8182

8283
// MustCompile is like Compile but panics if the url cannot be compiled to *Schema.
8384
// It simplifies safe initialization of global variables holding compiled Schemas.
84-
func (c *Compiler) MustCompile(url string) *Schema {
85-
s, err := c.Compile(url)
85+
func (c *Compiler) MustCompile(ctx context.Context, url string) *Schema {
86+
s, err := c.Compile(ctx, url)
8687
if err != nil {
8788
panic(fmt.Sprintf("jsonschema: Compile(%q): %s", url, err))
8889
}
@@ -91,10 +92,10 @@ func (c *Compiler) MustCompile(url string) *Schema {
9192

9293
// Compile parses json-schema at given url returns, if successful,
9394
// a Schema object that can be used to match against json.
94-
func (c *Compiler) Compile(url string) (*Schema, error) {
95+
func (c *Compiler) Compile(ctx context.Context, url string) (*Schema, error) {
9596
base, fragment := split(url)
9697
if _, ok := c.resources[base]; !ok {
97-
r, err := c.loadURL(base)
98+
r, err := c.loadURL(ctx, base)
9899
if err != nil {
99100
return nil, err
100101
}
@@ -125,17 +126,17 @@ func (c *Compiler) Compile(url string) (*Schema, error) {
125126
r.draft = c.Draft
126127
}
127128
}
128-
return c.compileRef(r, r.url, fragment)
129+
return c.compileRef(ctx, r, r.url, fragment)
129130
}
130131

131-
func (c Compiler) loadURL(s string) (io.ReadCloser, error) {
132+
func (c Compiler) loadURL(ctx context.Context, s string) (io.ReadCloser, error) {
132133
if c.LoadURL != nil {
133-
return c.LoadURL(s)
134+
return c.LoadURL(ctx, s)
134135
}
135-
return LoadURL(s)
136+
return LoadURL(ctx, s)
136137
}
137138

138-
func (c *Compiler) compileRef(r *resource, base, ref string) (*Schema, error) {
139+
func (c *Compiler) compileRef(ctx context.Context, r *resource, base, ref string) (*Schema, error) {
139140
var err error
140141
if rootFragment(ref) {
141142
if _, ok := r.schemas["#"]; !ok {
@@ -144,7 +145,7 @@ func (c *Compiler) compileRef(r *resource, base, ref string) (*Schema, error) {
144145
}
145146
s := &Schema{URL: r.url, Ptr: "#"}
146147
r.schemas["#"] = s
147-
if _, err := c.compile(r, s, base, r.doc); err != nil {
148+
if _, err := c.compile(ctx, r, s, base, r.doc); err != nil {
148149
return nil, err
149150
}
150151
}
@@ -161,7 +162,7 @@ func (c *Compiler) compileRef(r *resource, base, ref string) (*Schema, error) {
161162
return nil, err
162163
}
163164
r.schemas[ref] = &Schema{URL: base, Ptr: ref}
164-
if _, err := c.compile(r, r.schemas[ref], ptrBase, doc); err != nil {
165+
if _, err := c.compile(ctx, r, r.schemas[ref], ptrBase, doc); err != nil {
165166
return nil, err
166167
}
167168
}
@@ -187,7 +188,7 @@ func (c *Compiler) compileRef(r *resource, base, ref string) (*Schema, error) {
187188
u, f := split(refURL)
188189
s := &Schema{URL: u, Ptr: f}
189190
r.schemas[refURL] = s
190-
if err := c.compileMap(r, s, refURL, v); err != nil {
191+
if err := c.compileMap(ctx, r, s, refURL, v); err != nil {
191192
return nil, err
192193
}
193194
return s, nil
@@ -197,10 +198,10 @@ func (c *Compiler) compileRef(r *resource, base, ref string) (*Schema, error) {
197198
if base == r.url {
198199
return nil, fmt.Errorf("invalid ref: %q", refURL)
199200
}
200-
return c.Compile(refURL)
201+
return c.Compile(ctx, refURL)
201202
}
202203

203-
func (c *Compiler) compile(r *resource, s *Schema, base string, m interface{}) (*Schema, error) {
204+
func (c *Compiler) compile(ctx context.Context, r *resource, s *Schema, base string, m interface{}) (*Schema, error) {
204205
if s == nil {
205206
s = new(Schema)
206207
s.URL, _ = split(base)
@@ -210,11 +211,11 @@ func (c *Compiler) compile(r *resource, s *Schema, base string, m interface{}) (
210211
s.Always = &m
211212
return s, nil
212213
default:
213-
return s, c.compileMap(r, s, base, m.(map[string]interface{}))
214+
return s, c.compileMap(ctx, r, s, base, m.(map[string]interface{}))
214215
}
215216
}
216217

217-
func (c *Compiler) compileMap(r *resource, s *Schema, base string, m map[string]interface{}) error {
218+
func (c *Compiler) compileMap(ctx context.Context, r *resource, s *Schema, base string, m map[string]interface{}) error {
218219
var err error
219220

220221
if id, ok := m[r.draft.id]; ok {
@@ -225,7 +226,7 @@ func (c *Compiler) compileMap(r *resource, s *Schema, base string, m map[string]
225226

226227
if ref, ok := m["$ref"]; ok {
227228
b, _ := split(base)
228-
s.Ref, err = c.compileRef(r, b, ref.(string))
229+
s.Ref, err = c.compileRef(ctx, r, b, ref.(string))
229230
if err != nil {
230231
return err
231232
}
@@ -267,7 +268,7 @@ func (c *Compiler) compileMap(r *resource, s *Schema, base string, m map[string]
267268

268269
loadSchema := func(pname string) (*Schema, error) {
269270
if pvalue, ok := m[pname]; ok {
270-
return c.compile(r, nil, base, pvalue)
271+
return c.compile(ctx, r, nil, base, pvalue)
271272
}
272273
return nil, nil
273274
}
@@ -281,7 +282,7 @@ func (c *Compiler) compileMap(r *resource, s *Schema, base string, m map[string]
281282
pvalue := pvalue.([]interface{})
282283
schemas := make([]*Schema, len(pvalue))
283284
for i, v := range pvalue {
284-
sch, err := c.compile(r, nil, base, v)
285+
sch, err := c.compile(ctx, r, nil, base, v)
285286
if err != nil {
286287
return nil, err
287288
}
@@ -318,7 +319,7 @@ func (c *Compiler) compileMap(r *resource, s *Schema, base string, m map[string]
318319
props := props.(map[string]interface{})
319320
s.Properties = make(map[string]*Schema, len(props))
320321
for pname, pmap := range props {
321-
s.Properties[pname], err = c.compile(r, nil, base, pmap)
322+
s.Properties[pname], err = c.compile(ctx, r, nil, base, pmap)
322323
if err != nil {
323324
return err
324325
}
@@ -333,7 +334,7 @@ func (c *Compiler) compileMap(r *resource, s *Schema, base string, m map[string]
333334
patternProps := patternProps.(map[string]interface{})
334335
s.PatternProperties = make(map[*regexp.Regexp]*Schema, len(patternProps))
335336
for pattern, pmap := range patternProps {
336-
s.PatternProperties[regexp.MustCompile(pattern)], err = c.compile(r, nil, base, pmap)
337+
s.PatternProperties[regexp.MustCompile(pattern)], err = c.compile(ctx, r, nil, base, pmap)
337338
if err != nil {
338339
return err
339340
}
@@ -347,7 +348,7 @@ func (c *Compiler) compileMap(r *resource, s *Schema, base string, m map[string]
347348
s.AdditionalProperties = false
348349
}
349350
case map[string]interface{}:
350-
s.AdditionalProperties, err = c.compile(r, nil, base, additionalProps)
351+
s.AdditionalProperties, err = c.compile(ctx, r, nil, base, additionalProps)
351352
if err != nil {
352353
return err
353354
}
@@ -362,7 +363,7 @@ func (c *Compiler) compileMap(r *resource, s *Schema, base string, m map[string]
362363
case []interface{}:
363364
s.Dependencies[pname] = toStrings(pvalue)
364365
default:
365-
s.Dependencies[pname], err = c.compile(r, nil, base, pvalue)
366+
s.Dependencies[pname], err = c.compile(ctx, r, nil, base, pvalue)
366367
if err != nil {
367368
return err
368369
}
@@ -388,7 +389,7 @@ func (c *Compiler) compileMap(r *resource, s *Schema, base string, m map[string]
388389
case bool:
389390
s.AdditionalItems = additionalItems
390391
case map[string]interface{}:
391-
s.AdditionalItems, err = c.compile(r, nil, base, additionalItems)
392+
s.AdditionalItems, err = c.compile(ctx, r, nil, base, additionalItems)
392393
if err != nil {
393394
return err
394395
}
@@ -397,7 +398,7 @@ func (c *Compiler) compileMap(r *resource, s *Schema, base string, m map[string]
397398
s.AdditionalItems = true
398399
}
399400
default:
400-
s.Items, err = c.compile(r, nil, base, items)
401+
s.Items, err = c.compile(ctx, r, nil, base, items)
401402
if err != nil {
402403
return err
403404
}

draft4.go

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,10 @@
44

55
package jsonschema
66

7-
import "strings"
7+
import (
8+
"context"
9+
"strings"
10+
)
811

912
// Draft4 respresents http://json-schema.org/specification-links.html#draft-4
1013
var Draft4 = &Draft{id: "id", version: 4, url: "http://json-schema.org/draft-04/schema", data: `{
@@ -162,10 +165,11 @@ var Draft4 = &Draft{id: "id", version: 4, url: "http://json-schema.org/draft-04/
162165
}`}
163166

164167
func init() {
168+
ctx := context.Background()
165169
c := NewCompiler()
166170
err := c.AddResource(Draft4.url, strings.NewReader(Draft4.data))
167171
if err != nil {
168172
panic(err)
169173
}
170-
Draft4.meta = c.MustCompile(Draft4.url)
174+
Draft4.meta = c.MustCompile(ctx, Draft4.url)
171175
}

draft6.go

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,10 @@
44

55
package jsonschema
66

7-
import "strings"
7+
import (
8+
"context"
9+
"strings"
10+
)
811

912
// Draft6 respresents http://json-schema.org/specification-links.html#draft-6
1013
var Draft6 = &Draft{id: "$id", version: 6, url: "http://json-schema.org/draft-06/schema", data: `{
@@ -160,10 +163,11 @@ var Draft6 = &Draft{id: "$id", version: 6, url: "http://json-schema.org/draft-06
160163
}`}
161164

162165
func init() {
166+
ctx := context.Background()
163167
c := NewCompiler()
164168
err := c.AddResource(Draft6.url, strings.NewReader(Draft6.data))
165169
if err != nil {
166170
panic(err)
167171
}
168-
Draft6.meta = c.MustCompile(Draft6.url)
172+
Draft6.meta = c.MustCompile(ctx, Draft6.url)
169173
}

draft7.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
package jsonschema
66

77
import (
8+
"context"
89
"strings"
910
)
1011

@@ -186,10 +187,11 @@ var Draft7 = &Draft{id: "$id", version: 7, url: "http://json-schema.org/draft-07
186187
}`}
187188

188189
func init() {
190+
ctx := context.Background()
189191
c := NewCompiler()
190192
err := c.AddResource(Draft7.url, strings.NewReader(Draft7.data))
191193
if err != nil {
192194
panic(err)
193195
}
194-
Draft7.meta = c.MustCompile(Draft7.url)
196+
Draft7.meta = c.MustCompile(ctx, Draft7.url)
195197
}

extension.go

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44

55
package jsonschema
66

7+
import "context"
8+
79
// Extension is used to define additional keywords to standard jsonschema.
810
// An extension can implement more than one keyword.
911
//
@@ -33,14 +35,14 @@ type CompilerContext struct {
3335

3436
// Compile compiles given value v into *Schema. This is useful in implementing
3537
// keyword like allOf/oneOf
36-
func (ctx CompilerContext) Compile(v interface{}) (*Schema, error) {
37-
return ctx.c.compile(ctx.r, nil, ctx.base, v)
38+
func (ctx CompilerContext) Compile(c context.Context, v interface{}) (*Schema, error) {
39+
return ctx.c.compile(c, ctx.r, nil, ctx.base, v)
3840
}
3941

4042
// CompileRef compiles the schema referenced by ref uri
41-
func (ctx CompilerContext) CompileRef(ref string) (*Schema, error) {
43+
func (ctx CompilerContext) CompileRef(c context.Context, ref string) (*Schema, error) {
4244
b, _ := split(ctx.base)
43-
return ctx.c.compileRef(ctx.r, b, ref)
45+
return ctx.c.compileRef(c, ctx.r, b, ref)
4446
}
4547

4648
// ValidationContext provides additional context required in validating for extension.

0 commit comments

Comments
 (0)