Merge pull request #868 from stevvooe/ng-api-specification

[WIP] Automatically generate V2 API specification
This commit is contained in:
Olivier Gambier 2014-12-23 15:14:39 -08:00
commit 6612be9acb
9 changed files with 3347 additions and 141 deletions

View 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
}

View file

@ -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")
}
}