oas

package module
v0.7.2 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 8, 2018 License: MIT Imports: 27 Imported by: 1

README

oas2

GoDoc CircleCI codecov Go Report Card GitHub release License MIT

Note that this is not stable yet. In accordance with semantic versioning, the API can change between any minor versions. Use a vendoring tool of your preference to lock an exact release version.

Package oas2 provides utilities for building APIs using the OpenAPI 2.0 specification (aka Swagger) in Go idiomatic way on top of net/http.

You don't need to learn any special framework or write net/http-incompatible code - just delegate request validation, request parameters decoding and other routines to this library - and focus on your application logic.

This package is built on top of OpenAPI Initiative golang toolkit.

Should I have an OpenAPI specification for my API?

If you don't have a spec for your API yet - it's definitely worth it to create one. The specification itself provides many useful things, such as documentation, usage examples, and others. Learn more about OpenAPI and its purposes. The great thing is that it is compatible with many tools for developers and consumers; Swagger Toolkit is the most popular set of utilities for OpenAPI.

This package offers an integration of the spec with your code. And tightly coupling your code with the spec is a good thing - you create a strong contract for API consumers, and any changes to your API will be clearly reflected in the spec. You will see many benefits, such as distinctly recognize the situation when you need to increase the major version of your API because of incompatible changes.

Features

Router from a spec

This package provides an easy way to automatically create a router supporting all resources from your OpenAPI specification file. The underlying router is only your choice - you can use gorilla/mux, chi or any other.

Let's dive into a simple example.

Given a spec: petstore.yaml

First of all, load your spec in your app (note that though package import path ends in oas2, the package namespace is actually oas):

import "github.com/hypnoglow/oas2"

// ...

// specPath is a path to your spec file.
doc, _ := oas.LoadFile(specPath)

Next, create an operation handler. Let's define a handler for findPetsByStatus operation:

type FindPetsByStatusHandler struct {
	storage PetStorage
}

func (h FindPetsByStatusHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
	statuses := req.URL.Query()["status"]

	pets := h.storage.FindByStatus(statuses)

	_ = json.NewEncoder(w).Encode(pets)
}
handlers := oas.OperationHandlers{
    "findPetsByStatus":    findPetsByStatus{},
}

Define what options (logger, middleware) you will use:

logger := logrus.New()
logger.SetLevel(logrus.DebugLevel)
queryValidator := oas.QueryValidator(errHandler)

Create a router:

router, _ := oas.NewRouter(
    doc, 
    handlers, 
    oas.DebugLog(logger.Debugf), 
    oas.Use(queryValidator)
)

Then you can use your router as an argument for http.ListenAndServe or even as a subrouter for the other router.

http.ListenAndServe(":8080", router)

Now the server handles requests based on the paths defined in the given spec. It validates request query parameters against the spec and runs errHandler func if any error occured during validation. The router also sets the operation identifier to each request's context, so it can be used in a handler or any custom middleware.

See the full example for the complete code.

Decode query parameters to a struct

Given request query parameters: ?name=John&age=27

Given OpenAPI v2 (swagger) spec:

...
parameters:
- name: name
  type: string
- name: age
  type: integer
  format: int32
- name: loves_apples
  type: bool
  default: true
...

In your Go code you create a struct:

type Member struct {
	Name        string `oas:"name"`
	Age         int32  `oas:"age"`
	LovesApples bool   `oas:"loves_apples"`
}

And populate it:

var m Member 
oas.DecodeQuery(req, &m)

fmt.Printf("%#v", m) // Member{Name:"John", Age:27, LovesApples:true}

Note that it works only with oas router, because it needs to extract operation spec from the request. To use custom parameters spec, use oas.DecodeQueryParams(). See godoc example for details.

Pluggable formats & validators

The specification allows to have custom formats and to validate against them.

This package provides the following custom formats and validators:

You can also implement your custom format and validator for it, and then register it:

validate.RegisterFormat("myformat", &MyCustomFormat{}, ValidateMyCustomFormat)

