mirror of
				https://github.com/woodpecker-ci/woodpecker.git
				synced 2025-10-31 08:41:51 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			237 lines
		
	
	
		
			5.3 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			237 lines
		
	
	
		
			5.3 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| // +build ignore
 | |
| 
 | |
| // This program generates api documentation from a
 | |
| // swaggerfile using an amber template.
 | |
| 
 | |
| package main
 | |
| 
 | |
| import (
 | |
| 	"crypto/md5"
 | |
| 	"flag"
 | |
| 	"fmt"
 | |
| 	"io"
 | |
| 	"log"
 | |
| 	"os"
 | |
| 	"sort"
 | |
| 
 | |
| 	"github.com/eknkc/amber"
 | |
| 	"github.com/go-swagger/go-swagger/spec"
 | |
| )
 | |
| 
 | |
| var (
 | |
| 	templ  = flag.String("template", "index.amber", "")
 | |
| 	input  = flag.String("input", "swagger.json", "")
 | |
| 	output = flag.String("output", "", "")
 | |
| )
 | |
| 
 | |
| func main() {
 | |
| 	flag.Parse()
 | |
| 
 | |
| 	// parses the swagger spec file
 | |
| 	spec, err := spec.YAMLSpec(*input)
 | |
| 	if err != nil {
 | |
| 		log.Fatal(err)
 | |
| 	}
 | |
| 	swag := spec.Spec()
 | |
| 
 | |
| 	// create output source for file. defaults to
 | |
| 	// stdout but may be file.
 | |
| 	var w io.WriteCloser = os.Stdout
 | |
| 	if *output != "" {
 | |
| 		w, err = os.Create(*output)
 | |
| 		if err != nil {
 | |
| 			fmt.Fprintf(os.Stderr, "%v\n", err)
 | |
| 			return
 | |
| 		}
 | |
| 		defer w.Close()
 | |
| 	}
 | |
| 
 | |
| 	// we wrap the swagger file in a map, otherwise it
 | |
| 	// won't work with our existing templates, which expect
 | |
| 	// a map as the root parameter.
 | |
| 	var data = map[string]interface{}{
 | |
| 		"Swagger": normalize(swag),
 | |
| 	}
 | |
| 
 | |
| 	t := amber.MustCompileFile(*templ, amber.DefaultOptions)
 | |
| 	err = t.Execute(w, data)
 | |
| 	if err != nil {
 | |
| 		log.Fatal(err)
 | |
| 	}
 | |
| }
 | |
| 
 | |
| // Swagger is a simplified representation of the swagger
 | |
| // document with a subset of the fields used to generate
 | |
| // our API documentation.
 | |
| type Swagger struct {
 | |
| 	Tags []Tag
 | |
| }
 | |
| 
 | |
| type Tag struct {
 | |
| 	Name string
 | |
| 	Ops  []Operation
 | |
| }
 | |
| 
 | |
| type Operation struct {
 | |
| 	ID      string
 | |
| 	Method  string
 | |
| 	Path    string
 | |
| 	Desc    string
 | |
| 	Summary string
 | |
| 
 | |
| 	Params  []Param
 | |
| 	Results []Result
 | |
| }
 | |
| 
 | |
| type Param struct {
 | |
| 	Name     string
 | |
| 	Desc     string
 | |
| 	Type     string
 | |
| 	Example  interface{}
 | |
| 	InputTo  string
 | |
| 	IsObject bool
 | |
| }
 | |
| 
 | |
| type Result struct {
 | |
| 	Status   int
 | |
| 	Desc     string
 | |
| 	Example  interface{}
 | |
| 	IsObject bool
 | |
| 	IsArray  bool
 | |
| }
 | |
| 
 | |
| // normalize is a helper function that normalizes the swagger
 | |
| // file to a simpler format that makes it easier to work with
 | |
| // inside the template.
 | |
