webadmin: don't show runtime typecheck error for invalid values in dmarc and tls reports

several fields in dmarc and tls reports have known string values. we have a Go
string type for them. sherpats (through sherpadoc) turns those strings into
typescript enums, and sherpats generates runtime-typechecking code (to enforce
correct types for incoming json, to prevent failing deeper in the code when we
get invalid data (much harder to debug)). the Go not-really-enum types allow
other values, and real-world reports have unknown/unspecified/invalid values.
this uses the sherpadoc -rename flag to turn those enums into regular untyped
strings, so sherpats doesn't generate enum-enforcing runtime type checking
code.

this required an update to sherpadoc, to properly handling renaming a type to a
basic type instead of another named type.

for issue #161 by RobSlgm, thanks for reporting!
This commit is contained in:
Mechiel Lukkien
2024-05-09 15:58:14 +02:00
parent 44a6927379
commit a2c9cfc55b
11 changed files with 117 additions and 583 deletions

View File

@ -4,6 +4,15 @@ import (
"fmt"
)
// IsBasicType returns whether name is a basic type, like int32, string, any, timestamp, etc.
func IsBasicType(name string) bool {
switch name {
case "any", "bool", "int8", "uint8", "int16", "uint16", "int32", "uint32", "int64", "uint64", "int64s", "uint64s", "float32", "float64", "string", "timestamp":
return true
}
return false
}
type genError struct{ error }
func parseError(path string, format string, args ...interface{}) {
@ -88,6 +97,13 @@ func (c checker) checkTypewords(path string, tokens []string, okNullable bool) {
}
t := tokens[0]
tokens = tokens[1:]
if IsBasicType(t) {
if len(tokens) != 0 {
parseError(path, "leftover typewords %v", tokens)
}
return
}
switch t {
case "nullable":
if !okNullable {
@ -97,10 +113,6 @@ func (c checker) checkTypewords(path string, tokens []string, okNullable bool) {
parseError(path, "missing typeword after %#v", t)
}
c.checkTypewords(path, tokens, false)
case "any", "bool", "int8", "uint8", "int16", "uint16", "int32", "uint32", "int64", "uint64", "int64s", "uint64s", "float32", "float64", "string", "timestamp":
if len(tokens) != 0 {
parseError(path, "leftover typewords %v", tokens)
}
case "[]", "{}":
if len(tokens) == 0 {
parseError(path, "missing typeword after %#v", t)

View File

@ -46,6 +46,7 @@ import (
"log"
"os"
"path/filepath"
"sort"
"strings"
"github.com/mjl-/sherpadoc"
@ -59,6 +60,8 @@ var (
rename = flag.String("rename", "", "comma-separated list of type renames as used with a package selector, e.g. \"somepkg SomeName OtherName\"")
title = flag.String("title", "", "title of the API, default is the name of the type of the main API")
adjustFunctionNames = flag.String("adjust-function-names", "", `by default, the first character of function names is turned into lower case; with "lowerWord" the first string of upper case characters is lower cased, with "none" the name is left as is`)
sortfuncs = flag.Bool("sort-funcs", false, "sort functions within section by name")
sorttypes = flag.Bool("sort-types", false, "sort types within section by name")
)
// If there is a "vendor" directory, we'll load packages from there (instead of
@ -176,11 +179,13 @@ func main() {
log.Printf("duplicate rename %q", elem)
usage()
}
if to[l[2]] {
log.Printf("duplicate rename type %q", l[2])
usage()
if !sherpadoc.IsBasicType(l[2]) {
if to[l[2]] {
log.Printf("duplicate rename type %q", l[2])
usage()
}
to[l[2]] = true
}
to[l[2]] = true
renames[src] = l[2]
}
}
@ -223,9 +228,33 @@ func main() {
err := sherpadoc.Check(doc)
check(err, "checking sherpadoc output before writing")
sortFuncs(doc)
writeJSON(doc)
}
func sortFuncs(s *sherpadoc.Section) {
if *sortfuncs {
sort.Slice(s.Functions, func(i, j int) bool {
return s.Functions[i].Name < s.Functions[j].Name
})
}
if *sorttypes {
sort.Slice(s.Structs, func(i, j int) bool {
return s.Structs[i].Name < s.Structs[j].Name
})
sort.Slice(s.Ints, func(i, j int) bool {
return s.Ints[i].Name < s.Ints[j].Name
})
sort.Slice(s.Strings, func(i, j int) bool {
return s.Strings[i].Name < s.Strings[j].Name
})
}
for _, ss := range s.Sections {
sortFuncs(ss)
}
}
func writeJSON(v interface{}) {
buf, err := json.MarshalIndent(v, "", "\t")
check(err, "marshal to json")

View File

@ -186,6 +186,9 @@ func parseSection(t *doc.Type, pp *parsedPackage) *section {
func ensureNamedType(t *doc.Type, sec *section, pp *parsedPackage) (name string) {
if s, ok := renames[renameSrc{pp.Pkg.Name, t.Name}]; ok {
name = s
if sherpadoc.IsBasicType(s) {
return
}
} else {
name = t.Name
}