route

package
v0.0.0-...-29e6843 Latest Latest
Warning

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

Go to latest
Published: Jul 20, 2018 License: Apache-2.0 Imports: 36 Imported by: 0

Documentation

Overview

Adding a Route

Adding a new route to the REST v2 API requires creation of a few structs and implementation of a few new methods.

RequestHandler

The RequestHandler is a central interface in the REST v2 API with following signature:

type RequestHandler interface {
	Handler() RequestHandler
	ParseAndValidate(*http.Request) error
	Execute(data.Connector) (ResponseData, error)
}

RequestHandlers should be placed in files in the route/ directory depending on the type of resource they access.

To add a new route you must create a struct that implements its three main interface methods. The Handler method must return a new copy of the RequestHandler so that a new copy of this object can be used on successive calls to the same endpoint.

The ParseAndValidate method is the only method that has access to the http.Request object. All necessary query parameters and request body information must be fetched from the request in this function and added to the struct for use in the Execute function. These fetches can take a few main forms:

From mux Context

Data gathered before the main request by the PrefetchFunc's are attached to the mux Context for that request and can be fetched from request's context and providing it with the correct key for the desired data.

From the Route Variables

Variables from routes defined with variables such as /tasks/{task_id} can be fetched using calls to the gimlet.GetVars function and providing the variable name to the returned map. For example, the taskId of that route could be fetched using:

gimlet.GetVars(r)["task_id"]

From the URL Query Parameters

To fetch variables from the URL query parameters, get it from the http.Request's URL object using:

r.URL.Query().Get("status")

Finally, the Execute method is the only method with access to the Connector and is therefore capable of making calls to the backing database to fetch and alter its state. The Execute method should use the parameters gathered in the ParseAndValidate method to implement the main logic and functionality of the request.

Pagination

PaginationExecutor is a struct that already implements the RequestHandler interface. To create a method with pagination, the only function that is needed is a PaginatorFunc.

PaginatorFunc

A PaginatorFunc defines how to paginate over a resource given a key to start pagination from and a limit to limit the number of results. PaginatorFunc has the following signature:

func(key string, limit int, args interface{}, sc Connector)([]Model, *PageResult, error)

The key and limit are fetched automatically by the PaginationExecutor's ParseAndValidate function. These parameters should be used to query for the correct set of results.

The args is a parameter that may optionally be used when more information is needed to completed the request. To populate this field, the RequestHandler that wraps the PaginationExecutor must implement a ParseAndValidate method that overwrites the PaginationExecutor's and then calls it with the resulting request for example, a RequestHandler called fooRequestHandler that needs additional args would look like:

fooRequestHandler{
 *PaginationExecutor
}

extraFooArgs{
	extraParam string
}

func(f *fooRequesetHandler) ParseAndValidate(r *http.RequestHandler) error{
	urlParam := r.URL.Query().Get("extra_url_param")
	f.PaginationExecutor.Args = extraFooArgs{urlParam}

	return f.PaginationExecutor.ParseAndValidate(r)
}

func fooRequestPaginator(key string, limit int, args interface{},
	 sc data.Connector)([]model.Model, *PageResult, error){

	 fooArgs, ok := args.(extraFooArgs)
	 if !ok {
		// Error
	 }

...
}

PageResult

The PageResult is a struct that must be constructed and returned by a PaginatorFunc It contains the information used for creating links to the next and previous page of results.

To construct a Page, you must provide it with the limit of the number of results for the page, which is either the default limit if none was provided, the limit of the previous request if provided, or the number of documents between the page and the end of the result set. The end of the result set is either the beginning of set of results currently being returned if constructing a previous page, or the end of all results if constructing a next page.

The Page must also contain the key of the item that begins the Page of results. For example, when creating a next Page when fetching a page of 100 tasks, the task_id of the 101st task should be used as the key for the next Page.

If the page being returned is the first or last page of pagination, then there is no need to create that Page.

MethodHandler

The MethodHandler type contains all data and types for complete execution of an API method. It holds:

- A list of PrefetchFuncs used for grabbing data needed before the main execution of a method, such as user data for authentication

- The HTTP method type (GET, PUT, etc.)

- The Authenticator this method uses to control access to its data

- The RequestHandler this method uses for the main execution of its request

A MethodHandler is a composition of defined structs and functions that in total comprise the method. Many Authenticator and PrefetchFunc are already implemented and only need to be attached to this object to create the method once the Requesthandler is complete.

RouteManager

The RouteManager type holds all of the methods associated with a particular API route. It holds these as an array of MethodHandlers. It also contains the path by which this route may be accessed, and the version of the API that this is implemented as part of.

When adding to the API there may already be a RouteManger in existence for the method being devloped. For example, if a method for GET /tasks/<task_id> is already implemented, then a new route manager is not required when createing POST /tasks/<task_id>. Its implementation only needs to be added to the existing RouteManager.

