Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Change READMEs to markdown files #99

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 0 additions & 27 deletions README

This file was deleted.

28 changes: 28 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# Gadgeto!

Author: Thomas Schaffer
Language: Golang
License: MIT

Gadgeto! is a collection of tools that aim to facilitate the development of
REST APIs in Go.
These tools are based on and enrich popular open-source solutions, to provide
higher-level functionalities.

## Components

- [tonic](./tonic/README.md): Based on the REST framework Gin (<https://github.com/gin-gonic/gin>),
tonic lets you write simpler handler functions, and handles
repetitive tasks for you (parameter binding, error handling).

- [zesty](./zesty/README.md): Based on Gorp (<https://github.com/go-gorp/gorp>), zesty abstracts
DB specifics for easy (nested) transaction management.

- [iffy](./iffy/README.md): An HTTP testing library for functional/unit tests,
iffy does all the hard work (http, marshaling, templating...) and
lets you describe http calls as one-liners, chaining them in complex scenarios.

- [amock](./amock/README.md): An HTTP mocking library. amock lets you easily write mock objects to inject
into http clients.
It does not require any go-generate tricks, and lets you inject mocks
into existing libraries that may not be properly abstracted through interfaces.
24 changes: 0 additions & 24 deletions amock/README

This file was deleted.

21 changes: 21 additions & 0 deletions amock/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# amock

amock lets you easily mock any HTTP dependency you may have. It respects the http.RoundTripper interface to replace an http client's transport.

Responses are stacked, and indexed by code path: you specify responses for a certain Go function, and when it's invoked the mock object will go up the stack until it reaches max depth, or finds a function for which you specified a response.

The response will be pop'ed, so the next identical call will get the next expected response.

You can specify conditional filters on responses:

- `OnFunc(foo.GetFoo)`: Filter on calls that went through a given go function
- `OnIdentifier("foo")`: Shortcut to filter on requests following a path pattern of /.../foo(/...). It is a reasonable assumption that REST implementations follow that pattern, which makes writing conditions for these simple cases very easy.
- `On(func(c *amock.Context) bool)`: More verbose but possible to express anything. Example that would filter all GET requests:

```go
On(func(c *amock.Context) bool {
return c.Request.Method == "GET"
})
```

For a working example, see amock_test.go
9 changes: 6 additions & 3 deletions iffy/README → iffy/README.md
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
# iffy

iffy helps you test http handlers.

We assume JSON for any marshaling/unmarshaling. If you use something else, that's fine, but the advanced features (responseObject + templating) will not work.

Example:
## Example

```go
func TestFoo(t *testing.T) {

// Instantiate & configure anything that implements http.Handler
r := gin.Default()
r.GET("/hello", tonic.Handler(helloHandler, 200))
Expand All @@ -32,5 +34,6 @@ func TestFoo(t *testing.T) {

tester.Run()
}
```

For a real-life example, see https://github.com/loopfz/gadgeto/blob/master/tonic/tonic_test.go
For a real-life example, see <https://github.com/loopfz/gadgeto/blob/master/tonic/tonic_test.go>
117 changes: 0 additions & 117 deletions tonic/README

This file was deleted.

152 changes: 152 additions & 0 deletions tonic/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
# tonic

tonic lets you write simpler gin handlers. The way it works is that it generates wrapping gin-compatible handlers, that do all the repetitive work and wrap the call to your simple tonic handler.

Package tonic handles path/query/body parameter binding in a single consolidated input object which allows you to remove all the boilerplate code that retrieves and tests the presence of various parameters.

## Table of contents

1. [Usage](#usage)
- [Inputs](#inputs)
- [Outputs](#outputs)
- [Validation](#validation)
- [Enum Validation](#enum-validation)
2. [Basic Example](#basic-example)
3. [Customization](#customization)
- [Custom Errors Example](#custom-errors-example)
4. [OpenAPI / Swagger Documentation](#openapi--swagger-documentation)

## Usage

### Inputs

Here is an example input object.

```go
type MyInput struct {
Foo int `path:"foo"`
Bar string `query:"bar" default:"foobar"`
Baz string `json:"baz"`
}
```

### Outputs

Output objects can be of any type, and will be marshaled to JSON.

### Validation

Input validation is performed after binding into the object using the validator library
(<https://github.com/go-playground/validator>). You can use the tag 'validate' on your object
definition to perform validation inside the tonic handler phase.

```go
type MyInput struct {
Foo int `path:"foo" validate:"required,gt=10"`
Bar string `query:"bar" default:"foobar" validate:"nefield=Baz"`
Baz string `json:"baz" validate:"required,email"`
}
```

#### Enum validation

In addition to the validation provided by the validator library, enum input validation is also implemented natively by tonic, and can check that the provided input value corresponds to one of the expected enum values.

```go
type MyInput struct {
Bar string `query:"bar" enum:"foo,buz,biz"`
}
```

The handler can return an error, which will be returned to the caller.

## Basic Example

Here is a basic application that greets a user on <http://localhost:8080/hello/me>

```go
import (
"errors"
"fmt"

"github.com/gin-gonic/gin"
"github.com/loopfz/gadgeto/tonic"
)

type GreetUserInput struct {
Name string `path:"name" validate:"required,gt=3" description:"User name"`
}

type GreetUserOutput struct {
Message string `json:"message"`
}

func GreetUser(c *gin.Context, in *GreetUserInput) (*GreetUserOutput, error) {
if in.Name == "satan" {
return nil, errors.New("go to hell")
}
return &GreetUserOutput{Message: fmt.Sprintf("Hello %s!", in.Name)}, nil
}

func main() {
r := gin.Default()
r.GET("/hello/:name", tonic.Handler(GreetUser, 200))
r.Run(":8080")
}
```

## Customization

If needed, you can also override different parts of the logic via certain available hooks in tonic:

- binding
- default: bind from JSON
- error handling
- default: http status 400
- render
- default: render into JSON

You will probably want to customize the error hook to produce finer grained error status codes.

The role of this error hook is to inspect the returned error object and deduce the http specifics from it. We provide a ready-to-use error hook that depends on the juju/errors package (richer errors): <https://github.com/loopfz/gadgeto/tree/master/tonic/utils/jujerr>

## Custom Errors Example

Example of the same application as before, using juju errors:

```go
import (
"fmt"

"github.com/gin-gonic/gin"
"github.com/juju/errors"
"github.com/loopfz/gadgeto/tonic"
"github.com/loopfz/gadgeto/tonic/utils/jujerr"
)

type GreetUserInput struct {
Name string `path:"name" description:"User name" validate:"required"`
}

type GreetUserOutput struct {
Message string `json:"message"`
}

func GreetUser(c *gin.Context, in *GreetUserInput) (*GreetUserOutput, error) {
if in.Name == "satan" {
return nil, errors.NewForbidden(nil, "go to hell")
}
return &GreetUserOutput{Message: fmt.Sprintf("Hello %s!", in.Name)}, nil
}

func main() {
tonic.SetErrorHook(jujerr.ErrHook)
r := gin.Default()
r.GET("/hello/:name", tonic.Handler(GreetUser, 200))
r.Run(":8080")
}
```

## OpenAPI / Swagger Documentation

You can also easily serve auto-generated swagger documentation (using tonic data) with <https://github.com/wi2l/fizz>
Loading