twirk

package module
v5.9.0+incompatible Latest Latest
Warning

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

Go to latest
Published: Sep 6, 2019 License: Apache-2.0 Imports: 5 Imported by: 0

README

twirk Logo Build Status Go Report Card GoDoc


twirk is a framework for service-to-service communication emphasizing simplicity and minimalism. It generates routing and serialization from API definition files and lets you focus on your application's logic instead of thinking about folderol like HTTP methods and paths and JSON.

Define your service in a Protobuf file and then twirk autogenerates Go code with a server interface and fully functional clients. It's similar to gRPC, but without the custom HTTP server and transport implementations: it runs on the standard library's extremely-well-tested-and-high-performance net/http Server. It can run on HTTP 1.1, not just http/2, and supports JSON clients for easy integrations across languages

twirk handles routing and serialization for you in a well-tested, standardized, thoughtful way so you don't have to. Serialization and deserialization code is error-prone and tricky, and you shouldn't be wasting your time deciding whether it should be "POST /friends/:id/new" or "POST /:id/friend" or whatever. Just get to the real work of building services!

Along the way, you get an autogenerated client and a simple, smart framework for passing error messages. Nice!

For more on the motivation behind twirk (and a comparison to REST APIs and gRPC), the announcement blog post is a good read.

Installation

Use go get to install the Go client-and-server generator:

go get github.com/darioielardi/twirk/protoc-gen-twirk

You will also need:

Documentation

Thorough documentation is on the website.

Source for this documentation is in the docs subdirectory. The website is generated from that folder using Docusaurus.

Implementations in other languages

This repo only has the official generators, which write out Go and Python code. For other languages, there are third-party generators available:

Language Clients Servers Repository
Java github.com/fajran/protoc-gen-twirk_java_jaxrs
Java https://github.com/devork/flit
JavaScript github.com/thechriswalker/protoc-gen-twirk_js
JavaScript github.com/Xe/twirk-codegens/cmd/protoc-gen-twirk_jsbrowser
Typescript github.com/larrymyers/protoc-gen-twirk_typescript
Ruby github.com/darioielardi/twirk-ruby
Rust github.com/cretz/prost-twirk
Swagger github.com/elliots/protoc-gen-twirk_swagger
PHP github.com/twirkhp/twirk
Dart github.com/apptreesoftware/protoc-gen-twirk_dart

This list isn't an endorsement, it's just a convenience to help you find stuff for your language.

Support and Community

We have a channel on the Gophers slack, #twirk, which is the best place to get quick answers to your questions. You can join the Gopher slack here.

Releases

twirk follows semantic versioning through git tags, and uses Github Releases for release notes and upgrade guides: twirk Releases

Contributing

Check out CONTRIBUTING.md for notes on making contributions.

License

This library is licensed under the Apache 2.0 License.

Documentation

Overview

Package twirk provides core types used in generated twirk servers and client.

twirk services handle errors using the `twirk.Error` interface.

For example, a server method may return an InvalidArgumentError:

if req.Order != "DESC" && req.Order != "ASC" {
    return nil, twirk.InvalidArgumentError("Order", "must be DESC or ASC")
}

And the same twirk.Error is returned by the client, for example:

resp, err := twirkClient.RPCMethod(ctx, req)
if err != nil {
    if twerr, ok := err.(twirk.Error); ok {
        switch twerr.Code() {
        case twirk.InvalidArgument:
            log.Error("invalid argument "+twirk.Meta("argument"))
        default:
            log.Error(twerr.Error())
        }
    }
}

Clients may also return Internal errors if something failed on the system: the server, the network, or the client itself (i.e. failure parsing response).

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func HTTPRequestHeaders

func HTTPRequestHeaders(ctx context.Context) (http.Header, bool)

func IsValidErrorCode

func IsValidErrorCode(code ErrorCode) bool

IsValidErrorCode returns true if is one of the valid predefined constants.

func MethodName

func MethodName(ctx context.Context) (string, bool)

MethodName extracts the name of the method being handled in the given context. If it is not known, it returns ("", false).

func PackageName

func PackageName(ctx context.Context) (string, bool)

PackageName extracts the fully-qualified protobuf package name of the service handling the given context. If it is not known, it returns ("", false). If the service comes from a proto file that does not declare a package name, it returns ("", true).

Note that the protobuf package name can be very different than the go package name; the two are unrelated.

func ServerHTTPStatusFromErrorCode

func ServerHTTPStatusFromErrorCode(code ErrorCode) int

ServerHTTPStatusFromErrorCode maps a twirk error type into a similar HTTP response status. It is used by the twirk server handler to set the HTTP response status code. Returns 0 if the ErrorCode is invalid.

func ServiceName

func ServiceName(ctx context.Context) (string, bool)

ServiceName extracts the name of the service handling the given context. If it is not known, it returns ("", false).

func SetHTTPResponseHeader

func SetHTTPResponseHeader(ctx context.Context, key, value string) error

SetHTTPResponseHeader sets an HTTP header key-value pair using a context provided by a twirk-generated server, or a child of that context. The server will include the header in its response for that request context.

This can be used to respond with custom HTTP headers like "Cache-Control". But note that HTTP headers are a twirk implementation detail, only visible by middleware, not by the clients or their responses.

The header will be ignored (noop) if the context is invalid (i.e. using a new context.Background() instead of passing the context from the handler).

If called multiple times with the same key, it replaces any existing values associated with that key.

SetHTTPResponseHeader returns an error if the provided header key would overwrite a header that is needed by twirk, like "Content-Type".

func StatusCode

func StatusCode(ctx context.Context) (string, bool)

StatusCode retrieves the status code of the response (as string like "200"). If it is known returns (status, true). If it is not known, it returns ("", false).

func WithHTTPRequestHeaders

func WithHTTPRequestHeaders(ctx context.Context, h http.Header) (context.Context, error)

WithHTTPRequestHeaders stores an http.Header in a context.Context. When using a twirk-generated client, you can pass the returned context into any of the request methods, and the stored header will be included in outbound HTTP requests.

This can be used to set custom HTTP headers like authorization tokens or client IDs. But note that HTTP headers are a twirk implementation detail, only visible by middleware, not by the server implementation.

WithHTTPRequestHeaders returns an error if the provided http.Header would overwrite a header that is needed by twirk, like "Content-Type".

Types

type Error

type Error interface {
	// Code is of the valid error codes.
	Code() ErrorCode

	// Msg returns a human-readable, unstructured messages describing the error.
	Msg() string

	// WithMeta returns a copy of the Error with the given key-value pair attached
	// as metadata. If the key is already set, it is overwritten.
	WithMeta(key string, val string) Error

	// Meta returns the stored value for the given key. If the key has no set
	// value, Meta returns an empty string. There is no way to distinguish between
	// an unset value and an explicit empty string.
	Meta(key string) string

	// MetaMap returns the complete key-value metadata map stored on the error.
	MetaMap() map[string]string

	// Error returns a string of the form "twirk error <Type>: <Msg>"
	Error() string
}

Error represents an error in a twirk service call.

func InternalError

func InternalError(msg string) Error

InternalError constructor for the common Internal error. Should be used to specify that something bad or unexpected happened.

func InternalErrorWith

func InternalErrorWith(err error) Error

InternalErrorWith is an easy way to wrap another error. It adds the underlying error's type as metadata with a key of "cause", which can be useful for debugging. Should be used in the common case of an unexpected error returned from another API, but sometimes it is better to build a more specific error (like with NewError(Unknown, err.Error()), for example).

The returned error also has a Cause() method which will return the original error, if it is known. This can be used with the github.com/pkg/errors package to extract the root cause of an error. Information about the root cause of an error is lost when it is serialized, so this doesn't let a client know the exact root cause of a server's error.

func InvalidArgumentError

