Merge pull request #868 from stevvooe/ng-api-specification
[WIP] Automatically generate V2 API specification
This commit is contained in:
commit
6612be9acb
9 changed files with 3347 additions and 141 deletions
118
cmd/registry-api-descriptor-template/main.go
Normal file
118
cmd/registry-api-descriptor-template/main.go
Normal file
|
@ -0,0 +1,118 @@
|
|||
// registry-api-descriptor-template uses the APIDescriptor defined in the
|
||||
// api/v2 package to execute templates passed to the command line.
|
||||
//
|
||||
// For example, to generate a new API specification, one would execute the
|
||||
// following command from the repo root:
|
||||
//
|
||||
// $ registry-api-descriptor-template doc/SPEC.md.tmpl > doc/SPEC.md
|
||||
//
|
||||
// The templates are passed in the api/v2.APIDescriptor object. Please see the
|
||||
// package documentation for fields available on that object. The template
|
||||
// syntax is from Go's standard library text/template package. For information
|
||||
// on Go's template syntax, please see golang.org/pkg/text/template.
|
||||
package main
|
||||
|
||||
import (
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"text/template"
|
||||
|
||||
"github.com/docker/docker-registry/api/v2"
|
||||
)
|
||||
|
||||
var spaceRegex = regexp.MustCompile(`\n\s*`)
|
||||
|
||||
func main() {
|
||||
|
||||
if len(os.Args) != 2 {
|
||||
log.Fatalln("please specify a template to execute.")
|
||||
}
|
||||
|
||||
path := os.Args[1]
|
||||
filename := filepath.Base(path)
|
||||
|
||||
funcMap := template.FuncMap{
|
||||
"removenewlines": func(s string) string {
|
||||
return spaceRegex.ReplaceAllString(s, " ")
|
||||
},
|
||||
"statustext": http.StatusText,
|
||||
"prettygorilla": prettyGorillaMuxPath,
|
||||
}
|
||||
|
||||
tmpl := template.Must(template.New(filename).Funcs(funcMap).ParseFiles(path))
|
||||
|
||||
if err := tmpl.Execute(os.Stdout, v2.APIDescriptor); err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
}
|
||||
|
||||
// prettyGorillaMuxPath removes the regular expressions from a gorilla/mux
|
||||
// route string, making it suitable for documentation.
|
||||
func prettyGorillaMuxPath(s string) string {
|
||||
// Stateful parser that removes regular expressions from gorilla
|
||||
// routes. It correctly handles balanced bracket pairs.
|
||||
|
||||
var output string
|
||||
var label string
|
||||
var level int
|
||||
|
||||
start:
|
||||
if s[0] == '{' {
|
||||
s = s[1:]
|
||||
level++
|
||||
goto capture
|
||||
}
|
||||
|
||||
output += string(s[0])
|
||||
s = s[1:]
|
||||
|
||||
goto end
|
||||
capture:
|
||||
switch s[0] {
|
||||
case '{':
|
||||
level++
|
||||
case '}':
|
||||
level--
|
||||
|
||||
if level == 0 {
|
||||
s = s[1:]
|
||||
goto label
|
||||
}
|
||||
case ':':
|
||||
s = s[1:]
|
||||
goto skip
|
||||
default:
|
||||
label += string(s[0])
|
||||
}
|
||||
s = s[1:]
|
||||
goto capture
|
||||
skip:
|
||||
switch s[0] {
|
||||
case '{':
|
||||
level++
|
||||
case '}':
|
||||
level--
|
||||
}
|
||||
s = s[1:]
|
||||
|
||||
if level == 0 {
|
||||
goto label
|
||||
}
|
||||
|
||||
goto skip
|
||||
label:
|
||||
if label != "" {
|
||||
output += "<" + label + ">"
|
||||
label = ""
|
||||
}
|
||||
end:
|
||||
if s != "" {
|
||||
goto start
|
||||
}
|
||||
|
||||
return output
|
||||
|
||||
}
|
|
@ -1,95 +0,0 @@
|
|||
// registry-api-doctable-gen uses various descriptors within the registry code
|
||||
// base to generate markdown tables for use in documentation. This is only
|
||||
// meant to facilitate updates to documentation and not as an automated tool.
|
||||
//
|
||||
// For now, this only includes support for error codes:
|
||||
//
|
||||
// $ registry-api-doctable-gen errors
|
||||
//
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"os"
|
||||
"reflect"
|
||||
"strings"
|
||||
"text/tabwriter"
|
||||
|
||||
"github.com/docker/docker-registry/api/v2"
|
||||
)
|
||||
|
||||
func main() {
|
||||
|
||||
if len(os.Args) < 2 {
|
||||
log.Fatalln("please specify a table to generate: (errors)")
|
||||
}
|
||||
|
||||
switch os.Args[1] {
|
||||
case "errors":
|
||||
dumpErrors(os.Stdout)
|
||||
default:
|
||||
log.Fatalln("unknown descriptor table:", os.Args[1])
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func dumpErrors(wr io.Writer) {
|
||||
writer := tabwriter.NewWriter(os.Stdout, 8, 8, 0, '\t', 0)
|
||||
defer writer.Flush()
|
||||
|
||||
fmt.Fprint(writer, "|")
|
||||
dtype := reflect.TypeOf(v2.ErrorDescriptor{})
|
||||
var fieldsPrinted int
|
||||
for i := 0; i < dtype.NumField(); i++ {
|
||||
field := dtype.Field(i)
|
||||
if field.Name == "Value" {
|
||||
continue
|
||||
}
|
||||
|
||||
fmt.Fprint(writer, field.Name, "|")
|
||||
fieldsPrinted++
|
||||
}
|
||||
|
||||
divider := strings.Repeat("-", 8)
|
||||
var parts []string
|
||||
for i := 0; i < fieldsPrinted; i++ {
|
||||
parts = append(parts, divider)
|
||||
}
|
||||
divider = strings.Join(parts, "|")
|
||||
|
||||
fmt.Fprintln(writer, "\n"+divider)
|
||||
|
||||
for _, descriptor := range v2.ErrorDescriptors {
|
||||
fmt.Fprint(writer, "|")
|
||||
|
||||
v := reflect.ValueOf(descriptor)
|
||||
for i := 0; i < dtype.NumField(); i++ {
|
||||
value := v.Field(i).Interface()
|
||||
field := v.Type().Field(i)
|
||||
if field.Name == "Value" {
|
||||
continue
|
||||
} else if field.Name == "Description" {
|
||||
value = strings.Replace(value.(string), "\n", " ", -1)
|
||||
} else if field.Name == "Code" {
|
||||
value = fmt.Sprintf("`%s`", value)
|
||||
} else if field.Name == "HTTPStatusCodes" {
|
||||
if len(value.([]int)) > 0 {
|
||||
var codes []string
|
||||
for _, code := range value.([]int) {
|
||||
codes = append(codes, fmt.Sprint(code))
|
||||
}
|
||||
value = strings.Join(codes, ", ")
|
||||
} else {
|
||||
value = "Any"
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
fmt.Fprint(writer, value, "|")
|
||||
}
|
||||
|
||||
fmt.Fprint(writer, "\n")
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue