services

package
v0.1.4 Latest Latest
Warning

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

Go to latest
Published: Mar 7, 2024 License: MIT Imports: 15 Imported by: 0

Documentation

Index

Constants

View Source
const (
	// GatewayTypeAPI marks a gateway as being an HTTP/RPC API gateway.
	GatewayTypeAPI = GatewayType("API")
	// GatewayTypeEvents marks a gateway as being event-sourced using publish/subscribe.
	GatewayTypeEvents = GatewayType("EVENTS")
)

Variables

This section is empty.

Functions

This section is empty.

Types

type ContentFileNameGetter

type ContentFileNameGetter interface {
	// ContentFileName returns the name of the file that should be used when downloading this stream.
	ContentFileName() string
}

ContentFileNameGetter is used to supply an optional Content-Disposition header, allowing you to customize the name of the file presented in download modals of browsers/clients.

type ContentFileNameSetter

type ContentFileNameSetter interface {
	// SetContentFileName applies the name of the file that should be used when downloading this stream.
	SetContentFileName(string)
}

ContentFileNameSetter is used to supply an optional Content-Disposition header, allowing you to customize the name of the file presented in download modals of browsers/clients.

type ContentGetter

type ContentGetter interface {
	// Content returns the stream of exact bytes to send back to the caller.
	Content() io.ReadCloser
}

ContentGetter provides a way for your service response to indicate that you want to return a raw stream of bytes rather than relying on our auto-encoding.

type ContentLengthGetter

type ContentLengthGetter interface {
	// ContentLength returns the total number of bytes you can/should read from the Content stream.
	ContentLength() int
}

ContentLengthGetter is used by raw response streams to indicate exactly how many bytes are in the response's Content stream.

type ContentLengthSetter

type ContentLengthSetter interface {
	// SetContentLength applies the total number of bytes you can/should read from the Content stream.
	SetContentLength(int)
}

ContentLengthSetter recaptures the custom content length header from raw responses when using the code-generate Go client for your services.

type ContentRangeGetter

type ContentRangeGetter interface {
	// ContentRange returns the individual values used to build a custom Content-Range header
	// when responding with a raw content stream. For more information on how these values are
	// used, please see: https://www.geeksforgeeks.org/http-headers-content-range/
	ContentRange() (start int, end int, size int)
}

ContentRangeGetter is used by raw response streams to indicate that this is resumable using the standard Range header.

type ContentRangeSetter

type ContentRangeSetter interface {
	// SetContentRange accepts the 3 standard components of the Content-Range header.
	SetContentRange(start int, end int, size int)
}

ContentRangeSetter recaptures the custom content range header from raw responses when using the code-generated Go client for your services.

type ContentSetter

type ContentSetter interface {
	// SetContent applies the stream of bytes that the response object should use when reading.
	SetContent(io.ReadCloser)
}

ContentSetter allows raw stream responses to be properly reconstituted when using the code-generated Go client for your service.

type ContentTypeGetter

type ContentTypeGetter interface {
	// ContentType returns the MIME encoding type of the raw content stream.
	ContentType() string
}

ContentTypeGetter is used by raw response streams to indicate what type of data is in the stream.

type ContentTypeSetter

type ContentTypeSetter interface {
	// SetContentType sets the MIME encoding type of the raw content stream.
	SetContentType(string)
}

ContentTypeSetter recaptures the custom content type header from raw responses when using the code-generated Go client for your service.

type Endpoint

type Endpoint struct {
	// ServiceName is the name of the service that this operation is part of.
	ServiceName string
	// Name is the name of the function/operation that this endpoint describes.
	Name string
	// Handler is the actual function that will trigger the work on then underlying
	// service to accomplish the work that this endpoint is intended to.
	Handler HandlerFunc
	// NewInput returns a pointer to the struct that we pass into the handler function
	// for this input (e.g. "*LoginRequest"). It is the request struct pointer.
	NewInput func() StructPointer
	// Roles helps support role-based security by defining role patterns to indicate which
	// users are allowed to access this endpoint.  For example:
	//
	//    endpoint.Roles = []string{
	//        "admin.write",
	//        "group.{Group.ID}.write",
	//    }
	//
	// Notices that the roles should be allowed to have path variables that we can fill in
	// at runtime with the incoming binding data.
	Roles []string
	// Routes defines the actual ingress routes that allow this service operation to
	// be invoked by various gateways. For instance, they tell you that you can invoke
	// the API call "GET /user/{ID}" to invoke it or that it should trigger when the
	// event "ON UserService.UserCreated" fires.
	Routes []EndpointRoute
}