func InvalidArgumentError(argument string, validationMsg string) Error

InvalidArgumentError constructor for the common InvalidArgument error. Can be used when an argument has invalid format, is a number out of range, is a bad option, etc).

func NewError

func NewError(code ErrorCode, msg string) Error

NewError is the generic constructor for a twirk.Error. The ErrorCode must be one of the valid predefined constants, otherwise it will be converted to an error {type: Internal, msg: "invalid error type {{code}}"}. If you need to add metadata, use .WithMeta(key, value) method after building the error.

func NotFoundError

func NotFoundError(msg string) Error

NotFoundError constructor for the common NotFound error.

func RequiredArgumentError

func RequiredArgumentError(argument string) Error

RequiredArgumentError is a more specific constructor for InvalidArgument error. Should be used when the argument is required (expected to have a non-zero value).

type ErrorCode

type ErrorCode string

ErrorCode represents a twirk error type.

const (
	// Canceled indicates the operation was cancelled (typically by the caller).
	Canceled ErrorCode = "canceled"

	// Unknown error. For example when handling errors raised by APIs that do not
	// return enough error information.
	Unknown ErrorCode = "unknown"

	// InvalidArgument indicates client specified an invalid argument. It
	// indicates arguments that are problematic regardless of the state of the
	// system (i.e. a malformed file name, required argument, number out of range,
	// etc.).
	InvalidArgument ErrorCode = "invalid_argument"

	// Malformed indicates an error occurred while decoding the client's request.
	// This may mean that the message was encoded improperly, or that there is a
	// disagreement in message format between the client and server.
	Malformed ErrorCode = "malformed"

	// DeadlineExceeded means operation expired before completion. For operations
	// that change the state of the system, this error may be returned even if the
	// operation has completed successfully (timeout).
	DeadlineExceeded ErrorCode = "deadline_exceeded"

	// NotFound means some requested entity was not found.
	NotFound ErrorCode = "not_found"

	// BadRoute means that the requested URL path wasn't routable to a twirk
	// service and method. This is returned by the generated server, and usually
	// shouldn't be returned by applications. Instead, applications should use
	// NotFound or Unimplemented.
	BadRoute ErrorCode = "bad_route"

	// AlreadyExists means an attempt to create an entity failed because one
	// already exists.
	AlreadyExists ErrorCode = "already_exists"

	// PermissionDenied indicates the caller does not have permission to execute
	// the specified operation. It must not be used if the caller cannot be
	// identified (Unauthenticated).
	PermissionDenied ErrorCode = "permission_denied"

	// Unauthenticated indicates the request does not have valid authentication
	// credentials for the operation.
	Unauthenticated ErrorCode = "unauthenticated"

	// ResourceExhausted indicates some resource has been exhausted, perhaps a
	// per-user quota, or perhaps the entire file system is out of space.
	ResourceExhausted ErrorCode = "resource_exhausted"

	// FailedPrecondition indicates operation was rejected because the system is
	// not in a state required for the operation's execution. For example, doing
	// an rmdir operation on a directory that is non-empty, or on a non-directory
	// object, or when having conflicting read-modify-write on the same resource.
	FailedPrecondition ErrorCode = "failed_precondition"

	// Aborted indicates the operation was aborted, typically due to a concurrency
	// issue like sequencer check failures, transaction aborts, etc.
	Aborted ErrorCode = "aborted"

	// OutOfRange means operation was attempted past the valid range. For example,
	// seeking or reading past end of a paginated collection.
	//
	// Unlike InvalidArgument, this error indicates a problem that may be fixed if
	// the system state changes (i.e. adding more items to the collection).
	//
	// There is a fair bit of overlap between FailedPrecondition and OutOfRange.
	// We recommend using OutOfRange (the more specific error) when it applies so
	// that callers who are iterating through a space can easily look for an
	// OutOfRange error to detect when they are done.
	OutOfRange ErrorCode = "out_of_range"

	// Unimplemented indicates operation is not implemented or not
	// supported/enabled in this service.
	Unimplemented ErrorCode = "unimplemented"

	// Internal errors. When some invariants expected by the underlying system
	// have been broken. In other words, something bad happened in the library or
	// backend service. Do not confuse with HTTP Internal Server Error; an
	// Internal error could also happen on the client code, i.e. when parsing a
	// server response.
	Internal ErrorCode = "internal"

	// Unavailable indicates the service is currently unavailable. This is a most
	// likely a transient condition and may be corrected by retrying with a
	// backoff.
	Unavailable ErrorCode = "unavailable"

	// DataLoss indicates unrecoverable data loss or corruption.
	DataLoss ErrorCode = "data_loss"

	// NoError is the zero-value, is considered an empty error and should not be
	// used.
	NoError ErrorCode = ""
)