Once created, the RouteManager must be registered onto the mux.Router with the Connector by calling

route.Register(router, serviceContext).

Index

Constants

View Source
const (
	// Key value used to map user and project data to request context.
	// These are private custom types to avoid key collisions.
	RequestContext requestContextKey = 0
)

Variables

This section is empty.

Functions

func AttachHandler

func AttachHandler(app *gimlet.APIApp, queue amboy.Queue, URL string, superUsers []string, githubSecret []byte)

AttachHandler attaches the api's request handlers to the given mux router. It builds a Connector then attaches each of the main functions for the api to the router.

func GetHandler

func GetHandler(app *gimlet.APIApp, sc data.Connector, queue amboy.Queue, githubSecret []byte)

GetHandler builds each of the functions that this api implements and then registers them on the given router. It then returns the given router as an http handler which can be given more functions.

func GetProjectContext

func GetProjectContext(ctx context.Context) *model.Context

GetProjectContext returns the project context associated with a given request.

func MustHaveProjectContext

func MustHaveProjectContext(ctx context.Context) *model.Context

MustHaveProjectContext returns the project context set on the http request context. It panics if none is set.

func MustHaveUser

func MustHaveUser(ctx context.Context) *user.DBUser

MustHaveUser returns the user associated with a given request or panics if none is present.

func PrefetchProjectContext

func PrefetchProjectContext(ctx context.Context, sc data.Connector, r *http.Request) (context.Context, error)

PrefetchProjectContext gets the information related to the project that the request contains and fetches the associated project context and attaches that to the request context.

Types

type Authenticator

type Authenticator interface {
	Authenticate(context.Context, data.Connector) error
}

Authenticator is an interface which defines how requests can authenticate against the API service.

type MethodHandler

type MethodHandler struct {
	// PrefetchFunctions is a list of functions to be run before the main request
	// is executed.
	PrefetchFunctions []PrefetchFunc
	// MethodType is the HTTP Method Type that this handler will handler.
	// POST, PUT, DELETE, etc.
	MethodType string

	Authenticator
	RequestHandler
}

MethodHandler contains all of the methods necessary for completely processing an API request. It contains an Authenticator to control access to the method and a RequestHandler to perform the required work for the request.

type NoAuthAuthenticator

type NoAuthAuthenticator struct{}

NoAuthAuthenticator is an authenticator which allows all requests to pass through.

func (*NoAuthAuthenticator) Authenticate

func (n *NoAuthAuthenticator) Authenticate(ctx context.Context, sc data.Connector) error

Authenticate does not examine the request and allows all requests to pass through.

type Page

type Page struct {
	Relation string
	Key      string
	Limit    int
}

Page contains the information about a single page of the resource.

type PageResult

type PageResult struct {
	Next *Page
	Prev *Page
}

PageResult is a type that holds the two pages that pagination handlers create

type PaginationExecutor

type PaginationExecutor struct {
	// KeyQueryParam is the query param that a PaginationExecutor
	// expects to hold the key to use for fetching results.
	KeyQueryParam string
	// LimitQueryParam is the query param that a PaginationExecutor
	// expects to hold the limit to use for fetching results.
	LimitQueryParam string

	// Paginator is the function that a PaginationExector uses to
	// retrieve results from the service layer.
	Paginator PaginatorFunc

	// Args contain all additional arguments that will be passed to the paginator
	// function.
	Args interface{}
	// contains filtered or unexported fields
}

PaginationExecutor is a struct that handles gathering necessary information for pagination and handles executing the pagination. It is designed to be embedded into a request handler to completely handler the execution of endpoints with pagination.

func (*PaginationExecutor) Execute

Execute serves as an implementation of the RequestHandler's 'Execute' method. It calls the embedded PaginationFunc and then processes and returns the results.

func (*PaginationExecutor) ParseAndValidate

func (pe *PaginationExecutor) ParseAndValidate(_ context.Context, r *http.Request) error

ParseAndValidate gets the key and limit from the request and sets them on the PaginationExecutor.

type PaginationMetadata

type PaginationMetadata struct {
	Pages *PageResult

	KeyQueryParam   string
	LimitQueryParam string
}

PaginationMetadata is a struct that contains all of the information for creating the next and previous pages of data.

func ParsePaginationHeader

func ParsePaginationHeader(header, keyQueryParam,
	limitQueryParam string) (*PaginationMetadata, error)

ParsePaginationHeader creates a PaginationMetadata using the header that a paginator creates.

func (*PaginationMetadata) MakeHeader

func (pm *PaginationMetadata) MakeHeader(w http.ResponseWriter, apiURL, route string) error

