jsonapi

package module
v0.0.0-...-5001978 Latest Latest
Warning

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

Go to latest
Published: Aug 17, 2015 License: MIT Imports: 13 Imported by: 0

README

jsonapi

developer's note: jsonapi is not complete yet. this readme refers to features that are not complete yet, but will be before this project hits 1.0. Volenti non fit iniuria.

jsonapi is a Golang http framework for producing JSON-based APIs. jsonapi's mission is to provide rails-like simplicity without sacrificing the flexibility, performance, scalability, and concurrency that Go is known for.

Fully-functional Blog Example

jsonapi is:

  • Easy to understand. Code written for jsonapi reads like a graph of your software architecture model, and everything else is CRUD primitives.
  • Powerful by default. Several Resources (SQL, Redis, etc) come built in, as well as Caching and Pagination layers that can be easily tacked on anywhere.
  • Very fast. Most requests incur under 2ms of framework overhead, even on my low-grade laptop.
  • Flexible for high-volume enviornments. jsonapi is designed to be the framework that you write your prototype with, and the framework that you scale to millions with, without substantial rewrites in between.
  • Concurrent to the extent possible. jsonapi builds an internal dependency tree that allows Resources to compute at their own speed, independent of one another until they must be brought together.
  • Fail-safe. Resource-agnostic mechanisms exist to roll back transactions after a critical failure
  • Built with authentication in mind. jsonapi provides a generalized authentication scheme that can be easily extended to check for login, permission level, or outright refuse certain types of requests for certain resources
  • Compliant with the json-api spec

jsonapi is not:

  • capable of being a drop in replacement for a pre-existing API, unless that API is already json-api spec compliant.

Documentation

Index

Constants

This section is empty.

Variables

View Source
var FutureOutputTimeout time.Duration = 10 * time.Second

Functions

func Catch

func Catch(f func()) (r interface{})

* Catch() functions similarly to most other languages try-catch... If a panic is thrown within the provided lambda, it will be intercepted and returned as an argument. If no error occurs, the return is nil.

func Check

func Check(e error)

* Check() will call panic(e) if the error provided is non-nil

func DenatureObject

func DenatureObject(data interface{}) map[string]interface{}

func GetField

func GetField(i interface{}, field string) interface{}

func GetFieldByTag

func GetFieldByTag(ider interface{}, realtag string) (reflect.Value, reflect.StructField)

func GetId

func GetId(ider interface{}) string

func GetIdField

func GetIdField(ider interface{}) (reflect.Value, reflect.StructField)

func IsZeroOfUnderlyingType

func IsZeroOfUnderlyingType(x interface{}) bool

func Reply

func Reply(a interface{})

* Reply() is just an alias for panic() -- it is syntax sugar in a few places.

func SetField

func SetField(field string, i interface{}, v interface{})

func SetId

func SetId(ider interface{}, id string) error

Types

type API

type API struct {
	Resources              map[string]*APIMountedResource
	Relationships          map[string]map[string]*APIMountedRelationship
	DefaultResourceWrapper func(*APIMountedResource)
	Router                 *httprouter.Router
	BaseURI                string
	Logger                 Logger
}

*

  • API is the primary user-facing structure within this framework. It
  • provides all of the functionality needed to intialize this framework,
  • as well as all of the glue to step down into more specific functionality

func NewAPI

func NewAPI(baseuri string) *API

func (*API) CentralFindRouter

func (a *API) CentralFindRouter(r *Request, resourcestr, idstr string, preroute []string, outputtype OutputType)

func (*API) EntryCreate

func (a *API) EntryCreate(r *Request)

func (*API) EntryDelete

func (a *API) EntryDelete(r *Request)

func (*API) EntryFindDefault

func (a *API) EntryFindDefault(r *Request)

func (*API) EntryFindRecordByResourceAndId

func (a *API) EntryFindRecordByResourceAndId(r *Request)

func (*API) EntryFindRelationshipByNameAndResourceId

func (a *API) EntryFindRelationshipByNameAndResourceId(r *Request)

func (*API) EntryFindRelationshipsByResourceId

func (a *API) EntryFindRelationshipsByResourceId(r *Request)

func (*API) EntryUpdate

func (a *API) EntryUpdate(r *Request)

func (*API) GetRelationship

func (a *API) GetRelationship(srcR, linkName string) *APIMountedRelationship

