From 5db145196fe8921249e0f3e6c6cb33298fa6a425 Mon Sep 17 00:00:00 2001 From: Steven Guiheux Date: Wed, 7 Sep 2022 17:53:07 +0200 Subject: [PATCH] feat: add all_of and condition to specify a sub jsonschema as a type --- reflect.go | 27 +++++++++++++++++++++++++++ reflect_test.go | 30 ++++++++++++++++++++++++++++++ 2 files changed, 57 insertions(+) diff --git a/reflect.go b/reflect.go index 36ee30a..4fcafd4 100644 --- a/reflect.go +++ b/reflect.go @@ -651,6 +651,9 @@ func (t *Schema) structKeywordsFromTags(f reflect.StructField, parent *Schema, p } extras := strings.Split(f.Tag.Get("jsonschema_extras"), ",") t.extraKeywords(extras) + + allOfIfs := splitOnUnescapedCommas(f.Tag.Get("jsonschema_allof_type")) + parent.addAllOfConditions(allOfIfs, propertyName) } // read struct tags for generic keyworks @@ -853,6 +856,30 @@ func (t *Schema) arrayKeywords(tags []string) { } } +func (t *Schema) addAllOfConditions(conditions []string, propertyName string) { + if len(t.AllOf) == 0 { + t.AllOf = make([]*Schema, 0) + } + // tag Format: :=/path/my/type + for _, condition := range conditions { + parts := strings.Split(condition, ":") + if len(parts) != 2 { + continue + } + conditionPart := strings.Split(strings.TrimSpace(parts[0]), "=") + if len(conditionPart) != 2 { + continue + } + ruleSchema := Schema{ + If: &Schema{Properties: orderedmap.New()}, + Then: &Schema{Properties: orderedmap.New()}, + } + ruleSchema.If.Properties.Set(conditionPart[0], Schema{Const: conditionPart[1]}) + ruleSchema.Then.Properties.Set(propertyName, Schema{Ref: parts[1]}) + t.AllOf = append(t.AllOf, &ruleSchema) + } +} + func (t *Schema) extraKeywords(tags []string) { for _, tag := range tags { nameValue := strings.Split(tag, "=") diff --git a/reflect_test.go b/reflect_test.go index 72e6922..23c9cdc 100644 --- a/reflect_test.go +++ b/reflect_test.go @@ -498,3 +498,33 @@ func TestArrayFormat(t *testing.T) { pt := p.Items.Format require.Equal(t, pt, "uri") } + +func TestAllOfType(t *testing.T) { + type Application struct { + Name string `json:"name"` + Kind string `json:"kind"` + Specification json.RawMessage `json:"spec" jsonschema_allof_type:"kind=docker:#/$defs/dockerApp, kind=vm:#/$defs/vmApp"` + } + + r := new(Reflector) + appSchema := r.Reflect(&Application{}) + + if appSchema.Definitions == nil { + appSchema.Definitions = make(map[string]*Schema, 0) + } + + require.Len(t, appSchema.Definitions, 1) + require.NotNil(t, appSchema.Definitions["Application"]) + require.Len(t, appSchema.Definitions["Application"].AllOf, 2) + + // Check IF part + allOfDocker := appSchema.Definitions["Application"].AllOf[0] + jsonDocker, _ := allOfDocker.MarshalJSON() + dockerIfMustBe := `{"if":{"properties":{"kind":{"const":"docker"}}},"then":{"properties":{"spec":{"$ref":"#/$defs/dockerApp"}}}}` + require.Equal(t, dockerIfMustBe, string(jsonDocker)) + + allOfVM := appSchema.Definitions["Application"].AllOf[1] + jsonVM, _ := allOfVM.MarshalJSON() + vmIfMustBe := `{"if":{"properties":{"kind":{"const":"vm"}}},"then":{"properties":{"spec":{"$ref":"#/$defs/vmApp"}}}}` + require.Equal(t, vmIfMustBe, string(jsonVM)) +}