Skip to content

Commit

Permalink
fix: enforce prefixed schema:name for manufacturer and author;
Browse files Browse the repository at this point in the history
validate TMs for import by a json schema instead
  • Loading branch information
alexbrdn committed Nov 23, 2023
1 parent e95af8d commit 8a34098
Show file tree
Hide file tree
Showing 12 changed files with 138 additions and 53 deletions.
8 changes: 1 addition & 7 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -5,23 +5,18 @@ go 1.21
require (
github.com/Masterminds/semver/v3 v3.2.1
github.com/buger/jsonparser v1.1.1
github.com/go-playground/validator/v10 v10.15.5
github.com/kennygrant/sanitize v1.2.4
github.com/spf13/cobra v1.7.0
github.com/spf13/viper v1.17.0
github.com/stretchr/testify v1.8.4
github.com/web-of-things-open-source/go-jsonschema v0.0.0-20231122093147-d1a487982f5c
github.com/web-of-things-open-source/go-jsonschema v0.0.0-20231123170049-1c3cab186d29
)

require (
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
github.com/fsnotify/fsnotify v1.6.0 // indirect
github.com/gabriel-vasile/mimetype v1.4.2 // indirect
github.com/go-playground/locales v0.14.1 // indirect
github.com/go-playground/universal-translator v0.18.1 // indirect
github.com/hashicorp/hcl v1.0.0 // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/leodido/go-urn v1.2.4 // indirect
github.com/magiconair/properties v1.8.7 // indirect
github.com/mitchellh/mapstructure v1.5.0 // indirect
github.com/pelletier/go-toml/v2 v2.1.0 // indirect
Expand All @@ -35,7 +30,6 @@ require (
github.com/subosito/gotenv v1.6.0 // indirect
go.uber.org/atomic v1.9.0 // indirect
go.uber.org/multierr v1.9.0 // indirect
golang.org/x/crypto v0.15.0 // indirect
golang.org/x/exp v0.0.0-20230905200255-921286631fa9 // indirect
golang.org/x/net v0.18.0 // indirect
golang.org/x/sys v0.14.0 // indirect
Expand Down
18 changes: 2 additions & 16 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -65,19 +65,9 @@ github.com/frankban/quicktest v1.14.4 h1:g2rn0vABPOOXmZUj+vbmUp0lPoXEMuhTpIluN0X
github.com/frankban/quicktest v1.14.4/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY=
github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw=
github.com/gabriel-vasile/mimetype v1.4.2 h1:w5qFW6JKBz9Y393Y4q372O9A7cUSequkh1Q7OhCmWKU=
github.com/gabriel-vasile/mimetype v1.4.2/go.mod h1:zApsH/mKG4w07erKIaJPFiX0Tsq9BFQgN3qGY5GnNgA=
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
github.com/go-playground/validator/v10 v10.15.5 h1:LEBecTWb/1j5TNY1YYG2RcOUN3R7NLylN+x8TTueE24=
github.com/go-playground/validator/v10 v10.15.5/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
Expand Down Expand Up @@ -155,8 +145,6 @@ github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/leodido/go-urn v1.2.4 h1:XlAE/cm/ms7TE/VMVoduSpNBoyc2dOxHs5MZSwAN63Q=
github.com/leodido/go-urn v1.2.4/go.mod h1:7ZrI8mTSeBSHl/UaRyKQW1qZeMgak41ANeCNaVckg+4=
github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY=
github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0=
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
Expand Down Expand Up @@ -198,13 +186,12 @@ github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8=
github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU=
github.com/web-of-things-open-source/go-jsonschema v0.0.0-20231122093147-d1a487982f5c h1:iQpkPMPMSByM8PLnwbLHThjUYX9NEezJfksra6nB63U=
github.com/web-of-things-open-source/go-jsonschema v0.0.0-20231122093147-d1a487982f5c/go.mod h1:qjqU56vW5YwhHIY7u48KloTM0UnzBQKsCxpTbXgvYjs=
github.com/web-of-things-open-source/go-jsonschema v0.0.0-20231123170049-1c3cab186d29 h1:jIY9WgwunMAiNCzIDPLD6vkUKNqFYT7u70Ep26Xo4CA=
github.com/web-of-things-open-source/go-jsonschema v0.0.0-20231123170049-1c3cab186d29/go.mod h1:qjqU56vW5YwhHIY7u48KloTM0UnzBQKsCxpTbXgvYjs=
github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f h1:J9EGpcZtP0E/raorCMxlFGSTBrsSlaDGf3jU/qvAE2c=
github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHovont7NscjpAxXsDA8S8BMYve8Y5+7cuRE7R0=
Expand Down Expand Up @@ -234,7 +221,6 @@ golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPh
golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.15.0 h1:frVn1TEaCEaZcn3Tmd7Y2b5KKPaZ+I32Q2OA3kYp5TA=
golang.org/x/crypto v0.15.0/go.mod h1:4ChreQoLWfG3xLDer1WdlH5NdlQ3+mwnQq1YTKY+72g=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
Expand Down
56 changes: 56 additions & 0 deletions internal/commands/validate/tmc-mandatory.schema.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
{
"description": "JSON Schema for validating Thing Models for importing into Thing Model Catalog",
"$schema": "http://json-schema.org/draft-07/schema#",
"$id": "tm-mandatory.schema.json",
"definitions": {
"context": {
"type": "array",
"contains": {
"type": "object",
"properties": {
"schema": {
"const": "https://schema.org/"
}
},
"required": [
"schema"
]
}
},
"objWithSchemaName": {
"type": "object",
"properties": {
"schema:name": {
"type": "string",
"pattern": "\\w+"
}
},
"required": [
"schema:name"
]
}
},
"type": "object",
"properties": {
"@context": {
"$ref": "#/definitions/context"
},
"schema:mpn": {
"type": "string",
"pattern": "\\w+"
},
"schema:author": {
"$ref": "#/definitions/objWithSchemaName"
},
"schema:manufacturer": {
"$ref": "#/definitions/objWithSchemaName"
}
},
"additionalProperties": true,
"required": [
"@context",
"schema:author",
"schema:manufacturer",
"schema:mpn"
]
}
22 changes: 14 additions & 8 deletions internal/commands/validate/validate.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import (
"encoding/json"
"log/slog"

"github.com/go-playground/validator/v10"
"github.com/kennygrant/sanitize"
"github.com/web-of-things-open-source/go-jsonschema"
"github.com/web-of-things-open-source/tm-catalog-cli/internal/model"
Expand All @@ -18,12 +17,20 @@ var tmValidationSchema []byte
//go:embed modbus.schema.json
var modbusValidationSchema []byte

//go:embed tmc-mandatory.schema.json
var tmcMandatorySchema []byte

var tmcMandatoryValidator *jsonschema.Schema
var tmValidator *jsonschema.Schema
var modbusValidator *jsonschema.Schema
var structValidator *validator.Validate

func init() {
var err error
tmcMandatoryValidator, err = jsonschema.New(tmcMandatorySchema)
if err != nil {
panic(err)
}

tmValidator, err = jsonschema.New(tmValidationSchema)
if err != nil {
panic(err)
Expand All @@ -37,7 +44,6 @@ func init() {
panic(err)
}

structValidator = validator.New(validator.WithRequiredStructEnabled())
}

func ValidateAsTM(raw []byte) error {
Expand All @@ -59,13 +65,13 @@ func shouldTryModbus(raw []byte) bool {
return bytes.Index(raw, []byte("\"modbus:")) != -1
}

func ParseRequiredMetadata(raw []byte) (*model.ThingModel, error) {
tm := &model.ThingModel{}
err := json.Unmarshal(raw, tm)
func ValidateAsTmcImportable(raw []byte) (*model.ThingModel, error) {
_, err := tmcMandatoryValidator.Validate(raw)
if err != nil {
return nil, err
}
err = structValidator.Struct(tm)
tm := &model.ThingModel{}
err = json.Unmarshal(raw, tm)
if err != nil {
return nil, err
}
Expand All @@ -80,7 +86,7 @@ func ParseRequiredMetadata(raw []byte) (*model.ThingModel, error) {
func ValidateThingModel(raw []byte) (*model.ThingModel, error) {
log := slog.Default()

tm, err := ParseRequiredMetadata(raw)
tm, err := ValidateAsTmcImportable(raw)
if err != nil {
return nil, err
}
Expand Down
41 changes: 37 additions & 4 deletions internal/commands/validate/validate_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,17 +43,50 @@ func TestValidateAsModbus(t *testing.T) {
}

func TestParseRequiredMetadata(t *testing.T) {
tmFile := `{ "schema:manufacturer": {
"name": "omnicorp GmbH & Co. KG"
tmFile := `{
"@context": [{
"schema":"https://schema.org/"
}],
"schema:manufacturer": {
"schema:name": "omnicorp GmbH & Co. KG"
},
"schema:mpn": "sense&all",
"schema:author": {
"name": "omnicorp R&D/research"
"schema:name": "omnicorp R&D/research"
}
}`
tm, err := ParseRequiredMetadata([]byte(tmFile))
tm, err := ValidateAsTmcImportable([]byte(tmFile))
assert.NoError(t, err)
assert.Equal(t, "omnicorp-GmbH-Co-KG", tm.Manufacturer.Name)
assert.Equal(t, "omnicorp-R-D-research", tm.Author.Name)
assert.Equal(t, "sense-all", tm.Mpn)

tmFile = `{
"@context": [{
"schema":"https://schema.org/"
}],
"schema:manufacturer": {
"schema:name": "omnicorp GmbH & Co. KG"
},
"schema:mpn": "sense&all",
"schema:author": {
"schema:name": " .-"
}
}`
tm, err = ValidateAsTmcImportable([]byte(tmFile))
assert.Error(t, err)
assert.ErrorContains(t, err, "$.schema:author.schema:name")

tmFile = `{
"@context": [{
"schema":"https://schema.org/"
}],
"schema:mpn": "sense&all",
"schema:author": {
"schema:name": "omnicorp"
}
}`
_, err = ValidateAsTmcImportable([]byte(tmFile))
assert.Error(t, err)
assert.ErrorContains(t, err, "schema:manufacturer at \"$\"")
}
4 changes: 2 additions & 2 deletions internal/model/model.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,10 @@ type ThingModel struct {
}

type SchemaAuthor struct {
Name string `json:"name" validate:"required"`
Name string `json:"schema:name" validate:"required"`
}
type SchemaManufacturer struct {
Name string `json:"name" validate:"required"`
Name string `json:"schema:name" validate:"required"`
}

type Version struct {
Expand Down
8 changes: 5 additions & 3 deletions test/data/push/omnilamp-versioned.json
Original file line number Diff line number Diff line change
@@ -1,16 +1,18 @@
{
"@context": [
"https://www.w3.org/2022/wot/td/v1.1",
"https://schema.org/docs/jsonldcontext.json"
{
"schema": "https://schema.org/"
}
],
"@type": "tm:ThingModel",
"title": "Lamp Thing Model",
"schema:manufacturer": {
"name": "omnicorp"
"schema:name": "omnicorp"
},
"schema:mpn": "omnilamp",
"schema:author": {
"name": "omnicorp TM department"
"schema:name": "omnicorp TM department"
},
"properties": {
"status": {
Expand Down
8 changes: 5 additions & 3 deletions test/data/push/omnilamp.json
Original file line number Diff line number Diff line change
@@ -1,16 +1,18 @@
{
"@context": [
"https://www.w3.org/2022/wot/td/v1.1",
"https://schema.org/docs/jsonldcontext.json"
{
"schema": "https://schema.org/"
}
],
"@type": "tm:ThingModel",
"title": "Lamp Thing Model",
"schema:manufacturer": {
"name": "omnicorp"
"schema:name": "omnicorp"
},
"schema:mpn": "omnilamp",
"schema:author": {
"name": "omnicorp TM department"
"schema:name": "omnicorp TM department"
},
"properties": {
"status": {
Expand Down
5 changes: 3 additions & 2 deletions test/data/validate/modbus-senseall-broken.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
"@context": [
"https://www.w3.org/2019/wot/td/v1",
{
"schema": "https://schema.org/",
"modbus": "https://www.w3.org/2019/wot/modbus"
}
],
Expand All @@ -16,10 +17,10 @@
},
"schema:mpn": "senseall",
"schema:manufacturer": {
"name": "omnicorp"
"schema:name": "omnicorp"
},
"schema:author": {
"name": "omnicorp"
"schema:name": "omnicorp"
},
"security": "nosec_sc",
"properties": {
Expand Down
5 changes: 3 additions & 2 deletions test/data/validate/modbus-senseall.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
"@context": [
"https://www.w3.org/2019/wot/td/v1",
{
"schema": "https://schema.org/",
"modbus": "https://www.w3.org/2019/wot/modbus"
}
],
Expand All @@ -16,10 +17,10 @@
},
"schema:mpn": "senseall",
"schema:manufacturer": {
"name": "omnicorp"
"schema:name": "omnicorp"
},
"schema:author": {
"name": "omnicorp"
"schema:name": "omnicorp"
},
"security": "nosec_sc",
"properties": {
Expand Down
8 changes: 5 additions & 3 deletions test/data/validate/omnilamp-broken.json
Original file line number Diff line number Diff line change
@@ -1,16 +1,18 @@
{
"@context": [
"https://www.w3.org/2022/wot/td/v1.1",
"https://schema.org/docs/jsonldcontext.json"
{
"schema": "https://schema.org/"
}
],
"@type": "tm:ThingModel",
"title": "Lamp Thing Model",
"schema:manufacturer": {
"name": "omnicorp"
"schema:name": "omnicorp"
},
"schema:mpn": "omnilamp",
"schema:author": {
"name": "omnicorp TM department"
"schema:name": "omnicorp TM department"
},
"properties": {
"status": {
Expand Down
Loading

0 comments on commit 8a34098

Please sign in to comment.