Endpoint describes an operation on an underlying service that we expose through one of potentially multiple gateways. The Routes on this Endpoint indicate all of the ingress mechanisms available to invoke this operation.

func (Endpoint) QualifiedName

func (end Endpoint) QualifiedName() string

QualifiedName returns the fully-qualified name/identifier of this service operation. It is simply the formatted string "ServiceName.MethodName".

type EndpointRoute

type EndpointRoute struct {
	// GatewayType indicates the type of gateway that should service this route. For instance
	// if the type is "HTTP" then the API gateway should take care of it. If the type
	// is "EVENT" then we should let our pub-sub event source take care of it.
	GatewayType GatewayType
	// Method describes some sort of action/verb that describes this route. For API endpoints
	// it is the HTTP method (e.g. GET, PUT, POST, etc). For events it is "ON", and so forth.
	Method string
	// Path describes the actual unique routing path that the gateway should use to ensure
	// that requests get to this endpoint. For API endpoints, it's the request path
	// like "/user/{ID}" and for event endpoints, it's the subscription key like "FooService.Save".
	Path string
	// PathParams contains the names of the path variables/parameters you expect in the path of
	// this endpoint. For instance, the path "/user/{UserID}/transaction/{TransactionID}" would set
	// this slice to []string{"UserID", "TransactionID"}. This allows you to quickly bind only the
	// values you expect in the pattern.
	PathParams []string
	// Status is mainly used by API gateway routes to determine what HTTP status code we should
	// return to the caller when this endpoint succeeds. By default, this is 200.
	Status int
}

EndpointRoute defines an actual ingress route that allows a service operation to be invoked by various gateways. For instance, one route will tell you that you can invoke the API call "GET /user/{ID}" to invoke the method or that it should run when the event "ON UserService.UserCreated" fires.

type Gateway

type Gateway interface {
	// Type returns the identifier used to distinguish this gateway from others registered
	// for the same service.
	Type() GatewayType
	// Register adds an ingress handler for the given operation/endpoint to this gateway.
	Register(endpoint Endpoint, route EndpointRoute)
	// Listen causes the gateway to start accepting requests to invoke methods on the
	// underlying service. Implementations should attempt to follow these rules:
	//
	//   * Block on the current Goroutine. This should unblock on an abnormal interruption
	//     to the gateway's ability to continue processing or if Shutdown() has been
	//     called elsewhere.
	//   * The error should be 'nil' if nothing has gone wrong. This is different from the
	//     behavior of http.ListenAndServe() which returns a http.ErrServerClosed error
	//     even when things shut down as expected. Gateway instances should keep their
	//     whore mouths shut and only report an error when there's actually something
	//     to be concerned about.
	Listen() error
	// Shutdown should attempt to gracefully wind down processing of requests. Where
	// possible, you should use the context to determine if/when you should give up
	// on dealing with existing work. Implementations should try to follow these rules:
	//
	//   * Immediately stop accepting incoming requests.
	//   * Allow in-process requests to finish cleanly.
	//   * Abide any cancellation of the context and give up on existing requests.
	Shutdown(ctx context.Context) error
}

Gateway describes a way to execute operations on some underlying service. By default, service methods are closed off to all external processes, but gateways provide a protocol such as HTTP/RPC or PubSub to trigger them. How that actually happens is completely up to the Gateway implementation - the interface merely provides signals to start/stop the mechanism that accepts/processes function calls.

type GatewayMiddleware

type GatewayMiddleware interface {
	Gateway
	// Middleware are the functions that a Server should add to EVERY endpoint it registers.
	Middleware() MiddlewareFuncs
}

GatewayMiddleware allows gateway implementations to add special middleware to the standard execution pipeline for an endpoint. Your gateway would implement this when it has special functionality you want included in the execution of every endpoint regardless of which gateway is actually servicing it.

The canonical example for this is the event gateway. When a service method gets invoked, we want to publish a "Service.Method" event afterwards no matter what so that the rest of your system can be notified about the event. We don't care if it was an HTTP request that triggered the invocation or some other event handler. We just know that we want to publish an event no matter what. The event middleware can return a handler(s) that injects that behavior by hiding it behind a generic middleware function.

type GatewayType

type GatewayType string

GatewayType is a tagging value that gateways can use to classify themselves.

func (GatewayType) String

func (t GatewayType) String() string

String returns the raw string value for the type.

type HandlerFunc

type HandlerFunc func(ctx context.Context, req any) (any, error)

HandlerFunc is the general purpose signature for any endpoint handler.

type MiddlewareFunc

type MiddlewareFunc func(ctx context.Context, req any, next HandlerFunc) (any, error)

