Skip to content

Commit a86a8fc

Browse files
committed
Generalize JSON schema handling code
Previously, the JSON schema handling code was all under the libraryproperties package, and written specifically for use in validating library.properties against the JSON schema. However, there will be be a need to work with schemas in other contexts, most immediately for writitng the schema tests, but also likely when additional schemas are added for the other Arduino project configuration data files. So it will be helpful to have the general purpose schema handling code in a dedicated package, leaving only the library.properties-specific aspects of the code in the libraryproperties package.
1 parent e3d1875 commit a86a8fc

File tree

4 files changed

+105
-54
lines changed

4 files changed

+105
-54
lines changed

check/checkdata/schema/schema.go

+77
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
// Package schema contains code for working with JSON schema.
2+
package schema
3+
4+
import (
5+
"os"
6+
"regexp"
7+
8+
"github.com/arduino/arduino-check/util"
9+
"github.com/arduino/go-paths-helper"
10+
"github.com/xeipuuv/gojsonschema"
11+
)
12+
13+
// Compile compiles the schema files specified by the filename arguments and returns the compiled schema.
14+
func Compile(schemaFilename string, referencedSchemaFilenames []string) *gojsonschema.Schema {
15+
workingPath, _ := os.Getwd()
16+
schemasPath := paths.New(workingPath)
17+
18+
schemaLoader := gojsonschema.NewSchemaLoader()
19+
20+
// Load the referenced schemas.
21+
for _, referencedSchemaFilename := range referencedSchemaFilenames {
22+
referencedSchemaPath := schemasPath.Join(referencedSchemaFilename)
23+
referencedSchemaURI := util.PathURI(referencedSchemaPath)
24+
err := schemaLoader.AddSchemas(gojsonschema.NewReferenceLoader(referencedSchemaURI))
25+
if err != nil {
26+
panic(err.Error())
27+
}
28+
}
29+
30+
// Compile the schema.
31+
schemaPath := schemasPath.Join(schemaFilename)
32+
schemaURI := util.PathURI(schemaPath)
33+
compiledSchema, err := schemaLoader.Compile(gojsonschema.NewReferenceLoader(schemaURI))
34+
if err != nil {
35+
panic(err.Error())
36+
}
37+
return compiledSchema
38+
}
39+
40+
// Validate validates an instance against a JSON schema and returns the gojsonschema.Result object.
41+
func Validate(instanceObject interface{}, schemaObject *gojsonschema.Schema) *gojsonschema.Result {
42+
result, err := schemaObject.Validate(gojsonschema.NewGoLoader(instanceObject))
43+
if err != nil {
44+
panic(err.Error())
45+
}
46+
47+
return result
48+
}
49+
50+
// RequiredPropertyMissing returns whether the given required property is missing from the document.
51+
func RequiredPropertyMissing(propertyName string, validationResult *gojsonschema.Result) bool {
52+
return ValidationErrorMatch("required", "(root)", propertyName+" is required", validationResult)
53+
}
54+
55+
// PropertyPatternMismatch returns whether the given property did not match the regular expression defined in the JSON schema.
56+
func PropertyPatternMismatch(propertyName string, validationResult *gojsonschema.Result) bool {
57+
return ValidationErrorMatch("pattern", propertyName, "", validationResult)
58+
}
59+
60+
// ValidationErrorMatch returns whether the given query matches against the JSON schema validation error.
61+
// See: https://github.com/xeipuuv/gojsonschema#working-with-errors
62+
func ValidationErrorMatch(typeQuery string, fieldQuery string, descriptionQueryRegexp string, validationResult *gojsonschema.Result) bool {
63+
if validationResult.Valid() {
64+
// No error, so nothing to match
65+
return false
66+
}
67+
for _, validationError := range validationResult.Errors() {
68+
if typeQuery == "" || typeQuery == validationError.Type() {
69+
if fieldQuery == "" || fieldQuery == validationError.Field() {
70+
descriptionQuery := regexp.MustCompile(descriptionQueryRegexp)
71+
return descriptionQuery.MatchString(validationError.Description())
72+
}
73+
}
74+
}
75+
76+
return false
77+
}

check/checkfunctions/library.go

+4-4
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@ package checkfunctions
44

55
import (
66
"github.com/arduino/arduino-check/check/checkdata"
7+
"github.com/arduino/arduino-check/check/checkdata/schema"
78
"github.com/arduino/arduino-check/check/checkresult"
8-
"github.com/arduino/arduino-check/project/library/libraryproperties"
99
)
1010

1111
// LibraryPropertiesFormat checks for invalid library.properties format.
@@ -22,7 +22,7 @@ func LibraryPropertiesNameFieldMissing() (result checkresult.Type, output string
2222
return checkresult.NotRun, ""
2323
}
2424