Valid twirk error types. Most error types are equivalent to gRPC status codes and follow the same semantics.

type ServerHooks

type ServerHooks struct {
	// RequestReceived is called as soon as a request enters the twirk
	// server at the earliest available moment.
	RequestReceived func(context.Context) (context.Context, error)

	// RequestRouted is called when a request has been routed to a
	// particular method of the twirk server.
	RequestRouted func(context.Context) (context.Context, error)

	// RequestDecoded is called when a request has been decoded into the
	// corrisponding struct. Useful for validation or others request specific actions
	RequestDecoded func(context.Context, interface{}) (context.Context, error)

	// ResponsePrepared is called when a request has been handled and a
	// response is ready to be sent to the client.
	ResponsePrepared func(context.Context) context.Context

	// ResponseSent is called when all bytes of a response (including an error
	// response) have been written. Because the ResponseSent hook is terminal, it
	// does not return a context.
	ResponseSent func(context.Context)

	// Error hook is called when an error occurs while handling a request. The
	// Error is passed as argument to the hook.
	Error func(context.Context, Error) context.Context
}

ServerHooks is a container for callbacks that can instrument a twirk-generated server. These callbacks all accept a context and return a context. They can use this to add to the request context as it threads through the system, appending values or deadlines to it.

The RequestReceived and RequestRouted hooks are special: they can return errors. If they return a non-nil error, handling for that request will be stopped at that point. The Error hook will be triggered, and the error will be sent to the client. This can be used for stuff like auth checks before deserializing a request.

The RequestReceived hook is always called first, and it is called for every request that the twirk server handles. The last hook to be called in a request's lifecycle is always ResponseSent, even in the case of an error.

Details on the timing of each hook are documented as comments on the fields of the ServerHooks type.

func ChainHooks

func ChainHooks(hooks ...*ServerHooks) *ServerHooks

ChainHooks creates a new *ServerHooks which chains the callbacks in each of the constituent hooks passed in. Each hook function will be called in the order of the ServerHooks values passed in.

For the erroring hooks, RequestReceived and RequestRouted, any returned errors prevent processing by later hooks.

Directories