* GetRelationship() will return a single relationship for a given resource string and relationship string. If the resource or relationship does not exist, this function returns a nil pointer.

func (*API) GetRelationshipsByResource

func (a *API) GetRelationshipsByResource(resource string) map[string]*APIMountedRelationship

* GetRelationshipsByResource() will return a list of all of the relationships that the given resource string can link to.

func (*API) GetResource

func (a *API) GetResource(name string) *APIMountedResource

* GetResource() will return the resource for a given resource string. If the resource does not exist, this function returns a nil pointer.

func (*API) InitRouter

func (a *API) InitRouter()

* InitRouter() prepares the internal httprouter object with all of the desired routes. This is called automatically. You should never have to call this unless you wish to muck around with the httprouter

func (*API) MountRelationship

func (a *API) MountRelationship(name, srcResourceName, dstResourceName string, relationship Relationship, authenticators ...Authenticator)

* MountRelationship() will take a given Relationship and make it available for requests sent to the given API. This also requires providing a source and destination Resource string. These resources must have already been mounted with MountResource() or this function will panic.

func (*API) MountResource

func (a *API) MountResource(name string, resource Resource, authenticators ...Authenticator)

* MountResource() will take a given Resource and make it available for requests sent to the given API. Any Resource that is accessible goes through this function

func (*API) ServeHTTP

func (a *API) ServeHTTP(w http.ResponseWriter, r *http.Request)

* ServeHTTP() is to satisfy net/http.Handler -- all requests are simply forwarded through to httprouter

func (*API) Wrap

func (a *API) Wrap(child func(r *Request)) httprouter.Handle

* Wrap() reroutes a request to a standard httprouter.Handler (? double check) and converts it to the function signature that our entrypoint functions expect. It also initializes our panic handling and our thread pool handling.

type APIMountedRelationship

type APIMountedRelationship struct {
	SrcResourceName string
	DstResourceName string
	Name            string
	Relationship
	Authenticator
}

type APIMountedResource

type APIMountedResource struct {
	Name string
	Resource
	Authenticator
}

type Authenticator

type Authenticator interface {
	Authenticate(r *Request, permission, id string)
}

type AuthenticatorMany

type AuthenticatorMany struct {
	Authenticators []Authenticator
}

func NewAuthenticatorMany

func NewAuthenticatorMany(authenticators ...Authenticator) *AuthenticatorMany

func (*AuthenticatorMany) Authenticate

func (am *AuthenticatorMany) Authenticate(r *Request, permission, id string)

type Denaturer

type Denaturer interface {
	Denature() interface{}
}

type Done

type Done struct {
	sync.Mutex
	Chan      chan bool
	HasClosed bool
}

func NewDone

func NewDone() *Done

func (*Done) Close

func (d *Done) Close()

func (*Done) Wait

func (d *Done) Wait() chan bool

type ExecutableFuture

type ExecutableFuture struct {
	Future
	Request  *Request
	Children map[Relationship]*ExecutableFuture
	Input    chan *FutureRequest

	//Relationships map[Relationship]*ExecutableFuture
	ResponsibleFor []*ExecutableFuture
	Resource       *APIMountedResource
	Relationship   *APIMountedRelationship
}

ExecutableFuture.Children and ExecutableFuture.Relationships must be disjoint sets

func NewExecutableFuture

func NewExecutableFuture(r *Request, f Future) *ExecutableFuture

func (*ExecutableFuture) Build

func (*ExecutableFuture) CatchPanic

func (ef *ExecutableFuture) CatchPanic()

func (*ExecutableFuture) Defer

func (ef *ExecutableFuture) Defer()

func (*ExecutableFuture) Execute

func (ef *ExecutableFuture) Execute()

func (*ExecutableFuture) GetRequest

func (ef *ExecutableFuture) GetRequest() *FutureRequest

func (*ExecutableFuture) Go

func (ef *ExecutableFuture) Go(f func())

func (*ExecutableFuture) HandleRequest

func (ef *ExecutableFuture) HandleRequest(req *FutureRequest, cb func(*FutureResponse))

func (*ExecutableFuture) HandleResponse

func (ef *ExecutableFuture) HandleResponse(res *FutureResponse)

func (*ExecutableFuture) Optimize

func (ef *ExecutableFuture) Optimize()