25-
if libraryproperties.FieldMissing("name", checkdata.LibraryPropertiesSchemaValidationResult()) {
25+
if schema.RequiredPropertyMissing("name", checkdata.LibraryPropertiesSchemaValidationResult()) {
2626
return checkresult.Fail, ""
2727
}
2828
return checkresult.Pass, ""
@@ -34,7 +34,7 @@ func LibraryPropertiesNameFieldDisallowedCharacters() (result checkresult.Type,
3434
return checkresult.NotRun, ""
3535
}
3636

37-
if libraryproperties.FieldPatternMismatch("name", checkdata.LibraryPropertiesSchemaValidationResult()) {
37+
if schema.PropertyPatternMismatch("name", checkdata.LibraryPropertiesSchemaValidationResult()) {
3838
return checkresult.Fail, ""
3939
}
4040

@@ -47,7 +47,7 @@ func LibraryPropertiesVersionFieldMissing() (result checkresult.Type, output str
4747
return checkresult.NotRun, ""
4848
}
4949

50-
if libraryproperties.FieldMissing("version", checkdata.LibraryPropertiesSchemaValidationResult()) {
50+
if schema.RequiredPropertyMissing("version", checkdata.LibraryPropertiesSchemaValidationResult()) {
5151
return checkresult.Fail, ""
5252
}
5353
return checkresult.Pass, ""

project/library/libraryproperties/libraryproperties.go

+4-50
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,7 @@
22
package libraryproperties
33

44
import (
5-
"net/url"
6-
"os"
7-
"path/filepath"
8-
5+
"github.com/arduino/arduino-check/check/checkdata/schema"
96
"github.com/arduino/go-paths-helper"
107
"github.com/arduino/go-properties-orderedmap"
118
"github.com/xeipuuv/gojsonschema"
@@ -22,51 +19,8 @@ func Properties(libraryPath *paths.Path) (*properties.Map, error) {
2219

2320
// Validate validates library.properties data against the JSON schema.
2421
func Validate(libraryProperties *properties.Map) *gojsonschema.Result {
25-
workingPath, _ := os.Getwd()
26-
schemaPath := paths.New(workingPath).Join("arduino-library-properties-schema.json")
27-
uriFriendlySchemaPath := filepath.ToSlash(schemaPath.String())
28-
schemaPathURI := url.URL{
29-
Scheme: "file",
30-
Path: uriFriendlySchemaPath,
31-
}
32-
schemaLoader := gojsonschema.NewReferenceLoader(schemaPathURI.String())
33-
34-
documentLoader := gojsonschema.NewGoLoader(libraryProperties)
35-
36-
result, err := gojsonschema.Validate(schemaLoader, documentLoader)
37-
if err != nil {
38-
panic(err.Error())
39-
}
40-
41-
return result
42-
}
43-
44-
// FieldMissing returns whether the given required field is missing from library.properties.
45-
func FieldMissing(fieldName string, validationResult *gojsonschema.Result) bool {
46-
return ValidationErrorMatch("required", "(root)", fieldName+" is required", validationResult)
47-
}
48-
49-
// FieldPatternMismatch returns whether the given field did not match the regular expression defined in the JSON schema.
50-
func FieldPatternMismatch(fieldName string, validationResult *gojsonschema.Result) bool {
51-
return ValidationErrorMatch("pattern", fieldName, "", validationResult)
52-
}
53-
54-
// ValidationErrorMatch returns whether the given query matches against the JSON schema validation error.
55-
// See: https://github.com/xeipuuv/gojsonschema#working-with-errors
56-
func ValidationErrorMatch(typeQuery string, fieldQuery string, descriptionQuery string, validationResult *gojsonschema.Result) bool {
57-
if validationResult.Valid() {
58-
// No error, so nothing to match
59-
return false
60-
}
61-
for _, validationError := range validationResult.Errors() {
62-
if typeQuery == "" || typeQuery == validationError.Type() {
63-
if fieldQuery == "" || fieldQuery == validationError.Field() {
64-
if descriptionQuery == "" || descriptionQuery == validationError.Description() {
65-
return true
66-
}
67-
}
68-
}
69-
}
22+
referencedSchemaFilenames := []string{}
23+
schemaObject := schema.Compile("arduino-library-properties-schema.json", referencedSchemaFilenames)
7024

71-
return false
25+
return schema.Validate(libraryProperties, schemaObject)
7226
}

util/util.go

+20
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
// Package util contains general purpose utility code.
2+
package util
3+
4+
import (
5+
"net/url"
6+
"path/filepath"
7+
8+
"github.com/arduino/go-paths-helper"
9+
)
10+
11+
// PathURI returns the URI representation of the path argument.
12+
func PathURI(path *paths.Path) string {
13+
uriFriendlyPath := filepath.ToSlash(path.String())
14+
pathURI := url.URL{
15+
Scheme: "file",
16+
Path: uriFriendlyPath,
17+
}
18+
19+
return pathURI.String()
20+
}

0 commit comments

Comments
 (0)