MiddlewareFunc is a function that can be used to decorate a service method/endpoint's handler.

type MiddlewareFuncs

type MiddlewareFuncs []MiddlewareFunc

MiddlewareFuncs is an ordered pipeline of operations that must occur before invoking a service method/endpoint's handler.

func (MiddlewareFuncs) Append

func (funcs MiddlewareFuncs) Append(mw ...MiddlewareFunc) MiddlewareFuncs

Append creates a new middleware function pipeline that runs the original handlers and then the additional ones specified by 'mw'.

func (MiddlewareFuncs) Then

func (funcs MiddlewareFuncs) Then(handler HandlerFunc) HandlerFunc

Then creates a single handler function that executes every operation in the middleware pipeline and terminates with the supplied handler.

type OnPanicFunc

type OnPanicFunc func(err error, stack []byte)

OnPanicFunc is the signature for custom callbacks to invoke when a panic occurs in your service code.

type Redirector

type Redirector interface {
	// Redirect returns the URI of an alternate resource that will provide the final data
	// we want this endpoint to return.
	Redirect() string
}

Redirector provides a way to tell gateways that the response value doesn't contain the raw byte stream we want to deliver. Instead, you should redirect to that URI to fetch the response data.

This indicates the redirect is temporary, and you should probably continue to use the same endpoint address in the future. You'd probably use this more in cases such as redirecting to a file on S3; something that will be different each time.

GATEWAY COMPATABILITY: This currently only works with the API gateway. When delivering/receiving responses through other gateways such as "Events", your response will be auto-encoded just like it was a normal struct/value. As a result, your response should continue to maintain exported fields that you would like to transport in those cases.

type RedirectorPermanent

type RedirectorPermanent interface {
	// RedirectPermanent returns the URI of an alternate resource that will provide the final data
	// we want this endpoint to return.
	RedirectPermanent() string
}

RedirectorPermanent provides a way to tell gateways that the response value doesn't contain the raw byte stream we want to deliver. Instead, you should redirect to that URI to fetch the response data.

This indicates that the redirect is permanent, and you should probably start using the redirected URI moving forward. You'd probably use this more in a situation where you are deprecating one API endpoint in favor of another. The old endpoint could redirect to the new endpoint to maintain backwards compatability, but you really should start using the new one.

GATEWAY COMPATABILITY: This currently only works with the API gateway. When delivering/receiving responses through other gateways such as "Events", your response will be auto-encoded just like it was a normal struct/value. As a result, your response should continue to maintain exported fields that you would like to transport in those cases.

type Server

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

Server is the primordial component that wrangles all of your services and gateways to get them talking to each other. You should not create one of these yourself. Instead, you should use the NewServer() constructor to do that for you.

func NewServer

func NewServer(options ...ServerOption) *Server

NewServer creates a new container that encapsulates one or more gateways and services. It helps set up endpoint routes and manages startup/shutdown routines so that you can start/stop accepting service requests.

Example:

calcHandler := calc.CalculatorServiceHandler{}
calcServer := gen.NewCalculatorService(calcHandler)

server := services.NewServer(
	services.Listen(apis.NewGateway()),
	services.Listen(events.NewGateway()),
	services.Register(calcServer),
)

func (*Server) Invoke

func (server *Server) Invoke(ctx context.Context, serviceName string, methodName string, req any) (any, error)

Invoke allows you to manually trigger any registered service endpoint/function given the name of the service/method. I'd suggest you stick to using the generated clients to invoke functions on your services rather than using this. This primarily exists to aid in testing - it's not really mean to be used in production code. But hey, you're an adult. Do what you want.

func (*Server) Run

func (server *Server) Run() error

Run turns on every gateway currently assigned to this service runtime. Call this once your service setup and registration is complete in order to start accepting incoming requests through your gateway(s).

func (*Server) Shutdown

func (server *Server) Shutdown(ctx context.Context) error

Shutdown attempts to gracefully shut down all of the gateways associated with this service runtime. It should immediately stop accepting new requests and then wait for existing requests to finish before returning. The context should be used to provide a cancellation/timeout to limit how long this will wait for in-flight requests to finish up.

func (*Server) ShutdownOnInterrupt

func (server *Server) ShutdownOnInterrupt(gracefulTimeout time.Duration)

ShutdownOnInterrupt provides some convenience around shutting down this service. This function will block until the process either receives a SIGTERM or SIGINT signal. At that point, it will invoke Shutdown() whose context will have a deadline of the given duration.

Example:

