nvalid

package module
v0.0.5 Latest Latest
Warning

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

Go to latest
Published: Oct 15, 2022 License: MIT Imports: 16 Imported by: 0

README

nvalid - generate http endpoint validators for use with nvelope

GoDoc unit tests report card codecov

Install:

go get github.com/muir/nvalid

Nvalid is a wrapper around kin-openapi to make it easy for nvelope to validate requests and responses.

kin-openapi is a package that parses and manipulates OpenAPI2 and OpenAPI3 files. It can also validate requests and responses.

nvelope applies dependency injection to building HTTP endpoint handlers to reduce the boilerplate to almost nothing.

See the example in the documentation.

Documentation

Overview

Example

Example shows an injection chain handling a single endpoint using nject, nape, and nvelope.

package main

import (
	"context"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http/httptest"
	"strings"

	"github.com/getkin/kin-openapi/openapi2"
	"github.com/getkin/kin-openapi/openapi2conv"
	"github.com/ghodss/yaml"
	"github.com/gorilla/mux"
	"github.com/muir/nape"
	"github.com/muir/nvalid"
	"github.com/muir/nvelope"
)

const swagger = `
swagger: "2.0"
info:
  version: 1.0.0
  title: testing
schemes:
- "http"

paths:
  /foo/{bar}:
    post:
      summary: Example 
      produces:
        - application/json
      parameters:
        - name: bar
          in: path
          type: number
          required: true
          description: example path parameter
        - name: baz
          in: query
          type: string
          format: email
          required: true
          description: example query parameter
        - name: body
          in: body
          schema:
            type: object
            required: 
              - john
            properties:
              john:
                type: boolean
              betty:
                type: string
          required: true
          description: example body parameter
      responses:
        200:
          description: response
          schema:
            type: object
            required:
              - status
            additionalProperties: false
            properties:
              status:
                type: integer
              weight:
                type: number
        400:
          description: error
          produces:
            - text/plain`

type PostBodyModel struct {
	John  bool    `json:"john"`
	Betty *string `json:"betty"`
}

type ExampleRequestBundle struct {
	Request     PostBodyModel `nvelope:"model"`
	Bar         float64       `nvelope:"path,name=bar"`
	Baz         string        `nvelope:"query,name=baz"`
	ContentType string        `nvelope:"header,name=Content-Type"`
}

type ExampleResponse struct {
	Status interface{} `json:"status,omitempty"`
}

func HandleExampleEndpoint(req ExampleRequestBundle) (nvelope.Response, error) {
	resp := ExampleResponse{
		Status: 100,
	}
	if req.Request.John {
		resp.Status = "string"
	}
	return resp, nil
}

func Service(router *mux.Router) {
	var v2Doc openapi2.T
	err := yaml.Unmarshal([]byte(swagger), &v2Doc)
	if err != nil {
		panic(fmt.Sprint("yaml", err))
	}
	v3Doc, err := openapi2conv.ToV3(&v2Doc)
	if err != nil {
		panic("v3 convert")
	}
	err = v3Doc.Validate(context.Background())
	if err != nil {
		panic("v3 validate")
	}

	requestValidator, responseValidator, err :=
		nvalid.OpenAPI3ValidatorFromParsed(v3Doc, "inline", false)
	if err != nil {
		panic("make validators")
	}
	encodeJSON := nvelope.MakeResponseEncoder("JSON",
		nvelope.WithEncoder("application/json", json.Marshal,
			nvelope.WithAPIEnforcer(responseValidator)))
	service := nape.RegisterServiceWithMux("example", router)
	service.RegisterEndpoint("/foo/{bar}",
		// order matters and this is a correct order
		nvelope.NoLogger,
		nvelope.InjectWriter,
		encodeJSON,
		nvelope.CatchPanic,
		nvelope.Nil204,
		nvelope.ReadBody,
		requestValidator,
		nape.DecodeJSON,
		HandleExampleEndpoint,
	).Methods("POST")
}

// Example shows an injection chain handling a single endpoint using nject,
// nape, and nvelope.
func main() {
	r := mux.NewRouter()
	Service(r)
	ts := httptest.NewServer(r)
	client := ts.Client()
	doPost := func(url string, body string) {
		// nolint:noctx
		res, err := client.Post(ts.URL+url, "application/json",
			strings.NewReader(body))
		if err != nil {
			fmt.Println("response error:", err)
			return
		}
		b, err := ioutil.ReadAll(res.Body)
		if err != nil {
			fmt.Println("read error:", err)
			return
		}
		res.Body.Close()
		fmt.Println(res.StatusCode, "->"+string(b))
	}
	fmt.Println("expect valid:")
	doPost("/foo/100?baz=j@example.com", `{"john":false,"betty":"Flinstone"}`)

	fmt.Println("expect request error:")
	doPost("/foo/100", `{"john":false,"betty":"Flinstone"}`) // invalid request

	fmt.Println("expect response error:")
	doPost("/foo/100?baz=j@example.com", `{"john":true,"betty":"Flinstone"}`)

}
Output:

expect valid:
200 ->{"status":100}
expect request error:
400 ->parameter "baz" in query has an error: value is required but missing
expect response error:
500 ->response body doesn't match the schema: Error at "/status": Field must be set to integer or not be present
Schema:
  {
    "type": "integer"
  }

Value:
  "string"

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

func OpenAPI2Validator

func OpenAPI2Validator(
	swaggerFile string,
	multiError bool,
) (
	func(r *http.Request, body nvelope.Body) nject.TerminalError,
	nvelope.APIEnforcerFunc,
	error,
)

OpenAPI2Validator returns request and response validators that can drop into an nvelope-based http handler chain. The first returned function should be placed just before or after the request decoder. The second function should be as the parameter for nvelope.WithAPIEnforcer().

OpenAPI2Validator's parameters are the filename where the JSON or YAML swagger can be found and an option specifier for openapi3filter: should it return MultiError or just a single error.

func OpenAPI3Validator

func OpenAPI3Validator(
	swaggerLocation string,
	multiError bool,
) (
	func(r *http.Request, body nvelope.Body) nject.TerminalError,
	nvelope.APIEnforcerFunc,
	error,
)

OpenAPI3Validator returns request and response validators that can drop into an nvelope-based http handler chain. The first returned function should be placed just before or after the request decoder. The second function should be as the parameter for nvelope.WithAPIEnforcer().

OpenAPI3Validator's parameters are the location (file or URL) where the swagger can be found and an option specifier for openapi3filter: should it return MultiError or just a single error.

func OpenAPI3ValidatorFromParsed

func OpenAPI3ValidatorFromParsed(
	doc *openapi3.T,
	swaggerLocation string,
	multiError bool,
) (
	func(r *http.Request, body nvelope.Body) nject.TerminalError,
	nvelope.APIEnforcerFunc,
	error,
)

OpenAPI3ValidatorFromParsed returns request and response validators that can drop into an nvelope-based http handler chain. The first returned function should be placed just before or after the request decoder. The second function should be as the parameter for nvelope.WithAPIEnforcer().

OpenAPI3Validator's parameters are the parsed API specification, the location (file or URL) where the swagger can be found; and an option specifier for openapi3filter: should it return MultiError or just a single error.

Types

This section is empty.

Jump to

Keyboard shortcuts

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