License

MIT.

Documentation

Overview

Package oas provides utilities to work with OpenAPI 2.0 specification (aka Swagger).

The purpose of this package is to provide utilities for building APIs from the OpenAPI specification in Go idiomatic way on top of `net/http`. The package can handle request validation, request parameters decoding and other routines.

Index

Examples

Constants

This section is empty.

Variables

View Source
var DefaultExtractorFunc = chi.URLParam

DefaultExtractorFunc is the extractor func that should be used with default router.

Functions

func DecodeQuery

func DecodeQuery(req *http.Request, dst interface{}) error

DecodeQuery decodes all query params by request operation spec to the dst.

func DecodeQueryParams added in v0.6.0

func DecodeQueryParams(ps []spec.Parameter, q url.Values, dst interface{}) error

DecodeQueryParams decodes query parameters by their spec to the dst.

Example
// In real app parameters will be taken from spec document (yaml or json).
params := []spec.Parameter{
	*spec.QueryParam("name").Typed("string", ""),
	*spec.QueryParam("age").Typed("integer", "int32"),
	*spec.QueryParam("loves_apples").Typed("boolean", "").
		AsRequired().
		WithDefault(true),
}

// In real app query will be taken from *http.Request.
query := url.Values{"name": []string{"John"}, "age": []string{"27"}}

type member struct {
	Name        string `oas:"name"`
	Age         int32  `oas:"age"`
	LovesApples bool   `oas:"loves_apples"`
}

var m member
if err := DecodeQueryParams(params, query, &m); err != nil {
	panic(err)
}

fmt.Printf("%#v", m)
Output:

oas.member{Name:"John", Age:27, LovesApples:true}

func DynamicSpecHandler added in v0.5.0

func DynamicSpecHandler(s *spec.Swagger) http.Handler

DynamicSpecHandler returns HTTP handler for OpenAPI spec that changes its host and schemes dynamically based on incoming request.

func GetPathParam

func GetPathParam(req *http.Request, name string) interface{}

GetPathParam returns a path parameter by name from a request. For example, a handler defined on a path "/pet/{id}" gets a request with path "/pet/12" - in this case GetPathParam(req, "id") returns 12.

func StaticSpecHandler added in v0.5.0

func StaticSpecHandler(s *spec.Swagger) http.Handler

StaticSpecHandler returns HTTP handler for static OpenAPI spec.

func WithOperation added in v0.2.1

func WithOperation(req *http.Request, op *Operation) *http.Request

WithOperation returns request with context value defining *spec.Operation.

func WithPathParam

func WithPathParam(req *http.Request, name string, value interface{}) *http.Request

WithPathParam returns request with context value defining path parameter name set to value.

Types

type BaseRouter

type BaseRouter interface {
	http.Handler
	Use(middleware func(http.Handler) http.Handler)
	Route(method string, pathPattern string, handler http.Handler)
}

BaseRouter is an underlying router used in oas router. Any third-party router can be a BaseRouter by using adapter pattern.

func ChiAdapter

func ChiAdapter(router chi.Router) BaseRouter

ChiAdapter returns a BaseRouter made from chi router (github.com/go-chi/chi). This is just an example adapter implementation, you should implement your own adapter for a desired router if you need it.

func DefaultBaseRouter added in v0.7.0

func DefaultBaseRouter() BaseRouter

DefaultBaseRouter is the base router used by default when no specific base router passed to NewRouter().

type Document added in v0.7.0

type Document struct {
	*loads.Document
}

Document represents a swagger spec document.

func LoadFile added in v0.6.0

func LoadFile(fpath string, opts ...LoadOption) (*Document, error)

LoadFile loads OpenAPI specification from file.

type JSONError added in v0.5.0

type JSONError struct {
	// contains filtered or unexported fields
}

JSONError occurs on json encoding or decoding. It can happen both in request and response validation.

type LoadOption added in v0.6.0

type LoadOption func(*LoadOptions)

LoadOption is option to use when loading specification.

