server

package
v1.14.0-RC2 Latest Latest
Warning

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

Go to latest
Published: Oct 7, 2022 License: Apache-2.0 Imports: 25 Imported by: 0

Documentation

Index

Constants

This section is empty.

Variables

Functions

func ConfigureAndStartHttpServer

func ConfigureAndStartHttpServer(
	lc fx.Lifecycle,
	config Configuration,
	logger *zap.SugaredLogger,
	ms *metrics.Metrics,
	serverControllers controllers,
	managementControllers managementControllers,
	ps *iam.ArmoryCloudPrincipalService,
) error

Types

type APIError

type APIError struct {
	// Code The business/project error code this instance represents (not to be confused with the HTTP status code which can be retrieved via HttpStatusCode).
	// This should never change for a given error so clients can rely on it and write code against it.
	Code int
	// Message the message that will be displayed the Client
	Message string
	// Metadata that about the error that will be returned to the client
	Metadata map[string]any
	// HttpStatusCode defaults to http.StatusInternalServerError if not overridden
	HttpStatusCode int
}

APIError an error that gets embedded in ResponseContract when an error response is return to the client

type AuthZValidatorFn

type AuthZValidatorFn func(p *iam.ArmoryCloudPrincipal) (string, bool)

AuthZValidatorFn a function that takes the authenticated principal and returns whether the principal is authorized. return true if the user is authorized return false if the user is NOT authorized and a string indicated the reason.

type Configuration

type Configuration struct {
	HTTP       http.HTTP
	Management http.HTTP
}

type Controller

type Controller struct {
	fx.Out
	Controller IController `group:"server"`
}

Controller the expected way of defining endpoint collections for an Armory application See the bellow example and IController, IControllerPrefix, IControllerAuthZValidator for options

EX:

package controllers

import (
	"context"
	"github.com/armory-io/go-commons/server"
	"github.com/armory-io/sccp/internal/sccp/k8s"
	"go.uber.org/zap"
	"net/http"
)

type ClusterController struct {
	log *zap.SugaredLogger
	k8s *k8s.Service
}

func NewClusterController(
	log *zap.SugaredLogger,
	k8sService *k8s.Service,
) server.Controller {
	return server.Controller{
		Controller: &ClusterController{
			log: log,
			k8s: k8sService,
		},
	}
}

func (c *ClusterController) Prefix() string {
	return "/clusters"
}

func (c *ClusterController) Handlers() []server.Handler {
	return []server.Handler{
		server.NewHandler(c.createClusterHandler, server.HandlerConfig{
			Method: http.MethodPost,
		}),
	}
}

type (
	createClusterRequest struct {
		AgentIdentifier string `json:"agentIdentifier" validate:"required,min=3,max=255"`
		ClientId        string `json:"clientId" validate:"required"`
		ClientSecret    string `json:"clientSecret" validate:"required"`
	}
	createClusterResponse struct {
		ClusterId string `json:"clusterId"`
	}
)

func (c *ClusterController) createClusterHandler(
	_ context.Context,
	req createClusterRequest,
) (*server.Response[createClusterResponse], server.Response) {
	id, err := c.k8s.CreateCluster(req.AgentIdentifier, req.ClientId, req.ClientSecret)

	if err != nil {
		return nil, server.NewErrorResponseFromApiError(server.APIError{
			Message: "Failed to create sandbox cluster",
		}, server.WithCause(err))
	}

	return server.SimpleResponse(createClusterResponse{
		ClusterId: id,
	}), nil
}

type Error

type Error interface {
	// Errors The collection of APIError associated with this instance.
	Errors() []APIError
	// ExtraDetailsForLogging Any extra details you want logged when this error is handled. Will never be null, but might be empty. NOTE: This will always be a mutable list so it can be modified at any time.
	ExtraDetailsForLogging() []KVPair
	// ExtraResponseHeaders Any extra headers you want sent to the origin when this error is handled.
	ExtraResponseHeaders() []KVPair
	// StackTraceLoggingBehavior Allows users to override the default behavior (logging stack traces for 5xx errors but not 4xx errors) and instead force stack trace on/off if they want to override the default 4xx vs. 5xx decision behavior.
	StackTraceLoggingBehavior() StackTraceLoggingBehavior
	// Message The error msg for logging, this will NOT be part of the server response
	Message() string
	// Cause The cause of the API error
	Cause() error
	// Stacktrace The stacktrace of the error
	Stacktrace() string
	// Origin the origination of the API error
	Origin() string
	// ToErrorResponseContract converts the Error into a ResponseContract
	ToErrorResponseContract(errorId string) ResponseContract
}

Error You'll want to create an instance using the NewErrorResponseFromApiError or NewErrorResponseFromApiErrors. e.g.

NewErrorResponseFromApiError(someError,
	withErrorMessage("super useful message")
)

NewErrorResponseFromApiErrors(
	[]error.APIError{someError,otherError},
	withErrorMessage("super useful message")
)

The generator functions accept many Option's that directly affect the response and what is logged, so take a close look at the available options.

func NewErrorResponseFromApiError

func NewErrorResponseFromApiError(error APIError, opts ...Option) Error

NewErrorResponseFromApiError Given a Single APIError and the given Option's returns an instance of Error

func NewErrorResponseFromApiErrors

func NewErrorResponseFromApiErrors(errors []APIError, opts ...Option) Error

NewErrorResponseFromApiErrors Given multiple APIError's and the given Option's returns an instance of Error

type Handler

type Handler interface {
	GetGinHandlerFn(log *zap.SugaredLogger, v *validator.Validate, handler *handlerDTO) gin.HandlerFunc
	Config() HandlerConfig
}

