rest

package
v0.0.0-...-6dcf3b2 Latest Latest
Warning

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

Go to latest
Published: Apr 18, 2022 License: GPL-2.0 Imports: 20 Imported by: 0

Documentation

Overview

Package rest is scaffolding around net/http that facilitates a RESTful HTTP API with certain patterns implicitly enforced: - When working on the same urls, all Methods should use the exact same data structures. E.g.: What you PUT is the same as what you GET out again. No cheating. - ETag is computed for all responses. - All responses are JSON-encoded, including error messages.

See objects/thing.go for how to use this, but the essence is: 1. Make whatever data structure you need. 2. Implement one or more of gondulapi.Getter/Putter/Poster/Deleter. 3. Use AddHandler() to register that data structure on a URL path 4. Grab lunch.

Receiver tries to do all HTTP and caching-related tasks for you, so you don't have to.

Index

Constants

This section is empty.

Variables

View Source
var InternalHTTPError = HTTPError{500, "Internal server error"}

InternalHTTPError is provided for the common case of returning an opaque error that can be passed to a user.

Functions

func AddHandler

func AddHandler(pathPrefix string, pathPattern string, allocator Allocator) error

AddHandler registeres an allocator/data structure with a url. The allocator should be a function returning an empty datastrcuture which implements one or more of gondulapi.Getter, Putter, Poster and Deleter

func StartReceiver

func StartReceiver()

StartReceiver a net/http server and handle all requests registered. Never returns.

func UpdateStaticAccessTokens

func UpdateStaticAccessTokens() error

UpdateStaticAccessTokens deletes the previous static tokens and load new ones from the config. To be called at least when starting the program.

Types

type AccessTokenEntries

type AccessTokenEntries []*AccessTokenEntry

AccessTokenEntries is multiple AccessTokenEntry.

func (*AccessTokenEntries) Get

func (tokens *AccessTokenEntries) Get(request *Request) Result

Get gets multiple access tokens.

type AccessTokenEntry

type AccessTokenEntry struct {
	ID  uuid.UUID `column:"id" json:"id"`
	Key string    `column:"key" json:"key,omitempty"`
	// TODO rename to just "user" since the DB is fixed now
	OwnerUserID    *uuid.UUID `column:"owner_user" json:"owner_user,omitempty"`       // Optional, not used for e.g. test status scripts.
	NonUserRole    *Role      `column:"non_user_role" json:"non_user_role,omitempty"` // Role if not a user token. Call .GetRole() to get the effective role.
	CreationTime   time.Time  `column:"creation_time" json:"creation_time"`
	ExpirationTime time.Time  `column:"expiration_time" json:"expiration_time"`
	IsStatic       bool       `column:"static" json:"static"` // If the token is static, i.e. defined by the config instead of DB and can't be created or deleted through the API.
	Comment        string     `column:"comment" json:"comment"`
	OwnerUser      *User      `column:"-" json:"-"` // The linked user (if any). Do not modify this object. Call .LoadUser() again if the underlying user is modified.
}

AccessTokenEntry is a collections of access things used for the client to authenticate itself and for the backend to know more about the client.

func (*AccessTokenEntry) Get

func (token *AccessTokenEntry) Get(request *Request) Result

Get gets a single access token.

func (*AccessTokenEntry) GetRole

func (token *AccessTokenEntry) GetRole() Role

GetRole returns the non-user role if non-user token or the user role if user token. Assumes the user is already loaded if user token. Returns an empty string (the invalid role) if inconsistent token.

func (*AccessTokenEntry) IsAuthenticated

func (token *AccessTokenEntry) IsAuthenticated() bool

IsAuthenticated checks if the requestor is authenticated.

type Allocator

type Allocator func() interface{}

Allocator is used to allocate a data structure that implements at least one of Getter, Putter, Poster or Deleter from gondulapi.

type Deleter

type Deleter interface {
	Delete(request *Request) Result
}

Deleter should delete the object identified by the element. It should be idempotent, in that it should be safe to call it on already-deleted items.

type Getter

type Getter interface {
	Get(request *Request) Result
}

Getter implements Get method, which should fetch the object represented by the element path.

type HTTPError

type HTTPError struct {
	Code    int `json:"-"`
	Message interface{}
}

HTTPError is used to combine a text-based error with a HTTP error code.

func HTTPErrorf

func HTTPErrorf(code int, str string, v ...interface{}) HTTPError

HTTPErrorf is a convenience-function to provide an Error data structure, which is essentially the same as fmt.Errorf(), but with an HTTP status code embedded into it which can be extracted.

func HTTPErrori

func HTTPErrori(code int, i interface{}) HTTPError