func (*ExecutableFuture) PushChild

func (ef *ExecutableFuture) PushChild(r Relationship, f *ExecutableFuture)

func (*ExecutableFuture) Takeover

func (ef *ExecutableFuture) Takeover(fr *FutureRequest)

type Field

type Field struct {
	Field string
	Value string
}

type Future

type Future interface {
	ShouldCombine(Future) bool
	Combine(Future) error
	Work(ef *ExecutableFuture)
}

type FutureOutput

type FutureOutput struct {
	Parents         []*ExecutableFuture
	PrimaryData     Future
	PrimaryDataType OutputType
}

func (*FutureOutput) Combine

func (fo *FutureOutput) Combine(f Future) error

func (*FutureOutput) PushParent

func (fo *FutureOutput) PushParent(ef *ExecutableFuture)

func (*FutureOutput) ShouldCombine

func (fo *FutureOutput) ShouldCombine(f Future) bool

func (*FutureOutput) Work

func (fo *FutureOutput) Work(pf *ExecutableFuture)

type FutureOutputDelete

type FutureOutputDelete struct{}

func (*FutureOutputDelete) Combine

func (fo *FutureOutputDelete) Combine(f Future) error

func (*FutureOutputDelete) ShouldCombine

func (fo *FutureOutputDelete) ShouldCombine(f Future) bool

func (*FutureOutputDelete) Work

func (fo *FutureOutputDelete) Work(pf *ExecutableFuture)

type FutureRequest

type FutureRequest struct {
	Request  *Request
	Response chan *FutureResponse
	Kind     FutureRequestKind
}

func NewFutureRequest

func NewFutureRequest(r *Request, kind FutureRequestKind) *FutureRequest

func (*FutureRequest) GetResponse

func (fr *FutureRequest) GetResponse() (res *FutureResponse)

func (*FutureRequest) SendResponse

func (fr *FutureRequest) SendResponse(res *FutureResponse)

type FutureRequestKind

type FutureRequestKind interface{}

type FutureRequestKindDeleteByIds

type FutureRequestKindDeleteByIds struct {
	Ids []string
}

type FutureRequestKindFailure

type FutureRequestKindFailure struct {
	Response *FutureResponse
}

type FutureRequestKindFindByAnyFields

type FutureRequestKindFindByAnyFields struct {
	Fields []Field
}

type FutureRequestKindFindByIds

type FutureRequestKindFindByIds struct {
	Ids []string
}

type FutureRequestKindIdentity

type FutureRequestKindIdentity struct {
	Response FutureResponseKind
	Future
}

type FutureRequestedPanic

type FutureRequestedPanic struct{}

type FutureResponse

type FutureResponse struct {
	IsSuccess       bool
	Success         map[Future]FutureResponseKind
	Failure         []OError
	WaitForComplete chan bool
}

type FutureResponseKind

type FutureResponseKind interface {
}

type FutureResponseKindByFields

type FutureResponseKindByFields struct {
	IsSingle bool
	Records  map[Field][]*Record
}

func (*FutureResponseKindByFields) GetIsSingle

func (frkbf *FutureResponseKindByFields) GetIsSingle() bool

func (*FutureResponseKindByFields) GetRecords

func (frkbf *FutureResponseKindByFields) GetRecords() []*Record

type FutureResponseKindDeleted

type FutureResponseKindDeleted struct {
}

type FutureResponseKindWithRecords

type FutureResponseKindWithRecords interface {
	GetIsSingle() bool
	GetRecords() []*Record
}

type Ider

type Ider interface {
	Id() string
	SetId(string) error
}

type IncludeInstructions

type IncludeInstructions struct {
	Children map[string]*IncludeInstructions
	Include  []string
	Parent   *IncludeInstructions
}

func NewIncludeInstructions

func NewIncludeInstructions(rawinst string) *IncludeInstructions

func NewIncludeInstructionsEmpty

func NewIncludeInstructionsEmpty() *IncludeInstructions

func NewIncludeInstructionsFromRequest

func NewIncludeInstructionsFromRequest(r *http.Request) *IncludeInstructions

func (*IncludeInstructions) GetChild

func (ii *IncludeInstructions) GetChild(childname string) *IncludeInstructions

func (*IncludeInstructions) Push

func (ii *IncludeInstructions) Push(inst_rels []string)

func (*IncludeInstructions) ShouldFetch