Handler The handler interface Instances of this interface should only ever be created by NewHandler, which happens automatically during server initialization The expected way that handlers are created is by creating a provider that provides an instance of Controller

func NewHandler

func NewHandler[REQUEST, RESPONSE any](f func(ctx context.Context, request REQUEST) (*Response[RESPONSE], Error), config HandlerConfig) Handler

NewHandler creates a Handler from a handler function and server.HandlerConfig

type HandlerConfig

type HandlerConfig struct {
	// Path The path or sub-path if a root path is set on the controller that the handler will be served on
	Path string
	// Method The HTTP method that the handler will be served on
	Method string
	// Consumes The content-type that the handler consumes, defaults to application/json
	Consumes string
	// Produces The content-type that the handler produces/offers, defaults to application/json
	Produces string
	// Default denotes that the handler should be used when the request doesn't specify a preferred Media/MIME type via the Accept header
	// Please note that one and only one handler for a given path/method combo can be marked as default, else a runtime error will be produced.
	Default bool
	// StatusCode The default status code to return when the request is successful, can be overridden by the handler by setting Response.StatusCode in the handler
	StatusCode int
	// AuthOptOut Set this to true if the handler should skip AuthZ and AuthN, this will cause the principal to be nil in the request context
	AuthOptOut bool
	// AuthZValidator see AuthZValidatorFn
	AuthZValidator AuthZValidatorFn
}

HandlerConfig config that configures a handler AKA an endpoint

type IController

type IController interface {
	Handlers() []Handler
}

IController baseController the base IController interface, all controllers must impl this via providing an instance of Controller or ManagementController

type IControllerAuthZValidator

type IControllerAuthZValidator interface {
	AuthZValidator(p *iam.ArmoryCloudPrincipal) (string, bool)
}

IControllerAuthZValidator an IController can implement this interface to apply a common AuthZ validator to all exported handlers

type IControllerPrefix

type IControllerPrefix interface {
	Prefix() string
}

IControllerPrefix an IController can implement this interface to have all of its handler func paths prefixed with a common path partial

type KVPair

type KVPair struct {
	Key   string
	Value string
}

type ManagementController

type ManagementController struct {
	fx.Out
	Controller IController `group:"management"`
}

ManagementController the same as Controller but the controllers in this group can be optionally configured to run on a separate port than the server controllers

type Option

type Option func(aE *apiErrorResponse)

func WithCause

func WithCause(err error) Option

WithCause The given error will be stored and used in logging and ultimately become Error.Cause.

func WithErrorMessage

func WithErrorMessage(message string) Option

WithErrorMessage The error message for logging, this will NOT be part of the server response. Could be used as context for what went wrong if the API errors aren't self-explanatory. Will ultimately become Error.Message.

func WithExtraDetailsForLogging

func WithExtraDetailsForLogging(extraDetailsForLogging ...KVPair) Option

WithExtraDetailsForLogging Adds the given logging details to what will ultimately become Error.ExtraDetailsForLogging.

func WithExtraResponseHeaders

func WithExtraResponseHeaders(extraResponseHeaders ...KVPair) Option

WithExtraResponseHeaders Adds the given response headers to what will ultimately become Error.ExtraResponseHeaders.

func WithStackTraceLoggingBehavior

func WithStackTraceLoggingBehavior(behavior StackTraceLoggingBehavior) Option

WithStackTraceLoggingBehavior Sets the given StackTraceLoggingBehavior for what will ultimately become Error.StackTraceLoggingBehavior.

type RequestDetails

type RequestDetails struct {
	// Headers the headers sent along with the request
	Headers http.Header
	// QueryParameters the decoded well-formed query params from the request
	// always a non-nil map containing all the valid query parameters found
	QueryParameters map[string][]string
	// PathParameters The map of path parameters if specified in the request configuration
	// ex: path: if the path was defined as "/customer/:id" and the request was for "/customer/foo"
	// PathParameters["id"] would equal "foo"
	PathParameters map[string]string
}

RequestDetails use server.GetRequestDetailsFromContext to get this out of the request context

func GetRequestDetailsFromContext

func GetRequestDetailsFromContext(ctx context.Context) (*RequestDetails, error)

GetRequestDetailsFromContext fetches the server.RequestDetails from the context

type Response

type Response[T any] struct {
	StatusCode int
	Headers    map[string][]string
	Body       T
}

Response The response wrapper for a handler response to be return to the client of the request StatusCode If set then it takes precedence to the default status code for the handler. Headers Any values set here will be added to the response sent to the client, if Content-Type is set here then

it will override the value set in HandlerConfig.Produces

func SimpleResponse

func SimpleResponse[T any](body T) *Response[T]

SimpleResponse a convenience function for wrapping a body in a response struct with defaults Use this if you do not need to supply custom headers or override the handlers default status code

type ResponseContract

type ResponseContract struct {
	ErrorId string                     `json:"error_id"`
	Errors  []ResponseContractErrorDTO `json:"errors"`
}

ResponseContract the strongly typed error contract that will be returned to the client if a request is not successful

type ResponseContractErrorDTO

type ResponseContractErrorDTO struct {
	Message  string         `json:"message"`
	Metadata map[string]any `json:"metadata,omitempty"`
	Code     string         `json:"code"`
}

type StackTraceLoggingBehavior

type StackTraceLoggingBehavior int
const (
	DeferToDefaultBehavior StackTraceLoggingBehavior = iota
	ForceStackTrace
	ForceNoStackTrace
)

type Void

type Void struct{}

Void an empty struct that can be used as a placeholder for requests/responses that do not have a body

Jump to

Keyboard shortcuts

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