webadmin: make routes configurable: globally, per domain, per account

this simplifies some of the code that makes modifications to the config file. a
few protected functions can make changes to the dynamic config, which webadmin
can use. instead of having separate functions in mox-/admin.go for each type of
change.

this also exports the parsed full dynamic config to webadmin, so we need fewer
functions for specific config fields too.
This commit is contained in:
Mechiel Lukkien
2024-04-18 11:14:24 +02:00
parent baf4df55a6
commit a69887bfab
19 changed files with 1165 additions and 189 deletions

View File

@ -17,8 +17,8 @@ MIT-licensed, see LICENSE.
- major cleanup required. too much parsing is done that can probably be handled by the go/* packages.
- check that all cases of embedding work (seems like we will include duplicates: when a struct has fields that override an embedded struct, we generate duplicate fields).
- check that all cross-package referencing (ast.SelectorExpr) works
- better cli syntax for replacements, and always replace based on fully qualified names. currently you need to specify both the fully qualified and unqualified type paths.
- see if order of items in output depends on a map somewhere, i've seen diffs for generated jsons where a type was only moved, not modified.
- better cli syntax for replacements and renames, and always replace based on fully qualified names. currently you need to specify both the fully qualified and unqualified type paths.
- see if order of items in output depends on a map somewhere, i've seen diffs for generated jsons where a type was only moved, not modified. perhaps the type was discovered earlier/later because of other type changes. we may want to sort sections,methods,types in the output.
- better error messages and error handling, stricter parsing
- support type aliases
- support plain iota enums? currently only simple literals are supported for enums.

View File

@ -56,6 +56,7 @@ import (
var (
packagePath = flag.String("package-path", ".", "of source code to parse")
replace = flag.String("replace", "", "comma-separated list of type replacements, e.g. \"somepkg.SomeType string\"")
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`)
)
@ -140,6 +141,13 @@ func check(err error, action string) {
}
}
type renameSrc struct {
Pkg string // Package selector, not full path at the moment.
Name string
}
var renames = map[renameSrc]string{}
func usage() {
log.Println("usage: sherpadoc [flags] section")
flag.PrintDefaults()
@ -155,6 +163,28 @@ func main() {
usage()
}
if *rename != "" {
to := map[string]bool{} // Track target names, for detecting duplicates.
for _, elem := range strings.Split(*rename, ",") {
l := strings.Split(elem, " ")
if len(l) != 3 {
log.Printf("invalid rename %q", elem)
usage()
}
src := renameSrc{l[0], l[1]}
if _, ok := renames[src]; ok {
log.Printf("duplicate rename %q", elem)
usage()
}
if to[l[2]] {
log.Printf("duplicate rename type %q", l[2])
usage()
}
to[l[2]] = true
renames[src] = l[2]
}
}
// If vendor exists, we load packages from it.
for dir, _ := os.Getwd(); dir != "" && dir != "/"; dir = filepath.Dir(dir) {
p := filepath.Join(dir, "go.mod")

View File

@ -181,15 +181,21 @@ func parseSection(t *doc.Type, pp *parsedPackage) *section {
}
// Ensure type "t" (used in a field or argument) defined in package pp is parsed
// and added to the section.
func ensureNamedType(t *doc.Type, sec *section, pp *parsedPackage) {
typePath := pp.Path + "." + t.Name
// and added to the section. The returned string is the name as used in the
// sherpadoc, it may have been renamed.
func ensureNamedType(t *doc.Type, sec *section, pp *parsedPackage) (name string) {
if s, ok := renames[renameSrc{pp.Pkg.Name, t.Name}]; ok {
name = s
} else {
name = t.Name
}
typePath := pp.Path + "." + name
if _, have := sec.Typeset[typePath]; have {
return
}
tt := &namedType{
Name: t.Name,
Name: name,
Text: strings.TrimSpace(t.Doc),
}
// add it early, so self-referencing types can't cause a loop
@ -360,6 +366,7 @@ func ensureNamedType(t *doc.Type, sec *section, pp *parsedPackage) {
default:
logFatalLinef(pp, t.Decl.TokPos, "unsupported field/param/return type %T", ts.Type)
}
return
}
func hasOmitEmpty(tag *ast.BasicLit) bool {
@ -407,8 +414,8 @@ func gatherFieldType(typeName string, f *field, e ast.Expr, fieldTag *ast.BasicL
case *ast.Ident:
tt := pp.lookupType(t.Name)
if tt != nil {
ensureNamedType(tt, sec, pp)
return []string{t.Name}
name := ensureNamedType(tt, sec, pp)
return []string{name}
}
commaString := isCommaString(fieldTag)
name := t.Name
@ -465,8 +472,8 @@ func parseArgType(e ast.Expr, sec *section, pp *parsedPackage) typewords {
case *ast.Ident:
tt := pp.lookupType(t.Name)
if tt != nil {
ensureNamedType(tt, sec, pp)
return []string{t.Name}
name := ensureNamedType(tt, sec, pp)
return []string{name}
}
name := t.Name
switch name {
@ -560,8 +567,7 @@ func parseSelector(t *ast.SelectorExpr, srcTypeName string, sec *section, pp *pa
if tt == nil {
logFatalLinef(pp, t.Pos(), "could not find type %q in package %q", typeName, importPath)
}
ensureNamedType(tt, sec, opp)
return typeName
return ensureNamedType(tt, sec, opp)
}
type replacement struct {