func (ii *IncludeInstructions) ShouldFetch(rel string) bool

func (*IncludeInstructions) ShouldInclude

func (ii *IncludeInstructions) ShouldInclude(inst string) bool

type LeasedPromise

type LeasedPromise struct {
	Promise
	ChanRelease chan bool
}

func (*LeasedPromise) Release

func (sp *LeasedPromise) Release()

type Logger

type Logger interface {
	Debugf(fmt string, args ...interface{})
	Infof(fmt string, args ...interface{})
	Warnf(fmt string, args ...interface{})
	Errorf(fmt string, args ...interface{})
	Criticalf(fmt string, args ...interface{})
}

type LoggerDefault

type LoggerDefault struct {
	Output io.Writer
}

func NewLoggerDefault

func NewLoggerDefault(output io.Writer) *LoggerDefault

func (*LoggerDefault) Criticalf

func (l *LoggerDefault) Criticalf(format string, args ...interface{})

func (*LoggerDefault) Debugf

func (l *LoggerDefault) Debugf(format string, args ...interface{})

func (*LoggerDefault) Errorf

func (l *LoggerDefault) Errorf(format string, args ...interface{})

func (*LoggerDefault) GetCaller

func (l *LoggerDefault) GetCaller(depth int) string

func (*LoggerDefault) GetTime

func (l *LoggerDefault) GetTime() string

func (*LoggerDefault) Infof

func (l *LoggerDefault) Infof(format string, args ...interface{})

func (*LoggerDefault) PrepareArgs

func (l *LoggerDefault) PrepareArgs(args []interface{}) []interface{}

func (*LoggerDefault) Warnf

func (l *LoggerDefault) Warnf(format string, args ...interface{})

type OData

type OData interface{}

OData is an interface used to represent the various forms of the primary data in the Output object. Currently, OutputData is an empty interface, so the use of this is purely syntactical. As of writing, the objects written in this file alongside OutputData are intended to be used as OutputData.

type OError

type OError struct {
	Id     string        `json:"id,omitempty"`
	Href   string        `json:"href,omitempty"`
	Status string        `json:"status,omitempty"`
	Code   string        `json:"code,omitempty"`
	Title  string        `json:"title,omitempty"`
	Detail string        `json:"detail,omitempty"`
	Source *OErrorSource `json:"source,omitempty"`
	Meta   interface{}   `json:"meta,omitempty"`
}

func ErrorToOError

func ErrorToOError(err error) OError

type OErrorSource

type OErrorSource struct {
	Pointer   string `json:"pointer,omitempty"`
	Parameter string `json:"parameter,omitempty"`
}

type OMeta

type OMeta map[string]interface{}

type ORecords

type ORecords struct {
	IsSingle bool
	Records  []*Record
}

ORecords is a struct that satisfies OData, and represents a list of potentially many Records. A list of zero records is possible. Attempting to encode this object as JSON with a list of more than one Record when IsSingle = true will cause a panic.

func (ORecords) MarshalJSON

func (o ORecords) MarshalJSON() ([]byte, error)

type ORelationship

type ORelationship struct {
	IsSingle bool `json:"-"`
	//Links OLinks `json:"links,omitempty"`
	Data                   []OResourceIdentifier `json:"data"`
	RelationshipWasFetched bool
	Meta                   OMeta  `json:"meta,omitempty"`
	RelatedBase            string `json:"-"`
	RelationshipName       string `json:"-"`
}

func (*ORelationship) MarshalJSON

func (o *ORelationship) MarshalJSON() ([]byte, error)

type ORelationships

type ORelationships struct {
	Relationships []*ORelationship
}

func (*ORelationships) GetRelationshipByName

func (o *ORelationships) GetRelationshipByName(name string) *ORelationship

func (*ORelationships) MarshalJSON

func (o *ORelationships) MarshalJSON() ([]byte, error)

type OResourceIdentifier

type OResourceIdentifier struct {
	Id   string `json:"id"`
	Type string `json:"type"`
}

func GetRelationshipDifferences

func GetRelationshipDifferences(src, dst []OResourceIdentifier) (add, remove []OResourceIdentifier)

func GetResourceIdentifiers

func GetResourceIdentifiers(records []*Record) (out []OResourceIdentifier)

func NewResourceIdentifier