func LoadCacheDir added in v0.6.0

func LoadCacheDir(dir string) LoadOption

LoadCacheDir returns option that allows to load expanded spec from cache.

func LoadSetAPIVersion added in v0.6.0

func LoadSetAPIVersion(version string) LoadOption

LoadSetAPIVersion returns option that sets application API version.

func LoadSetHost added in v0.6.0

func LoadSetHost(host string) LoadOption

LoadSetHost returns option that sets specification host.

func LoadSetSchemes added in v0.6.0

func LoadSetSchemes(schemes []string) LoadOption

LoadSetSchemes returns option that sets specification schemes.

type LoadOptions added in v0.6.0

type LoadOptions struct {
	// contains filtered or unexported fields
}

LoadOptions represent options that are used on specification load.

type LogWriter

type LogWriter func(format string, args ...interface{})

LogWriter logs router operations that will be handled and what will be not during router creation. Useful for debugging.

type Middleware

type Middleware func(next http.Handler) http.Handler

Middleware describes a middleware that can be applied to a http.handler.

func BodyValidator added in v0.4.0

func BodyValidator(errHandler RequestErrorHandler, opts ...MiddlewareOption) Middleware

BodyValidator returns new Middleware that validates request body against parameters defined in OpenAPI 2.0 spec.

func PathParameterExtractor added in v0.4.0

func PathParameterExtractor(extractor func(r *http.Request, key string) string) Middleware

PathParameterExtractor returns new Middleware that extracts parameters defined in OpenAPI 2.0 spec as path parameters from path.

func QueryValidator added in v0.4.0

func QueryValidator(errHandler RequestErrorHandler) Middleware

QueryValidator returns new Middleware that validates request query parameters against OpenAPI 2.0 spec.

func ResponseBodyValidator added in v0.4.0

func ResponseBodyValidator(errHandler ResponseErrorHandler, opts ...MiddlewareOption) Middleware

ResponseBodyValidator returns new Middleware that validates response body against schema defined in OpenAPI 2.0 spec.

type MiddlewareOption added in v0.6.0

type MiddlewareOption func(*MiddlewareOptions)

MiddlewareOption represent option for middleware.

func ContentTypeRegexSelector added in v0.6.0

func ContentTypeRegexSelector(selector *regexp.Regexp) MiddlewareOption

ContentTypeRegexSelector select requests/responses based on Content-Type header. If any selector matches Content-Type of the request/response, then it will be validated. Otherwise, validator skips validation of the request/response.

This options can be applied to the following middlewares: - BodyValidator - ResponseBodyValidator

type MiddlewareOptions added in v0.6.0

type MiddlewareOptions struct {
	// contains filtered or unexported fields
}

MiddlewareOptions represent options for middleware.

type Operation added in v0.7.0

type Operation struct {
	*spec.Operation
}

Operation describes a single API operation on a path.

func GetOperation

func GetOperation(req *http.Request) *Operation

GetOperation returns *spec.Operation from the request's context. In case of operation not found GetOperation returns nil.

func MustOperation added in v0.1.2

func MustOperation(req *http.Request) *Operation

MustOperation returns *spec.Operation from the request's context. In case of operation not found MustOperation panics.

type OperationHandlers

type OperationHandlers map[OperationID]http.Handler

OperationHandlers maps OperationID to its handler.

type OperationID

type OperationID string

OperationID is an operation identifier.

func (OperationID) String

func (oid OperationID) String() string

String implements fmt.Stringer interface.

Example
opID := OperationID("addPet")

fmt.Fprint(os.Stdout, opID.String())
Output:

addPet

type RequestErrorHandler

type RequestErrorHandler func(w http.ResponseWriter, req *http.Request, err error) (resume bool)

RequestErrorHandler is a function that handles an error occurred in middleware while working with request. It is the library user responsibility to implement this to handle various errors that can occur during middleware work. This errors can include request validation errors, json encoding errors and other. Also, user must return proper boolean value that indicates if the request should continue or it should be stopped (basically, call "next" or not).

