schema: support generic tags.

This commit is contained in:
fiatjaf
2025-11-23 13:03:16 -03:00
parent b9c9528fa5
commit 0b239c9fe0

View File

@@ -42,7 +42,10 @@ func fetchSchemaFromURL(schemaURL string) (string, error) {
return string(body), nil return string(body), nil
} }
type Schema map[string]KindSchema type Schema struct {
GenericTags map[string]nextSpec `yaml:"generic_tags"`
Kinds map[string]KindSchema `yaml:"kinds"`
}
type KindSchema struct { type KindSchema struct {
Content string `yaml:"content"` Content string `yaml:"content"`
@@ -73,7 +76,10 @@ type Validator struct {
} }
func NewValidatorFromBytes(schemaData []byte) (Validator, error) { func NewValidatorFromBytes(schemaData []byte) (Validator, error) {
schema := make(Schema) schema := Schema{
GenericTags: make(map[string]nextSpec),
Kinds: make(map[string]KindSchema),
}
if err := yaml.Unmarshal(schemaData, &schema); err != nil { if err := yaml.Unmarshal(schemaData, &schema); err != nil {
return Validator{}, fmt.Errorf("failed to parse schema: %w", err) return Validator{}, fmt.Errorf("failed to parse schema: %w", err)
} }
@@ -210,7 +216,7 @@ func (v *Validator) ValidateEvent(evt nostr.Event) error {
return ContentError{ErrDanglingSpace} return ContentError{ErrDanglingSpace}
} }
if sch, ok := v.Schema[strconv.FormatUint(uint64(evt.Kind), 10)]; ok { if sch, ok := v.Schema.Kinds[strconv.FormatUint(uint64(evt.Kind), 10)]; ok {
switch sch.Content { switch sch.Content {
case "json": case "json":
if !json.Valid(unsafe.Slice(unsafe.StringData(evt.Content), len(evt.Content))) { if !json.Valid(unsafe.Slice(unsafe.StringData(evt.Content), len(evt.Content))) {
@@ -233,10 +239,12 @@ func (v *Validator) ValidateEvent(evt nostr.Event) error {
} }
var lastErr error var lastErr error
var tagWasValidated bool
specs: specs:
for _, tagspec := range sch.Tags { for _, tagspec := range sch.Tags {
if tagspec.Name == tag[0] || (tagspec.Prefix != "" && strings.HasPrefix(tag[0], tagspec.Prefix)) { if tagspec.Name == tag[0] || (tagspec.Prefix != "" && strings.HasPrefix(tag[0], tagspec.Prefix)) {
if tagspec.Next != nil { if tagspec.Next != nil {
tagWasValidated = true
if ii, err := v.validateNext(tag, 1, tagspec.Next); err != nil { if ii, err := v.validateNext(tag, 1, tagspec.Next); err != nil {
lastErr = TagError{ti, ii, err} lastErr = TagError{ti, ii, err}
continue specs // see if there is another tagspec that matches this continue specs // see if there is another tagspec that matches this
@@ -253,6 +261,15 @@ func (v *Validator) ValidateEvent(evt nostr.Event) error {
// there was at least one failure for this tag and no further successes // there was at least one failure for this tag and no further successes
return lastErr return lastErr
} }
// when we don't find a specific tag validator for this kind, try a generic one
if !tagWasValidated {
if tagSpecNext, ok := v.Schema.GenericTags[tag[0]]; ok {
if ii, err := v.validateNext(tag, 1, &tagSpecNext); err != nil {
lastErr = TagError{ti, ii, err}
}
}
}
} }
} }
@@ -279,7 +296,7 @@ func (v *Validator) findUnknownTypes(schema Schema) []string {
var unknown []string var unknown []string
visitedTypes := make([]string, 0, 10) visitedTypes := make([]string, 0, 10)
for _, kindSchema := range schema { for _, kindSchema := range schema.Kinds {
for _, tagSpec := range kindSchema.Tags { for _, tagSpec := range kindSchema.Tags {
collectTypes(tagSpec.Next, visitedTypes, func(typeName string) { collectTypes(tagSpec.Next, visitedTypes, func(typeName string) {
if _, ok := v.TypeValidators[typeName]; !ok { if _, ok := v.TypeValidators[typeName]; !ok {
@@ -288,6 +305,13 @@ func (v *Validator) findUnknownTypes(schema Schema) []string {
}) })
} }
} }
for _, tagSpec := range schema.GenericTags {
collectTypes(tagSpec.Next, visitedTypes, func(typeName string) {
if _, ok := v.TypeValidators[typeName]; !ok {
unknown = append(unknown, typeName)
}
})
}
return unknown return unknown
} }