func NewResourceIdentifier(id, typ string) OResourceIdentifier

type Output

type Output struct {
	Data OData `json:"data,omitempty"`
	//Links *OutputLinks `json:"links, omitempty"`
	Included []*Record `json:"included,omitempty"`
	Errors   []OError  `json:"errors,omitempty"`
	Meta     OMeta     `json:"meta,omitempty"`
}

* Output is the primary output structure used by this framework. It is responsible for representing the root node of every spec-compliant response this framework can generate.

func NewOutput

func NewOutput() *Output

type OutputType

type OutputType int
const (
	OutputTypeResources OutputType = iota
	OutputTypeLinkages
)

type Paginator

type Paginator struct {
	ShouldPaginate bool
	CurPage        int
	LastPage       int
	MaxPerPage     int
}

func NewPaginator

func NewPaginator(r *Request) Paginator

type Promise

type Promise interface {
	Success(r *Request)
	Failure(r *Request)
}

type PromiseStorage

type PromiseStorage struct {
	Promises map[reflect.Type]chan PromiseStorageLease
	ChanGet  chan PromiseStorageLease
}

func NewPromiseStorage

func NewPromiseStorage() *PromiseStorage

func (*PromiseStorage) Defer

func (ps *PromiseStorage) Defer()

func (*PromiseStorage) Get

func (ps *PromiseStorage) Get(typ Promise, init func() Promise) LeasedPromise

func (*PromiseStorage) PromiseWorker

func (ps *PromiseStorage) PromiseWorker(p Promise) chan PromiseStorageLease

func (*PromiseStorage) Worker

func (ps *PromiseStorage) Worker()

type PromiseStorageLease

type PromiseStorageLease struct {
	Type         reflect.Type
	Initialize   func() Promise
	ChanResponse chan LeasedPromise
}

type Record

type Record struct {
	// exposed fields
	Type       string           `json:"type"`
	Id         string           `json:"id"`
	Attributes RecordAttributes `json:"attributes,omitempty"`
	//Links //TODO
	Relationships *ORelationships `json:"relationships,omitempty"`
	Meta          OMeta           `json:"meta,omitempty"`

	// internal fields for tracking
	ShouldInclude bool `json:"-"`
}

func ConvertInterfaceSliceToRecordSlice

func ConvertInterfaceSliceToRecordSlice(src interface{}) []*Record

func ParseJSONHelper

func ParseJSONHelper(v *Record, raw []byte, t reflect.Type) *Record

func (*Record) Denature

func (r *Record) Denature() interface{}

func (*Record) GetResourceIdentifier

func (r *Record) GetResourceIdentifier() OResourceIdentifier

func (*Record) HasFieldValue

func (r *Record) HasFieldValue(field Field) bool

func (*Record) PushRelationship

func (r *Record) PushRelationship(rel *ORelationship)

type RecordAttributes

type RecordAttributes interface{}

type RecordParserSimple

type RecordParserSimple struct {
	Data *Record `json:"data"`
}

type Relationship

type Relationship interface {
	IsSingle() bool
	PostMount(a *API)
	//VerifyLinks(r *Request, rec *Record, amr *APIMountedRelationship, rids []OResourceIdentifier) error
	//PreSave(r *Request, rec *Record, amr *APIMountedRelationship, rids []OResourceIdentifier) error
	//PostSave(r *Request, rec *Record, amr *APIMountedRelationship, rids []OResourceIdentifier) error
	//GetTargetFuture() Future
	Link(r *Request, src, dst *ExecutableFuture, input FutureResponseKind) (output FutureRequestKind)
	PushBackRelationships(r *Request, src, dst *ExecutableFuture, srcrk, dstrk FutureResponseKind)
}

RelationshipBehavior is a "base interface" children: IdRelationshipBehavior or a HasIdRelationshipBehavior

type RelationshipIdentity

type RelationshipIdentity struct {
	IsPrimary bool
}

func (*RelationshipIdentity) IsSingle

func (ri *RelationshipIdentity) IsSingle() bool
func (ri *RelationshipIdentity) Link(r *Request, src, dst *ExecutableFuture, input FutureResponseKind) (output FutureRequestKind)

func (*RelationshipIdentity) PostMount

func (ri *RelationshipIdentity) PostMount(a *API)

func (*RelationshipIdentity) PushBackRelationships