MakeHeader builds a list of links to different pages of the same resource and writes out the result to the ResponseWriter. As per the specification, the header has the form of: Link:

		http://evergreen.mongodb.com/{route}?key={key}&limit={limit}; rel="{relation}"
   http://...

type PaginatorFunc

type PaginatorFunc func(string, int, interface{}, data.Connector) ([]model.Model, *PageResult, error)

PaginatorFunc is a function that handles fetching results from the service layer. It takes as parameters a string which is the key to fetch starting from, and an int as the number of results to limit to.

type PrefetchFunc

type PrefetchFunc func(context.Context, data.Connector, *http.Request) (context.Context, error)

PrefetchFunc is a function signature that defines types of functions which may be used to fetch data before the main request handler is called. They should fetch data using data.Connector set them on the request context.

type ProjectAdminAuthenticator

type ProjectAdminAuthenticator struct{}

ProjectOwnerAuthenticator only allows the owner of the project and superusers access to the information. It requires that the project be available and that the user also be set.

func (*ProjectAdminAuthenticator) Authenticate

func (p *ProjectAdminAuthenticator) Authenticate(ctx context.Context, sc data.Connector) error

ProjectAdminAuthenticator checks that the user is either a super user or is part of the project context's project admins.

type RequestHandler

type RequestHandler interface {
	// Handler defines how to fetch a new version of this handler.
	Handler() RequestHandler

	// ParseAndValidate defines how to retrieve the needed parameters from the HTTP
	// request. All needed data should be retrieved during the parse function since
	// other functions do not have access to the HTTP request.
	ParseAndValidate(context.Context, *http.Request) error

	// Execute performs the necessary work on the evergreen backend and returns
	// an API model to be surfaced to the user.
	Execute(context.Context, data.Connector) (ResponseData, error)
}

RequestHandler is an interface that defines how to process an HTTP request against an API resource.

type RequireUserAuthenticator

type RequireUserAuthenticator struct{}

RequireUserAuthenticator requires that a user be attached to a request.

func (*RequireUserAuthenticator) Authenticate

func (rua *RequireUserAuthenticator) Authenticate(ctx context.Context, sc data.Connector) error

Authenticate checks that a user is set on the request. If one is set, it is because PrefetchUser already set it, which checks the validity of the APIKey, so that is no longer needed to be checked.

type ResponseData

type ResponseData struct {
	// Result is the resulting API models that the API request needs to return
	// to the user, either because they were queried for or because they were
	// created by this request.
	Result []model.Model

	// Metadata is an interface that holds any additional data that the handler
	// will need for encoding the API response.
	Metadata interface{}
}

ResponseData holds the information that the handler function will need to form its encoded response. A ResponseData is generated by a RequestHandler's Execute function and parsed in the main handler method.

type RouteManager

type RouteManager struct {
	// Methods is a slice containing all of the http methods (PUT, GET, DELETE, etc.)
	// for this route.
	Methods []MethodHandler

	// Route is the path in the url that this resource handles.
	Route string

	// Version is the version number of the API that this route is associated with.
	Version int
}

Route defines all of the functioning of a particular API route. It contains implementations of the various API methods that are defined on this endpoint.

func (*RouteManager) Register

func (rm *RouteManager) Register(app *gimlet.APIApp, sc data.Connector)

Register builds http handlers for each of the defined methods and attaches these to the given router.

type SuperUserAuthenticator

type SuperUserAuthenticator struct{}

SuperUserAuthenticator only allows user in the SuperUsers field of the settings file to complete the request

func (*SuperUserAuthenticator) Authenticate

func (s *SuperUserAuthenticator) Authenticate(ctx context.Context, sc data.Connector) error

Authenticate fetches the user information from the http request and checks if it matches the users in the settings file. If no SuperUsers exist in the settings file, all users are considered super. It returns 'NotFound' errors to prevent leaking sensitive information.

type TaskExecutionPatchHandler

type TaskExecutionPatchHandler struct {
	Activated *bool  `json:"activated"`
	Priority  *int64 `json:"priority"`
	// contains filtered or unexported fields
}

TaskExecutionPatchHandler implements the route PATCH /task/{task_id}. It fetches the changes from request, changes in activation and priority, and calls out to functions in the data to change these values.

func (*TaskExecutionPatchHandler) Execute

Execute sets the Activated and Priority field of the given task and returns an updated version of the task.

func (*TaskExecutionPatchHandler) Handler

func (*TaskExecutionPatchHandler) ParseAndValidate

func (tep *TaskExecutionPatchHandler) ParseAndValidate(ctx context.Context, r *http.Request) error

ParseAndValidate fetches the needed data from the request and errors otherwise. It fetches the task and user from the request context and fetches the changes in activation and priority from the request body.

Jump to

Keyboard shortcuts

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