Path Synopsis
_tools
src/github.com/gogo/protobuf/gogoproto
Package gogoproto provides extensions for protocol buffers to achieve: - fast marshalling and unmarshalling.
Package gogoproto provides extensions for protocol buffers to achieve: - fast marshalling and unmarshalling.
src/github.com/gogo/protobuf/plugin/defaultcheck
The defaultcheck plugin is used to check whether nullable is not used incorrectly.
The defaultcheck plugin is used to check whether nullable is not used incorrectly.
src/github.com/gogo/protobuf/plugin/description
The description (experimental) plugin generates a Description method for each message.
The description (experimental) plugin generates a Description method for each message.
src/github.com/gogo/protobuf/plugin/embedcheck
The embedcheck plugin is used to check whether embed is not used incorrectly.
The embedcheck plugin is used to check whether embed is not used incorrectly.
src/github.com/gogo/protobuf/plugin/enumstringer
The enumstringer (experimental) plugin generates a String method for each enum.
The enumstringer (experimental) plugin generates a String method for each enum.
src/github.com/gogo/protobuf/plugin/equal
The equal plugin generates an Equal and a VerboseEqual method for each message.
The equal plugin generates an Equal and a VerboseEqual method for each message.
src/github.com/gogo/protobuf/plugin/face
The face plugin generates a function will be generated which can convert a structure which satisfies an interface (face) to the specified structure.
The face plugin generates a function will be generated which can convert a structure which satisfies an interface (face) to the specified structure.
src/github.com/gogo/protobuf/plugin/gostring
The gostring plugin generates a GoString method for each message.
The gostring plugin generates a GoString method for each message.
src/github.com/gogo/protobuf/plugin/marshalto
The marshalto plugin generates a Marshal and MarshalTo method for each message.
The marshalto plugin generates a Marshal and MarshalTo method for each message.
src/github.com/gogo/protobuf/plugin/oneofcheck
The oneofcheck plugin is used to check whether oneof is not used incorrectly.
The oneofcheck plugin is used to check whether oneof is not used incorrectly.
src/github.com/gogo/protobuf/plugin/populate
The populate plugin generates a NewPopulated function.
The populate plugin generates a NewPopulated function.
src/github.com/gogo/protobuf/plugin/size
The size plugin generates a Size or ProtoSize method for each message.
The size plugin generates a Size or ProtoSize method for each message.
src/github.com/gogo/protobuf/plugin/stringer
The stringer plugin generates a String method for each message.
The stringer plugin generates a String method for each message.
src/github.com/gogo/protobuf/plugin/testgen
The testgen plugin generates Test and Benchmark functions for each message.
The testgen plugin generates Test and Benchmark functions for each message.
src/github.com/gogo/protobuf/plugin/union
The onlyone plugin generates code for the onlyone extension.
The onlyone plugin generates code for the onlyone extension.
src/github.com/gogo/protobuf/plugin/unmarshal
The unmarshal plugin generates a Unmarshal method for each message.
The unmarshal plugin generates a Unmarshal method for each message.
src/github.com/gogo/protobuf/proto
Package proto converts data structures to and from the wire format of protocol buffers.
Package proto converts data structures to and from the wire format of protocol buffers.
src/github.com/gogo/protobuf/protoc-gen-gogo/descriptor
Package descriptor provides functions for obtaining protocol buffer descriptors for generated Go types.
Package descriptor provides functions for obtaining protocol buffer descriptors for generated Go types.
src/github.com/gogo/protobuf/protoc-gen-gogo/generator
The code generator for the plugin for the Google protocol buffer compiler.
The code generator for the plugin for the Google protocol buffer compiler.
src/github.com/gogo/protobuf/protoc-gen-gogo/grpc
Package grpc outputs gRPC service descriptions in Go code.
Package grpc outputs gRPC service descriptions in Go code.
src/github.com/gogo/protobuf/protoc-gen-gogo/plugin
Package plugin_go is a generated protocol buffer package.
Package plugin_go is a generated protocol buffer package.
src/github.com/golang/protobuf/proto
Package proto converts data structures to and from the wire format of protocol buffers.
Package proto converts data structures to and from the wire format of protocol buffers.
src/github.com/golang/protobuf/protoc-gen-go/generator
The code generator for the plugin for the Google protocol buffer compiler.
The code generator for the plugin for the Google protocol buffer compiler.
src/github.com/golang/protobuf/protoc-gen-go/generator/internal/remap
Package remap handles tracking the locations of Go tokens in a source text across a rewrite by the Go formatter.
Package remap handles tracking the locations of Go tokens in a source text across a rewrite by the Go formatter.
src/github.com/golang/protobuf/protoc-gen-go/grpc
Package grpc outputs gRPC service descriptions in Go code.
Package grpc outputs gRPC service descriptions in Go code.
src/github.com/golang/protobuf/protoc-gen-go/plugin
Package plugin_go is a generated protocol buffer package.
Package plugin_go is a generated protocol buffer package.
src/github.com/kisielk/errcheck/internal/errcheck
Package errcheck is the library used to implement the errcheck command-line tool.
Package errcheck is the library used to implement the errcheck command-line tool.
src/github.com/kisielk/gotool
Package gotool contains utility functions used to implement the standard "cmd/go" tool, provided as a convenience to developers who want to write tools with similar semantics.
Package gotool contains utility functions used to implement the standard "cmd/go" tool, provided as a convenience to developers who want to write tools with similar semantics.
src/github.com/kisielk/gotool/internal/load
Package load loads packages.
Package load loads packages.
src/golang.org/x/tools/go/ast/astutil
Package astutil contains common utilities for working with the Go AST.
Package astutil contains common utilities for working with the Go AST.
src/golang.org/x/tools/go/buildutil
Package buildutil provides utilities related to the go/build package in the standard library.
Package buildutil provides utilities related to the go/build package in the standard library.
src/golang.org/x/tools/go/loader
Package loader loads a complete Go program from source code, parsing and type-checking the initial packages plus their transitive closure of dependencies.
Package loader loads a complete Go program from source code, parsing and type-checking the initial packages plus their transitive closure of dependencies.
internal/clientcompat
Package clientcompat is a generated twirk stub package.
Package clientcompat is a generated twirk stub package.
Package ctxsetters is an implementation detail for twirk generated code, used by the generated servers to set values in contexts for later access with the twirk package's accessors.
Package ctxsetters is an implementation detail for twirk generated code, used by the generated servers to set values in contexts for later access with the twirk package's accessors.
Package example is a generated twirk stub package.
Package example is a generated twirk stub package.
hooks
internal
contextkeys
Package contextkeys stores the keys to the context accessor functions, letting generated code safely set values in contexts without exposing the setters to the outside world.
Package contextkeys stores the keys to the context accessor functions, letting generated code safely set values in contexts without exposing the setters to the outside world.
descriptors
package descriptors provides tools for manipulating and inspecting protobuf descriptors.
package descriptors provides tools for manipulating and inspecting protobuf descriptors.
gen
twirptest
package twirktest provides servers for use in tests and for testing the cleanliness of the generated output of protoc-gen-twirk.
package twirktest provides servers for use in tests and for testing the cleanliness of the generated output of protoc-gen-twirk.
twirptest/empty_service
Package empty_service is a generated twirk stub package.
Package empty_service is a generated twirk stub package.
twirptest/gogo_compat
Package gogo_compat is a generated protocol buffer package.
Package gogo_compat is a generated protocol buffer package.
twirptest/google_protobuf_imports
Package google_protobuf_imports is a generated twirk stub package.
Package google_protobuf_imports is a generated twirk stub package.
twirptest/importable
Package importable is a generated twirk stub package.
Package importable is a generated twirk stub package.
twirptest/importer
Package importer is a generated twirk stub package.
Package importer is a generated twirk stub package.
twirptest/importmapping/x
Package x is a generated twirk stub package.
Package x is a generated twirk stub package.
twirptest/multiple
Package multiple is a generated twirk stub package.
Package multiple is a generated twirk stub package.
twirptest/no_package_name
Package no_package_name is a generated twirk stub package.
Package no_package_name is a generated twirk stub package.
twirptest/no_package_name_importer
Package no_package_name_importer is a generated twirk stub package.
Package no_package_name_importer is a generated twirk stub package.
twirptest/proto
Package proto is a generated twirk stub package.
Package proto is a generated twirk stub package.
twirptest/service_method_same_name
Package service_method_same_name is a generated twirk stub package.
Package service_method_same_name is a generated twirk stub package.
twirptest/snake_case_names
Package snake_case_names is a generated twirk stub package.
Package snake_case_names is a generated twirk stub package.
twirptest/source_relative
Package source_relative is a generated twirk stub package.
Package source_relative is a generated twirk stub package.

Jump to

Keyboard shortcuts

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