func (ri *RelationshipIdentity) PushBackRelationships(r *Request, src, dst *ExecutableFuture, input, output FutureResponseKind)

type RelationshipRequirement

type RelationshipRequirement int
const (
	Required RelationshipRequirement = iota
	NotRequired
)

type Request

type Request struct {
	HttpRequest         *http.Request
	HttpResponseWriter  http.ResponseWriter
	API                 *API
	Params              httprouter.Params
	IncludeInstructions *IncludeInstructions
	PromiseStorage      *PromiseStorage

	Done      *Done
	Responder chan *RunResponder
	// contains filtered or unexported fields
}

* Request is responsible for managing all of the common information between resources and relationships for the duration of a request. It contains references to often-needed components such as the raw net/http.Request, the API object, etc

func NewRequest

func NewRequest(a *API, httpreq *http.Request, httpres http.ResponseWriter, params httprouter.Params) *Request

* NewRequest() will return a populated instance of *Request. It will also initialize concurrency components.

func (*Request) CatchPanic

func (r *Request) CatchPanic()

func (*Request) Defer

func (r *Request) Defer()

* Defer() should be called in a defer call at the same point that a Request is initialized. It is responsible for the safe handling of responses

func (*Request) Failure

func (r *Request) Failure()

* Failure() is responsible for calling the appropriate failure handles. This function should never be called outside of a Responder

func (*Request) GetBaseURL

func (r *Request) GetBaseURL() string

* GetBaseURL() will provide the URL + URI for any arbitrary request such that curling the output of this function is the root API endpoint for requests to this instance of this framework.

func (*Request) HandlePanic

func (r *Request) HandlePanic(raw interface{})

* HandlePanic() is responsible for interpreting the object that was paniced, and replying with the appropriate answer.

func (*Request) InternalHandlePanic

func (r *Request) InternalHandlePanic(raw interface{}) (re Responder, is_valid bool)

func (*Request) Respond

func (r *Request) Respond(re Responder)

func (*Request) ResponderWorker

func (r *Request) ResponderWorker()

func (*Request) Send

func (r *Request) Send(obj interface{})

* Send() is responsible for converting a given *Output object to json, and sending it to the HttpResponseWriter that this Request is responsible for.

func (*Request) Success

func (r *Request) Success()

* Success() is responsible for calling the appropriate succcess handles. This function should never be called outside of a Responder

type RequestParams

type RequestParams struct {
	Paginator Paginator
}

type Resource

type Resource interface {
	GetFuture() Future
}

type Responder

type Responder interface {
	Respond(req *Request) error
}

type ResponderBase

type ResponderBase struct {
	Output  *Output
	Status  int
	Headers map[string][]string
	CB      func(r *Request)
}

func InsufficientPermissions

func InsufficientPermissions() *ResponderBase

func NewResponderBase

func NewResponderBase(status int, o *Output) *ResponderBase

func NewResponderBaseErrors

func NewResponderBaseErrors(code int, es ...error) *ResponderBase

func NewResponderErrorOperationNotSupported

func NewResponderErrorOperationNotSupported(desc string) *ResponderBase

func NewResponderErrorRelationshipDoesNotExist

func NewResponderErrorRelationshipDoesNotExist(relname string) *ResponderBase

func NewResponderErrorResourceDoesNotExist

func NewResponderErrorResourceDoesNotExist(relname string) *ResponderBase

func NewResponderForbidden

func NewResponderForbidden(e error) *ResponderBase

func NewResponderRecordCreate

func NewResponderRecordCreate(resource_str string, rec *Record, err error) *ResponderBase

TODO: rip this out and replace it with multiple responder functions... this function should not be internally resonsible for determining success or failure

func NewResponderResourceSuccessfullyDeleted

func NewResponderResourceSuccessfullyDeleted() *ResponderBase

func NewResponderUnimplemented

func NewResponderUnimplemented(e error) *ResponderBase

func TODO

func TODO() *ResponderBase

func Unimplemented

func Unimplemented() *ResponderBase

func (*ResponderBase) PushHeader

func (rb *ResponderBase) PushHeader(k, v string)

func (*ResponderBase) Respond

func (rb *ResponderBase) Respond(r *Request) error

type RunResponder

type RunResponder struct {
	Responder
	Done chan bool
}

Directories

Path Synopsis
old

Jump to

Keyboard shortcuts

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