forked from hashicorp/vault
-
Notifications
You must be signed in to change notification settings - Fork 0
/
path.go
160 lines (137 loc) · 4.72 KB
/
path.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
package framework
import (
"fmt"
"sort"
"strings"
"github.com/hashicorp/vault/logical"
)
// Helper which returns a generic regex string for creating endpoint patterns
// that are identified by the given name in the backends
func GenericNameRegex(name string) string {
return fmt.Sprintf("(?P<%s>\\w(([\\w-.]+)?\\w)?)", name)
}
// Helper which returns a regex string for optionally accepting the a field
// from the API URL
func OptionalParamRegex(name string) string {
return fmt.Sprintf("(/(?P<%s>.+))?", name)
}
// PathAppend is a helper for appending lists of paths into a single
// list.
func PathAppend(paths ...[]*Path) []*Path {
result := make([]*Path, 0, 10)
for _, ps := range paths {
result = append(result, ps...)
}
return result
}
// Path is a single path that the backend responds to.
type Path struct {
// Pattern is the pattern of the URL that matches this path.
//
// This should be a valid regular expression. Named captures will be
// exposed as fields that should map to a schema in Fields. If a named
// capture is not a field in the Fields map, then it will be ignored.
Pattern string
// Fields is the mapping of data fields to a schema describing that
// field. Named captures in the Pattern also map to fields. If a named
// capture name matches a PUT body name, the named capture takes
// priority.
//
// Note that only named capture fields are available in every operation,
// whereas all fields are available in the Write operation.
Fields map[string]*FieldSchema
// Callbacks are the set of callbacks that are called for a given
// operation. If a callback for a specific operation is not present,
// then logical.ErrUnsupportedOperation is automatically generated.
//
// The help operation is the only operation that the Path will
// automatically handle if the Help field is set. If both the Help
// field is set and there is a callback registered here, then the
// callback will be called.
Callbacks map[logical.Operation]OperationFunc
// ExistenceCheck, if implemented, is used to query whether a given
// resource exists or not. This is used for ACL purposes: if an Update
// action is specified, and the existence check returns false, the action
// is not allowed since the resource must first be created. The reverse is
// also true. If not specified, the Update action is forced and the user
// must have UpdateCapability on the path.
ExistenceCheck func(*logical.Request, *FieldData) (bool, error)
// Help is text describing how to use this path. This will be used
// to auto-generate the help operation. The Path will automatically
// generate a parameter listing and URL structure based on the
// regular expression, so the help text should just contain a description
// of what happens.
//
// HelpSynopsis is a one-sentence description of the path. This will
// be automatically line-wrapped at 80 characters.
//
// HelpDescription is a long-form description of the path. This will
// be automatically line-wrapped at 80 characters.
HelpSynopsis string
HelpDescription string
}
func (p *Path) helpCallback(
req *logical.Request, data *FieldData) (*logical.Response, error) {
var tplData pathTemplateData
tplData.Request = req.Path
tplData.RoutePattern = p.Pattern
tplData.Synopsis = strings.TrimSpace(p.HelpSynopsis)
if tplData.Synopsis == "" {
tplData.Synopsis = "<no synopsis>"
}
tplData.Description = strings.TrimSpace(p.HelpDescription)
if tplData.Description == "" {
tplData.Description = "<no description>"
}
// Alphabetize the fields
fieldKeys := make([]string, 0, len(p.Fields))
for k, _ := range p.Fields {
fieldKeys = append(fieldKeys, k)
}
sort.Strings(fieldKeys)
// Build the field help
tplData.Fields = make([]pathTemplateFieldData, len(fieldKeys))
for i, k := range fieldKeys {
schema := p.Fields[k]
description := strings.TrimSpace(schema.Description)
if description == "" {
description = "<no description>"
}
tplData.Fields[i] = pathTemplateFieldData{
Key: k,
Type: schema.Type.String(),
Description: description,
}
}
help, err := executeTemplate(pathHelpTemplate, &tplData)
if err != nil {
return nil, fmt.Errorf("error executing template: %s", err)
}
return logical.HelpResponse(help, nil), nil
}
type pathTemplateData struct {
Request string
RoutePattern string
Synopsis string
Description string
Fields []pathTemplateFieldData
}
type pathTemplateFieldData struct {
Key string
Type string
Description string
URL bool
}
const pathHelpTemplate = `
Request: {{.Request}}
Matching Route: {{.RoutePattern}}
{{.Synopsis}}
{{ if .Fields -}}
## PARAMETERS
{{range .Fields}}
{{indent 4 .Key}} ({{.Type}})
{{indent 8 .Description}}
{{end}}{{end}}
## DESCRIPTION
{{.Description}}
`