HTTPErrori creates an error with the given status-code, with i as the message. i should either be a text string or implement fmt.Stringer

func (HTTPError) Error

func (e HTTPError) Error() string

Error allows Error to implement the error interface. That's a whole lot of error in one sentence...

type Oauth2InfoData

type Oauth2InfoData struct {
	ClientID    string `json:"client_id"`
	AuthURL     string `json:"auth_url"`
	RedirectURL string `json:"redirect_url"`
}

Oauth2InfoData is the object for OAuth2 info requests.

func (*Oauth2InfoData) Get

func (response *Oauth2InfoData) Get(request *Request) Result

Get gets OAuth2 info.

type Oauth2LoginData

type Oauth2LoginData struct {
	User  User             `json:"user"`
	Token AccessTokenEntry `json:"token"`
}

Oauth2LoginData is the object for OAuth2 login requests.

func (*Oauth2LoginData) Post

func (response *Oauth2LoginData) Post(request *Request) Result

Post attempts to login using OAuth2.

type Oauth2LogoutData

type Oauth2LogoutData struct{}

Oauth2LogoutData is the object for OAuth2 login requests.

func (*Oauth2LogoutData) Post

func (response *Oauth2LogoutData) Post(request *Request) Result

Post deletes the current access token. Supports user tokens only.

type Poster

type Poster interface {
	Post(request *Request) Result
}

Poster is not necessarily idempotent, but can be. It should write the object provided, potentially generating a new ID for it if one isn't provided in the data structure itself. Post should ignore the request element.

type Putter

type Putter interface {
	Put(request *Request) Result
}

Putter is an idempotent method that requires an absolute path. It should (over-)write the object found at the element path.

type Request

type Request struct {
	ID          uuid.UUID
	Method      string
	AccessToken AccessTokenEntry
	PathArgs    map[string]string
	QueryArgs   map[string]string
	ListLimit   int  // How many elements to return in listings (convenience)
	ListBrief   bool // If only the most relevant fields should be included listings (convenience)
}

Request contains the last part of the URL (without the handler prefix), certain query args, and a limit on how many elements to get.

type Result

type Result struct {
	Message  string `json:"message,omitempty"` // Message for client
	Code     int    `json:"-"`                 // HTTP status
	Location string `json:"-"`                 // For location header if code 3xx
	Error    error  `json:"-"`                 // Internal error, forces code 500, hidden from client to avoid leak
}

Result is an update report on write-requests. The precise meaning might vary, but the gist should be the same.

func UnauthorizedResult

func UnauthorizedResult(token AccessTokenEntry) Result

UnauthorizedResult returns a 401 if the token is not authenticated or 403 if it is.

func (*Result) IsOk

func (result *Result) IsOk() bool

IsOk checks if error free and either not set code or a non-error code.

type Role

type Role string

Role defines a role for users and tokens.

const (
	// RoleInvalid - Invalid.
	RoleInvalid Role = ""
	// RoleGuest - No special access, for non-authenticated requests.
	RoleGuest Role = "guest"
	// RoleParticipant - Access to participate (i.e. logged in). Valid for user tokens only.
	RoleParticipant Role = "participant"
	// RoleOperator - Access to most stuff, but can't create new tracks, push status, etc.
	RoleOperator Role = "operator"
	// RoleAdmin - Access to everything.
	RoleAdmin Role = "admin"
	// RoleTester - Access to push test data, for status scripts. Valid for non-user tokens only.
	RoleTester Role = "tester"
	// RoleRunner - Access to modify stations, e.g. for updating the status when reprovisioning them. Valid for non-user tokens only.
	RoleRunner Role = "runner"
)

type User

type User struct {
	ID           *uuid.UUID `column:"id" json:"id"`                       // Required, unique
	Username     string     `column:"username" json:"username"`           // Required, unique
	DisplayName  string     `column:"display_name" json:"display_name"`   // Required
	EmailAddress string     `column:"email_address" json:"email_address"` // Required
	Role         Role       `column:"role" json:"role"`                   // Required (valid)
}

User reperesent a single user, including registry information. This is retrieved from the frontend, so where it comes from is somewhat irrelevant.

func (*User) ExistsWithID

func (user *User) ExistsWithID() (bool, error)

ExistsWithID checks whether a user with the specified ID exists or not.

func (*User) ExistsWithUsername

func (user *User) ExistsWithUsername() (bool, error)

ExistsWithUsername checks whether a user with the specified username exists or not.

func (*User) Get

func (user *User) Get(request *Request) Result

Get gets a user.

type Users

type Users []*User

Users is a list of users.

func (*Users) Get

func (users *Users) Get(request *Request) Result

Get gets multiple users.

Jump to

Keyboard shortcuts

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