Merge pull request #5793 from ostcar/modelsvalidate_external_lib
Use openslides-models-to-go
This commit is contained in:
commit
3c50e18cb3
2
.github/workflows/models.yml
vendored
2
.github/workflows/models.yml
vendored
@ -14,7 +14,7 @@ jobs:
|
|||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v2
|
||||||
|
|
||||||
- name: Build validator
|
- name: Build validator
|
||||||
run: go build ./cmd/modelsvalidator
|
run: go build
|
||||||
working-directory: docs/modelsvalidator
|
working-directory: docs/modelsvalidator
|
||||||
env:
|
env:
|
||||||
GO111MODULE: on
|
GO111MODULE: on
|
||||||
|
@ -1,13 +1,15 @@
|
|||||||
package models
|
package check
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
models "github.com/OpenSlides/openslides-models-to-go"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Check runs some checks on the given models.
|
// Check runs some checks on the given models.
|
||||||
func Check(models map[string]Model) error {
|
func Check(data map[string]models.Model) error {
|
||||||
validators := []func(map[string]Model) error{
|
validators := []func(map[string]models.Model) error{
|
||||||
validateTypes,
|
validateTypes,
|
||||||
validateRelations,
|
validateRelations,
|
||||||
validateTemplatePrefixes,
|
validateTemplatePrefixes,
|
||||||
@ -15,7 +17,7 @@ func Check(models map[string]Model) error {
|
|||||||
|
|
||||||
errors := new(ErrorList)
|
errors := new(ErrorList)
|
||||||
for _, v := range validators {
|
for _, v := range validators {
|
||||||
if err := v(models); err != nil {
|
if err := v(data); err != nil {
|
||||||
errors.append(err)
|
errors.append(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -26,14 +28,14 @@ func Check(models map[string]Model) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func validateTypes(models map[string]Model) error {
|
func validateTypes(data map[string]models.Model) error {
|
||||||
scalar := scalarTypes()
|
scalar := scalarTypes()
|
||||||
relation := relationTypes()
|
relation := relationTypes()
|
||||||
errs := &ErrorList{
|
errs := &ErrorList{
|
||||||
Name: "type validator",
|
Name: "type validator",
|
||||||
intent: 1,
|
intent: 1,
|
||||||
}
|
}
|
||||||
for modelName, model := range models {
|
for modelName, model := range data {
|
||||||
for fieldName, field := range model.Fields {
|
for fieldName, field := range model.Fields {
|
||||||
if scalar[strings.TrimSuffix(field.Type, "[]")] {
|
if scalar[strings.TrimSuffix(field.Type, "[]")] {
|
||||||
continue
|
continue
|
||||||
@ -52,13 +54,13 @@ func validateTypes(models map[string]Model) error {
|
|||||||
return errs
|
return errs
|
||||||
}
|
}
|
||||||
|
|
||||||
func validateRelations(models map[string]Model) error {
|
func validateRelations(data map[string]models.Model) error {
|
||||||
errs := &ErrorList{
|
errs := &ErrorList{
|
||||||
Name: "relation validator",
|
Name: "relation validator",
|
||||||
intent: 1,
|
intent: 1,
|
||||||
}
|
}
|
||||||
relation := relationTypes()
|
relation := relationTypes()
|
||||||
for modelName, model := range models {
|
for modelName, model := range data {
|
||||||
Next:
|
Next:
|
||||||
for fieldName, field := range model.Fields {
|
for fieldName, field := range model.Fields {
|
||||||
r := field.Relation()
|
r := field.Relation()
|
||||||
@ -67,12 +69,12 @@ func validateRelations(models map[string]Model) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for _, c := range r.ToCollections() {
|
for _, c := range r.ToCollections() {
|
||||||
toModel, ok := models[c.Collection]
|
toModel, ok := data[c.Collection]
|
||||||
if !ok {
|
if !ok {
|
||||||
errs.append(fmt.Errorf("%s/%s directs to nonexisting model `%s`", modelName, fieldName, c.Collection))
|
errs.append(fmt.Errorf("%s/%s directs to nonexisting model `%s`", modelName, fieldName, c))
|
||||||
continue Next
|
continue Next
|
||||||
}
|
}
|
||||||
// fmt.Printf("Relation %s/%s to %s/%s\n", modelName, fieldName, c.Collection, c.ToField.Name)
|
|
||||||
toField, ok := toModel.Fields[c.ToField.Name]
|
toField, ok := toModel.Fields[c.ToField.Name]
|
||||||
if !ok {
|
if !ok {
|
||||||
errs.append(fmt.Errorf("%s/%s directs to nonexisting collectionfield `%s/%s`", modelName, fieldName, c.Collection, c.ToField.Name))
|
errs.append(fmt.Errorf("%s/%s directs to nonexisting collectionfield `%s/%s`", modelName, fieldName, c.Collection, c.ToField.Name))
|
||||||
@ -93,7 +95,7 @@ func validateRelations(models map[string]Model) error {
|
|||||||
return errs
|
return errs
|
||||||
}
|
}
|
||||||
|
|
||||||
func validateTemplatePrefixes(models map[string]Model) error {
|
func validateTemplatePrefixes(models map[string]models.Model) error {
|
||||||
errs := &ErrorList{
|
errs := &ErrorList{
|
||||||
Name: "template prefixes validator",
|
Name: "template prefixes validator",
|
||||||
intent: 1,
|
intent: 1,
|
@ -1,11 +1,12 @@
|
|||||||
package models_test
|
package check_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/OpenSlides/Openslides/modelsvalidator/models"
|
"github.com/OpenSlides/Openslides/modelsvalidator/check"
|
||||||
|
models "github.com/OpenSlides/openslides-models-to-go"
|
||||||
)
|
)
|
||||||
|
|
||||||
const yamlUnknownFieldType = `---
|
const yamlUnknownFieldType = `---
|
||||||
@ -86,7 +87,7 @@ func TestCheck(t *testing.T) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Can not unmarshal yaml: %v", err)
|
t.Fatalf("Can not unmarshal yaml: %v", err)
|
||||||
}
|
}
|
||||||
gotErr := models.Check(data)
|
gotErr := check.Check(data)
|
||||||
if tt.err == "" {
|
if tt.err == "" {
|
||||||
if gotErr != nil {
|
if gotErr != nil {
|
||||||
t.Errorf("Models.Check() returned an unexepcted error: %v", err)
|
t.Errorf("Models.Check() returned an unexepcted error: %v", err)
|
||||||
@ -98,14 +99,14 @@ func TestCheck(t *testing.T) {
|
|||||||
t.Fatalf("Models.Check() did not return an error, expected: %v", tt.err)
|
t.Fatalf("Models.Check() did not return an error, expected: %v", tt.err)
|
||||||
}
|
}
|
||||||
|
|
||||||
var errList *models.ErrorList
|
var errList *check.ErrorList
|
||||||
if !errors.As(gotErr, &errList) {
|
if !errors.As(gotErr, &errList) {
|
||||||
t.Fatalf("Models.Check() did not return a ListError, got: %v", gotErr)
|
t.Fatalf("Models.Check() did not return a ListError, got: %v", gotErr)
|
||||||
}
|
}
|
||||||
|
|
||||||
var found bool
|
var found bool
|
||||||
for _, err := range errList.Errs {
|
for _, err := range errList.Errs {
|
||||||
var errList *models.ErrorList
|
var errList *check.ErrorList
|
||||||
if !errors.As(err, &errList) {
|
if !errors.As(err, &errList) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
@ -1,4 +1,4 @@
|
|||||||
package models
|
package check
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
@ -2,6 +2,4 @@ module github.com/OpenSlides/Openslides/modelsvalidator
|
|||||||
|
|
||||||
go 1.15
|
go 1.15
|
||||||
|
|
||||||
require (
|
require github.com/OpenSlides/openslides-models-to-go v0.2.0
|
||||||
gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776
|
|
||||||
)
|
|
||||||
|
@ -1,3 +1,6 @@
|
|||||||
|
github.com/OpenSlides/openslides-models-to-go v0.2.0 h1:1D+rD94GcUZAvmHLRyzJCIj7wklU+JBkoYifwtHH08s=
|
||||||
|
github.com/OpenSlides/openslides-models-to-go v0.2.0/go.mod h1:CriCefW5smTixhFfVLiuA8NgyMX4PAU5e3YpJHnaZx8=
|
||||||
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776 h1:tQIYjPdBoyREyB9XMu+nnTclpTYkz2zFM+lzLJFO4gQ=
|
gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776 h1:tQIYjPdBoyREyB9XMu+nnTclpTYkz2zFM+lzLJFO4gQ=
|
||||||
gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
|
@ -8,7 +8,8 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/OpenSlides/Openslides/modelsvalidator/models"
|
"github.com/OpenSlides/Openslides/modelsvalidator/check"
|
||||||
|
models "github.com/OpenSlides/openslides-models-to-go"
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
@ -27,7 +28,7 @@ func main() {
|
|||||||
log.Fatalf("Invalid model format: %v", err)
|
log.Fatalf("Invalid model format: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := models.Check(data); err != nil {
|
if err := check.Check(data); err != nil {
|
||||||
log.Fatalf("Invalid model structure:\n\n%v", err)
|
log.Fatalf("Invalid model structure:\n\n%v", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,239 +0,0 @@
|
|||||||
package models
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"gopkg.in/yaml.v3"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Unmarshal parses the content of models.yml to a datastruct.q
|
|
||||||
func Unmarshal(r io.Reader) (map[string]Model, error) {
|
|
||||||
var m map[string]Model
|
|
||||||
if err := yaml.NewDecoder(r).Decode(&m); err != nil {
|
|
||||||
return nil, fmt.Errorf("decoding models: %w", err)
|
|
||||||
}
|
|
||||||
return m, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Model replresents one model from models.yml.
|
|
||||||
type Model struct {
|
|
||||||
Fields map[string]Field
|
|
||||||
}
|
|
||||||
|
|
||||||
// UnmarshalYAML decodes a yaml model to models.Model.
|
|
||||||
func (m *Model) UnmarshalYAML(node *yaml.Node) error {
|
|
||||||
return node.Decode(&m.Fields)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Field of a model.
|
|
||||||
type Field struct {
|
|
||||||
Type string
|
|
||||||
relation Relation
|
|
||||||
template *AttributeTemplate
|
|
||||||
}
|
|
||||||
|
|
||||||
// Relation returns the relation object if the Field is a relation or a
|
|
||||||
// template with a relation. In other cases, it returns nil.
|
|
||||||
func (a *Field) Relation() Relation {
|
|
||||||
if a.relation != nil {
|
|
||||||
return a.relation
|
|
||||||
}
|
|
||||||
|
|
||||||
if a.template != nil && a.template.Fields.relation != nil {
|
|
||||||
return a.template.Fields.relation
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// UnmarshalYAML decodes a model attribute from yaml.
|
|
||||||
func (a *Field) UnmarshalYAML(value *yaml.Node) error {
|
|
||||||
var s string
|
|
||||||
if err := value.Decode(&s); err == nil {
|
|
||||||
a.Type = s
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
var typer struct {
|
|
||||||
Type string `yaml:"type"`
|
|
||||||
}
|
|
||||||
if err := value.Decode(&typer); err != nil {
|
|
||||||
return fmt.Errorf("field object without type: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
a.Type = typer.Type
|
|
||||||
switch typer.Type {
|
|
||||||
case "relation":
|
|
||||||
fallthrough
|
|
||||||
case "relation-list":
|
|
||||||
var relation AttributeRelation
|
|
||||||
if err := value.Decode(&relation); err != nil {
|
|
||||||
return fmt.Errorf("invalid object of type %s at line %d object: %w", typer.Type, value.Line, err)
|
|
||||||
}
|
|
||||||
a.relation = &relation
|
|
||||||
case "generic-relation":
|
|
||||||
fallthrough
|
|
||||||
case "generic-relation-list":
|
|
||||||
var relation AttributeGenericRelation
|
|
||||||
if err := value.Decode(&relation); err != nil {
|
|
||||||
return fmt.Errorf("invalid object of type %s at line %d object: %w", typer.Type, value.Line, err)
|
|
||||||
}
|
|
||||||
a.relation = &relation
|
|
||||||
case "template":
|
|
||||||
var template AttributeTemplate
|
|
||||||
if err := value.Decode(&template); err != nil {
|
|
||||||
return fmt.Errorf("invalid object of type template object in line %d: %w", value.Line, err)
|
|
||||||
}
|
|
||||||
a.template = &template
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Relation represents some kind of relation between fields.
|
|
||||||
type Relation interface {
|
|
||||||
ToCollections() []ToCollectionField
|
|
||||||
}
|
|
||||||
|
|
||||||
// ToCollectionField represents a field and a collection
|
|
||||||
type ToCollectionField struct {
|
|
||||||
Collection string `yaml:"collection"`
|
|
||||||
ToField ToField `yaml:"field"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// UnmarshalYAML decodes the models.yml to a To object.
|
|
||||||
func (t *ToCollectionField) UnmarshalYAML(value *yaml.Node) error {
|
|
||||||
var s string
|
|
||||||
if err := value.Decode(&s); err == nil {
|
|
||||||
cf := strings.Split(s, "/")
|
|
||||||
if len(cf) != 2 {
|
|
||||||
return fmt.Errorf("invalid value of `to` in line %d, expected one `/`: %s", value.Line, s)
|
|
||||||
}
|
|
||||||
t.Collection = cf[0]
|
|
||||||
t.ToField.Name = cf[1]
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
var d struct {
|
|
||||||
Collection string `yaml:"collection"`
|
|
||||||
Field ToField `yaml:"field"`
|
|
||||||
}
|
|
||||||
if err := value.Decode(&d); err != nil {
|
|
||||||
return fmt.Errorf("decoding to collection field at line %d: %w", value.Line, err)
|
|
||||||
}
|
|
||||||
t.Collection = d.Collection
|
|
||||||
t.ToField = d.Field
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
type ToField struct {
|
|
||||||
Name string `yaml:"name"`
|
|
||||||
Type string `yaml:"type"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// UnmarshalYAML decodes the models.yml to a ToField object.
|
|
||||||
func (t *ToField) UnmarshalYAML(value *yaml.Node) error {
|
|
||||||
var s string
|
|
||||||
if err := value.Decode(&s); err == nil {
|
|
||||||
t.Name = s
|
|
||||||
t.Type = "normal"
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
var d struct {
|
|
||||||
Name string `yaml:"name"`
|
|
||||||
Type string `yaml:"type"`
|
|
||||||
}
|
|
||||||
if err := value.Decode(&d); err != nil {
|
|
||||||
return fmt.Errorf("decoding to field at line %d: %w", value.Line, err)
|
|
||||||
}
|
|
||||||
t.Name = d.Name
|
|
||||||
t.Type = d.Type
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// AttributeRelation is a relation or relation-list field.
|
|
||||||
type AttributeRelation struct {
|
|
||||||
To To `yaml:"to"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// ToCollection returns the names of the collections there the attribute points
|
|
||||||
// to. It is allways a slice with one element.
|
|
||||||
func (r AttributeRelation) ToCollections() []ToCollectionField {
|
|
||||||
return []ToCollectionField{r.To.CollectionField}
|
|
||||||
}
|
|
||||||
|
|
||||||
// To is shows a Relation where to point to.
|
|
||||||
type To struct {
|
|
||||||
CollectionField ToCollectionField
|
|
||||||
}
|
|
||||||
|
|
||||||
// UnmarshalYAML decodes the models.yml to a To object.
|
|
||||||
func (t *To) UnmarshalYAML(value *yaml.Node) error {
|
|
||||||
var s string
|
|
||||||
if err := value.Decode(&s); err == nil {
|
|
||||||
cf := strings.Split(s, "/")
|
|
||||||
if len(cf) != 2 {
|
|
||||||
return fmt.Errorf("invalid value of `to` in line %d, expected one `/`: %s", value.Line, s)
|
|
||||||
}
|
|
||||||
t.CollectionField.Collection = cf[0]
|
|
||||||
t.CollectionField.ToField.Name = cf[1]
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := value.Decode(&(t.CollectionField)); err != nil {
|
|
||||||
return fmt.Errorf("decoding to field at line %d: %w", value.Line, err)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// AttributeGenericRelation is a generic-relation or generic-relation-list field.
|
|
||||||
type AttributeGenericRelation struct {
|
|
||||||
To ToGeneric `yaml:"to"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// ToCollections returns all collection, where the generic field could point to.
|
|
||||||
func (r AttributeGenericRelation) ToCollections() []ToCollectionField {
|
|
||||||
return r.To.CollectionFields
|
|
||||||
}
|
|
||||||
|
|
||||||
// AttributeTemplate represents a template field.
|
|
||||||
type AttributeTemplate struct {
|
|
||||||
Replacement string `yaml:"replacement"`
|
|
||||||
Fields Field `yaml:"fields"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// ToGeneric is like a To object, but for generic relations.
|
|
||||||
type ToGeneric struct {
|
|
||||||
CollectionFields []ToCollectionField
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *ToGeneric) UnmarshalYAML(value *yaml.Node) error {
|
|
||||||
var d struct {
|
|
||||||
Collections []string `yaml:"collections"`
|
|
||||||
Field ToField `yaml:"field"`
|
|
||||||
}
|
|
||||||
if err := value.Decode(&d); err == nil {
|
|
||||||
t.CollectionFields = make([]ToCollectionField, len(d.Collections))
|
|
||||||
for i, collection := range d.Collections {
|
|
||||||
t.CollectionFields[i].Collection = collection
|
|
||||||
t.CollectionFields[i].ToField = d.Field
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
var e []string
|
|
||||||
if err := value.Decode(&e); err != nil {
|
|
||||||
return fmt.Errorf("decoding to generic field at line %d: %w", value.Line, err)
|
|
||||||
}
|
|
||||||
t.CollectionFields = make([]ToCollectionField, len(e))
|
|
||||||
for i, collectionfield := range e {
|
|
||||||
cf := strings.Split(collectionfield, "/")
|
|
||||||
if len(cf) != 2 {
|
|
||||||
return fmt.Errorf("invalid value of `to` in line %d, expected one `/`: %s", value.Line, collectionfield)
|
|
||||||
}
|
|
||||||
t.CollectionFields[i].Collection = cf[0]
|
|
||||||
t.CollectionFields[i].ToField.Name = cf[1]
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
Loading…
Reference in New Issue
Block a user