-
Notifications
You must be signed in to change notification settings - Fork 411
/
endpoints.go
169 lines (146 loc) · 4.29 KB
/
endpoints.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
// Package docs can be used to gather go-ipfs commands and automatically
// generate documentation or tests.
package docs
import (
"fmt"
"sort"
jsondoc "github.com/Stebalien/go-json-doc"
cid "github.com/ipfs/go-cid"
cmds "github.com/ipfs/go-ipfs-cmds"
config "github.com/ipfs/kubo"
corecmds "github.com/ipfs/kubo/core/commands"
peer "github.com/libp2p/go-libp2p-core/peer"
multiaddr "github.com/multiformats/go-multiaddr"
)
var JsondocGlossary = jsondoc.NewGlossary().
WithSchema(new(cid.Cid), jsondoc.Object{"/": "<cid-string>"}).
WithName(new(multiaddr.Multiaddr), "multiaddr-string").
WithName(new(peer.ID), "peer-id").
WithSchema(new(peer.AddrInfo),
jsondoc.Object{"ID": "peer-id", "Addrs": []string{"<multiaddr-string>"}})
var ignoreOptsPerEndpoint = map[string]map[string]struct{}{
"/api/v0/add": {
cmds.RecLong: struct{}{},
cmds.DerefLong: struct{}{},
cmds.StdinName: struct{}{},
cmds.Hidden: struct{}{},
cmds.Ignore: struct{}{},
cmds.IgnoreRules: struct{}{},
},
}
// A map of single endpoints to be skipped (subcommands are processed though).
var IgnoreEndpoints = map[string]bool{}
// How much to indent when generating the response schemas
const IndentLevel = 4
// Failsafe when traversing objects containing objects of the same type
const MaxIndent = 20
// Endpoint defines an IPFS RPC API endpoint.
type Endpoint struct {
Name string
Status cmds.Status
Arguments []*Argument
Options []*Argument
Description string
Response string
Group string
}
// Argument defines an IPFS RPC API endpoint argument.
type Argument struct {
Endpoint string
Name string
Description string
Type string
Required bool
Default string
}
type sorter []*Endpoint
func (a sorter) Len() int { return len(a) }
func (a sorter) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
func (a sorter) Less(i, j int) bool { return a[i].Name < a[j].Name }
const APIPrefix = "/api/v0"
// AllEndpoints gathers all the endpoints from go-ipfs.
func AllEndpoints() []*Endpoint {
return Endpoints(APIPrefix, corecmds.Root)
}
func InStatus(endpoints []*Endpoint, status cmds.Status) []*Endpoint {
var results []*Endpoint
for _, endpoint := range endpoints {
if endpoint.Status == status {
results = append(results, endpoint)
}
}
return results
}
func IPFSVersion() string {
return config.CurrentVersionNumber
}
// Endpoints receives a name and a go-ipfs command and returns the endpoints it
// defines] (sorted). It does this by recursively gathering endpoints defined by
// subcommands. Thus, calling it with the core command Root generates all
// the endpoints.
func Endpoints(name string, cmd *cmds.Command) (endpoints []*Endpoint) {
var arguments []*Argument
var options []*Argument
ignore := cmd.Run == nil || IgnoreEndpoints[name]
if !ignore { // Extract arguments, options...
for _, arg := range cmd.Arguments {
argType := "string"
if arg.Type == cmds.ArgFile {
argType = "file"
}
arguments = append(arguments, &Argument{
Endpoint: name,
Name: arg.Name,
Type: argType,
Required: arg.Required,
Description: arg.Description,
})
}
for _, opt := range cmd.Options {
if ignoreOpts, ok := ignoreOptsPerEndpoint[name]; ok {
if _, ok := ignoreOpts[opt.Names()[0]]; ok {
// skip this option for this endpoint.
continue
}
}
def := fmt.Sprint(opt.Default())
if def == "<nil>" {
def = ""
}
options = append(options, &Argument{
Name: opt.Names()[0],
Type: opt.Type().String(),
Description: opt.Description(),
Default: def,
})
}
res := buildResponse(cmd.Type)
endpoints = []*Endpoint{
{
Name: name,
Status: cmd.Status,
Description: cmd.Helptext.Tagline,
Arguments: arguments,
Options: options,
Response: res,
},
}
}
for n, cmd := range cmd.Subcommands {
endpoints = append(endpoints,
Endpoints(fmt.Sprintf("%s/%s", name, n), cmd)...)
}
sort.Sort(sorter(endpoints))
return endpoints
}
func buildResponse(res interface{}) string {
// Commands with a nil type return text. This is a bad thing.
if res == nil {
return "This endpoint returns a `text/plain` response body."
}
desc, err := JsondocGlossary.Describe(res)
if err != nil {
panic(err)
}
return desc
}