// Ignore the bad (non-existent) error handling, but here's how your server
// setup/teardown code looks using ShutdownOnInterrupt. The server will start
// up and run until the process gets a SIGINT/SIGTERM signal. At that point, it
// will give all in-process requests in all gateways 10 seconds to finish.
void main() {
	server := services.NewServer(...)
	go server.ShutdownOnInterrupt(10*time.Second)
	server.Run()
}

type ServerOption

type ServerOption func(*Server)

ServerOption defines a setting that you can change on a services.Server while setting up your application in main().

func Listen

func Listen(gw Gateway) ServerOption

Listen adds another gateway to the server. You can supply this option more than once in order to provide multiple types of gateways. For instance, you can call it once to provide settings for an API/HTTP gateway and again to provide settings for an event source gateway.

func OnPanic

func OnPanic(handler OnPanicFunc) ServerOption

OnPanic provides a custom callback that will let you log/observe the error/stack from any panic that occurred in your service method code.

func Register

func Register(services ...*Service) ServerOption

Register adds endpoint handlers for the given service(s) to the appropriate gateways. Typically, you don't create the Service pointer yourself. These are built for you when you use the code generation tools to build the gateways based on your service interfaces.

Consider this call akin to adding routes to an HTTP router. This just adds routes to every applicable gateway in your runtime server.

type Service

type Service struct {
	// Name is the name of the service interface that we're building a server for.
	Name string
	// Version is an optional version identifier for this service.
	Version string
	// Handler is a reference to the actual service handler struct you provided during
	// setup of the gateway runtime in main.
	Handler any
	// Endpoints contains registration/execution information for every method/operation
	// that the service exposes.
	Endpoints []Endpoint
}

Service encapsulates your hand-implemented service handler and includes all of the endpoint registration information required to power our runtime gateways.

func (Service) Endpoint

func (svc Service) Endpoint(name string) (Endpoint, bool)

Endpoint looks up the operation info for a method given its name. This returns the matching endpoint value and an 'ok' boolean similar to a map lookup.

type StreamRequest

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

StreamRequest implements all of the ContentXxx and SetContentXxx methods that we support and look at when we look at streaming/upload style requests.

type FileUploadRequest struct {
	services.StreamRequest
}

func (res *ImageDownloadResponse) Init(file os.File, info fs.FileInfo) {
	res.SetContent(file)
	res.SetContentType("image/png")
	res.SetContentLength(info.Size())
}

type StreamResponse

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

StreamResponse implements all of the ContentXxx and SetContentXxx methods that we support. You can embed one of these structs in your response struct to automatically gain the ability to respond with raw data streams rather than auto-encoding.

type ImageDownloadResponse struct {
	services.StreamResponse
}

func (res *ImageDownloadResponse) Init(file os.File, info fs.FileInfo) {
	res.SetContent(file)
	res.SetContentType("image/png")
	res.SetContentLength(info.Size())
}

func (*StreamResponse) Content

func (res *StreamResponse) Content() io.ReadCloser

Content returns the raw byte stream representing the data returned by the endpoint.

func (*StreamResponse) ContentFileName

func (res *StreamResponse) ContentFileName() string

ContentFileName returns the name of the file the client should use to download the stream.

func (*StreamResponse) ContentLength

func (res *StreamResponse) ContentLength() int

ContentLength returns the number of bytes you can read from the content stream.

func (*StreamResponse) ContentRange

func (res *StreamResponse) ContentRange() (start int, end int, size int)

ContentRange returns non-zero values if this resource supports the ability to resume downloads.

func (*StreamResponse) ContentType

func (res *StreamResponse) ContentType() string

ContentType returns the MIME content type string describe the type of data in the stream.

func (*StreamResponse) SetContent

func (res *StreamResponse) SetContent(content io.ReadCloser)

SetContent applies the raw byte stream representing the data returned by the endpoint.

func (*StreamResponse) SetContentFileName

func (res *StreamResponse) SetContentFileName(contentFileName string)

SetContentFileName sets the name of the file the client should use to download the stream.

func (*StreamResponse) SetContentLength

func (res *StreamResponse) SetContentLength(contentLength int)

SetContentLength sets the number of bytes the caller should read from the content stream.

func (*StreamResponse) SetContentRange

func (res *StreamResponse) SetContentRange(start int, end int, size int)

SetContentRange applies the attributes related to controlling resumable downloads.

func (*StreamResponse) SetContentType

func (res *StreamResponse) SetContentType(contentType string)

SetContentType applies the MIME content type that describes the data in the stream.

type StructPointer

type StructPointer any

StructPointer is a tagging type used to indicate either a pointer to the "request" struct to service methods or a pointer to the "response" struct of the method.

Directories

Path Synopsis
gateways

Jump to

Keyboard shortcuts

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