| func normalize(swag *spec.Swagger) Swagger {
 | |
| 	swag_ := Swagger{}
 | |
| 
 | |
| 	for _, tag := range swag.Tags {
 | |
| 		tag_ := Tag{Name: tag.Name}
 | |
| 
 | |
| 		// group the paths based on their tag value.
 | |
| 		for route, path := range swag.Paths.Paths {
 | |
| 
 | |
| 			var ops = []*spec.Operation{
 | |
| 				path.Get,
 | |
| 				path.Put,
 | |
| 				path.Post,
 | |
| 				path.Patch,
 | |
| 				path.Delete,
 | |
| 			}
 | |
| 
 | |
| 			// flatten the operations into an array and convert
 | |
| 			// the underlying data so that it is a bit easier to
 | |
| 			// work with.
 | |
| 			for _, op := range ops {
 | |
| 
 | |
| 				// the operation must have a tag to
 | |
| 				// be rendered in our custom template.
 | |
| 				if op == nil || !hasTag(tag.Name, op.Tags) {
 | |
| 					continue
 | |
| 				}
 | |
| 
 | |
| 				item := Operation{}
 | |
| 				item.Path = route
 | |
| 				item.Method = getMethod(op, path)
 | |
| 				item.Desc = op.Description
 | |
| 				item.Summary = op.Summary
 | |
| 				item.ID = fmt.Sprintf("%x", md5.Sum([]byte(item.Path+item.Method)))
 | |
| 
 | |
| 				// convert the operation input parameters into
 | |
| 				// our internal format so that it is easier to
 | |
| 				// work with in the template.
 | |
| 				for _, param := range op.Parameters {
 | |
| 					param_ := Param{}
 | |
| 					param_.Name = param.Name
 | |
| 					param_.Desc = param.Description
 | |
| 					param_.Type = param.Type
 | |
| 					param_.IsObject = param.Schema != nil
 | |
| 					param_.InputTo = param.In
 | |
| 
 | |
| 					if param_.IsObject {
 | |
| 						param_.Type = param.Schema.Ref.GetPointer().String()[13:]
 | |
| 						param_.Example = param.Schema.Example
 | |
| 					}
 | |
| 					item.Params = append(item.Params, param_)
 | |
| 				}
 | |
| 
 | |
| 				// convert the operation response types into
 | |
| 				// our internal format so that it is easier to
 | |
| 				// work with in the template.
 | |
| 				for code, resp := range op.Responses.StatusCodeResponses {
 | |
| 					result := Result{}
 | |
| 					result.Desc = resp.Description
 | |
| 					result.Status = code
 | |
| 					result.IsObject = resp.Schema != nil
 | |
| 					if result.IsObject {
 | |
| 						result.IsArray = resp.Schema.Items != nil
 | |
| 
 | |
| 						name := resp.Schema.Ref.GetPointer().String()
 | |
| 						if len(name) != 0 {
 | |
| 							def, _ := swag.Definitions[name[13:]]
 | |
| 							result.Example = def.Example
 | |
| 						}
 | |
| 					}
 | |
| 					if result.IsArray {
 | |
| 						name := resp.Schema.Items.Schema.Ref.GetPointer().String()
 | |
| 						def, _ := swag.Definitions[name[13:]]
 | |
| 						result.Example = def.Example
 | |
| 					}
 | |
| 					item.Results = append(item.Results, result)
 | |
| 				}
 | |
| 				sort.Sort(ByCode(item.Results))
 | |
| 				tag_.Ops = append(tag_.Ops, item)
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		sort.Sort(ByPath(tag_.Ops))
 | |
| 		swag_.Tags = append(swag_.Tags, tag_)
 | |
| 	}
 | |
| 
 | |
| 	return swag_
 | |
| }
 | |
| 
 | |
| // hasTag is a helper function that returns true if
 | |
| // an operation has the specified tag label.
 | |
| func hasTag(want string, in []string) bool {
 | |
| 	for _, got := range in {
 | |
| 		if got == want {
 | |
| 			return true
 | |
| 		}
 | |
| 	}
 | |
| 	return false
 | |
| }
 | |
| 
 | |
| // getMethod is a helper function that returns the http
 | |
| // method for the specified operation in a path.
 | |
| func getMethod(op *spec.Operation, path spec.PathItem) string {
 | |
| 	switch {
 | |
| 	case op == path.Get:
 | |
| 		return "GET"
 | |
| 	case op == path.Put:
 | |
| 		return "PUT"
 | |
| 	case op == path.Patch:
 | |
| 		return "PATCH"
 | |
| 	case op == path.Post:
 | |
| 		return "POST"
 | |
| 	case op == path.Delete:
 | |
| 		return "DELETE"
 | |
| 	}
 | |
| 	return ""
 | |
| }
 | |
| 
 | |
| // ByCode helps sort a list of results by status code
 | |
| type ByCode []Result
 | |
| 
 | |
| func (a ByCode) Len() int           { return len(a) }
 | |
| func (a ByCode) Swap(i, j int)      { a[i], a[j] = a[j], a[i] }
 | |
| func (a ByCode) Less(i, j int) bool { return a[i].Status < a[j].Status }
 | |
| 
 | |
| // ByPath helps sort a list of endpoints by path
 | |
| type ByPath []Operation
 | |
| 
 | |
| func (a ByPath) Len() int           { return len(a) }
 | |
| func (a ByPath) Swap(i, j int)      { a[i], a[j] = a[j], a[i] }
 | |
| func (a ByPath) Less(i, j int) bool { return a[i].Path < a[j].Path }
 |