mirror of
https://github.com/optim-enterprises-bv/vault.git
synced 2025-11-01 11:08:10 +00:00
Create sdk/ and api/ submodules (#6583)
This commit is contained in:
278
sdk/framework/path.go
Normal file
278
sdk/framework/path.go
Normal file
@@ -0,0 +1,278 @@
|
||||
package framework
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"github.com/hashicorp/errwrap"
|
||||
"github.com/hashicorp/vault/sdk/helper/license"
|
||||
"github.com/hashicorp/vault/sdk/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)
|
||||
}
|
||||
|
||||
// GenericNameWithAtRegex returns a generic regex that allows alphanumeric
|
||||
// characters along with -, . and @.
|
||||
func GenericNameWithAtRegex(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)
|
||||
}
|
||||
|
||||
// Helper which returns a regex string for capturing an entire endpoint path
|
||||
// as the given name.
|
||||
func MatchAllRegex(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
|
||||
|
||||
// Operations is the set of operations supported and the associated OperationsHandler.
|
||||
//
|
||||
// If both Create and Update operations are present, documentation and examples from
|
||||
// the Update definition will be used. Similarly if both Read and List are present,
|
||||
// Read will be used for documentation.
|
||||
Operations map[logical.Operation]OperationHandler
|
||||
|
||||
// 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.
|
||||
//
|
||||
// Deprecated: Operations should be used instead and will take priority if present.
|
||||
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 ExistenceFunc
|
||||
|
||||
// FeatureRequired, if implemented, will validate if the given feature is
|
||||
// enabled for the set of paths
|
||||
FeatureRequired license.Features
|
||||
|
||||
// Deprecated denotes that this path is considered deprecated. This may
|
||||
// be reflected in help and documentation.
|
||||
Deprecated bool
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
// OperationHandler defines and describes a specific operation handler.
|
||||
type OperationHandler interface {
|
||||
Handler() OperationFunc
|
||||
Properties() OperationProperties
|
||||
}
|
||||
|
||||
// OperationProperties describes an operation for documentation, help text,
|
||||
// and other clients. A Summary should always be provided, whereas other
|
||||
// fields can be populated as needed.
|
||||
type OperationProperties struct {
|
||||
// Summary is a brief (usually one line) description of the operation.
|
||||
Summary string
|
||||
|
||||
// Description is extended documentation of the operation and may contain
|
||||
// Markdown-formatted text markup.
|
||||
Description string
|
||||
|
||||
// Examples provides samples of the expected request data. The most
|
||||
// relevant example should be first in the list, as it will be shown in
|
||||
// documentation that supports only a single example.
|
||||
Examples []RequestExample
|
||||
|
||||
// Responses provides a list of response description for a given response
|
||||
// code. The most relevant response should be first in the list, as it will
|
||||
// be shown in documentation that only allows a single example.
|
||||
Responses map[int][]Response
|
||||
|
||||
// Unpublished indicates that this operation should not appear in public
|
||||
// documentation or help text. The operation may still have documentation
|
||||
// attached that can be used internally.
|
||||
Unpublished bool
|
||||
|
||||
// Deprecated indicates that this operation should be avoided.
|
||||
Deprecated bool
|
||||
}
|
||||
|
||||
// RequestExample is example of request data.
|
||||
type RequestExample struct {
|
||||
Description string // optional description of the request
|
||||
Data map[string]interface{} // map version of sample JSON request data
|
||||
|
||||
// Optional example response to the sample request. This approach is considered
|
||||
// provisional for now, and this field may be changed or removed.
|
||||
Response *Response
|
||||
}
|
||||
|
||||
// Response describes and optional demonstrations an operation response.
|
||||
type Response struct {
|
||||
Description string // summary of the the response and should always be provided
|
||||
MediaType string // media type of the response, defaulting to "application/json" if empty
|
||||
Example *logical.Response // example response data
|
||||
}
|
||||
|
||||
// PathOperation is a concrete implementation of OperationHandler.
|
||||
type PathOperation struct {
|
||||
Callback OperationFunc
|
||||
Summary string
|
||||
Description string
|
||||
Examples []RequestExample
|
||||
Responses map[int][]Response
|
||||
Unpublished bool
|
||||
Deprecated bool
|
||||
}
|
||||
|
||||
func (p *PathOperation) Handler() OperationFunc {
|
||||
return p.Callback
|
||||
}
|
||||
|
||||
func (p *PathOperation) Properties() OperationProperties {
|
||||
return OperationProperties{
|
||||
Summary: strings.TrimSpace(p.Summary),
|
||||
Description: strings.TrimSpace(p.Description),
|
||||
Responses: p.Responses,
|
||||
Examples: p.Examples,
|
||||
Unpublished: p.Unpublished,
|
||||
Deprecated: p.Deprecated,
|
||||
}
|
||||
}
|
||||
|
||||
func (p *Path) helpCallback(b *Backend) OperationFunc {
|
||||
return func(ctx context.Context, 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, errwrap.Wrapf("error executing template: {{err}}", err)
|
||||
}
|
||||
|
||||
// Build OpenAPI response for this path
|
||||
doc := NewOASDocument()
|
||||
if err := documentPath(p, b.SpecialPaths(), b.BackendType, doc); err != nil {
|
||||
b.Logger().Warn("error generating OpenAPI", "error", err)
|
||||
}
|
||||
|
||||
return logical.HelpResponse(help, nil, doc), 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}}
|
||||
`
|
||||
Reference in New Issue
Block a user