type ResponseErrorHandler

type ResponseErrorHandler func(w http.ResponseWriter, req *http.Request, err error)

ResponseErrorHandler is a function that handles an error occurred in middleware while working with response. It is the library user responsibility to implement this to handle various errors that can occur on middleware work. This errors can include response validation errors, json serialization errors and others.

type Router

type Router struct {
	// contains filtered or unexported fields
}

Router routes requests based on OAS 2.0 spec operations.

func NewRouter

func NewRouter(
	doc *Document,
	handlers OperationHandlers,
	options ...RouterOption,
) (*Router, error)

NewRouter returns a new Router.

func (*Router) AddSpec added in v0.7.2

func (r *Router) AddSpec(doc *Document, handlers OperationHandlers) error

AddSpec adds routes from the spec to the router.

func (*Router) ServeHTTP

func (r *Router) ServeHTTP(w http.ResponseWriter, req *http.Request)

ServeHTTP implements http.Handler.

type RouterOption

type RouterOption func(*Router)

RouterOption is an option for oas router.

func Base

func Base(br BaseRouter) RouterOption

Base returns an option that sets a BaseRouter for oa2 router. It allows to plug-in your favorite router to the oas router.

func DebugLog

func DebugLog(lw LogWriter) RouterOption

DebugLog returns an option that sets a debug log for oas router. Debug log may help to see what router operations will be handled and what will be not.

func ServeSpec added in v0.5.0

func ServeSpec(t SpecHandlerType) RouterOption

ServeSpec returns an option that makes router serve its spec.

func Use

func Use(mw Middleware) RouterOption

Use returns an option that sets a middleware for router operations.

This middleware is applied to each operation handler. Thus, this middleware can use request context to extract and use operation spec. Also, this middleware cannot affect routing; for example, CORS middleware that handles OPTIONS method for all routes won't work with Use; use Wrap option instead.

Multiple middlewares will be executed exactly in the same order they were passed to the router. For example:

router, _ := oas.NewRouter(
    doc,
    handlers,
    oas.Use(RequestID),
    oas.Use(RequestLogger),
)

Here the RequestLogger will be executed after RequestID and thus will be able to use request id that RequestID middleware stored in a request context.

func Wrap added in v0.7.0

func Wrap(mw Middleware) RouterOption

Wrap returns an option that sets a middleware for the router.

This middleware is applied to the router itself. Thus, this middleware cannot extract operation spec from context. But, in contrast to Use, this middleware can affect routing, so this is a proper option to use CORS and similar.

All oas-specific middlewares like oas.QueryValidator should use Use option instead.

Multiple middlewares will be executed exactly in the same order they were passed to the router. For example:

router, _ := oas.NewRouter(
    doc,
    handlers,
    oas.Wrap(RequestID),
    oas.Wrap(RequestLogger),
)

Here the RequestLogger will be executed after RequestID and thus will be able to use request id that RequestID middleware stored in a request context.

type SpecHandlerType added in v0.5.0

type SpecHandlerType int

SpecHandlerType represents spec handler type.

const (
	// SpecHandlerTypeDynamic represents dynamic spec handler.
	SpecHandlerTypeDynamic SpecHandlerType = iota + 1

	// SpecHandlerTypeStatic represents static spec handler.
	SpecHandlerTypeStatic
)

type ValidationError

type ValidationError struct {
	// contains filtered or unexported fields
}

ValidationError occurs on request or response validation.

func (ValidationError) Error

func (ve ValidationError) Error() string

Error implements error.

func (ValidationError) Errors

func (ve ValidationError) Errors() []error

Errors returns validation errors.

Directories

Path Synopsis
_examples
cmd
oas-expand
CLI utility that expands OAS file to reduce init time.
CLI utility that expands OAS file to reduce init time.
e2e
Package validate provides utilities that allow to validate request and response data against OpenAPI Specification parameter and schema definitions.
Package validate provides utilities that allow to validate request and response data against OpenAPI Specification parameter and schema definitions.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL