Browse Source

Add lots of HTTP and API docs

This all gets formatted right by godoc so we can point the README to that
instead of having the docs so far away from the code.
kegan/all-the-docs
Kegan Dougal 8 years ago
parent
commit
dc6d7c593a
  1. 73
      src/github.com/matrix-org/go-neb/api/api.go
  2. 102
      src/github.com/matrix-org/go-neb/api/handlers/auth.go
  3. 27
      src/github.com/matrix-org/go-neb/api/handlers/client.go
  4. 17
      src/github.com/matrix-org/go-neb/api/handlers/heartbeat.go
  5. 48
      src/github.com/matrix-org/go-neb/api/handlers/service.go
  6. 9
      src/github.com/matrix-org/go-neb/api/handlers/webhook.go

73
src/github.com/matrix-org/go-neb/api/api.go

@ -1,3 +1,11 @@
// Package api contains the fundamental data types used by Go-NEB.
//
// Most HTTP API calls and/or config file sections are just ways of representing these
// data types.
//
// See also
//
// Package "api.handlers" for information on the HTTP API calls.
package api
import (
@ -8,31 +16,68 @@ import (
// ConfigureAuthRealmRequest is a request to /configureAuthRealm
type ConfigureAuthRealmRequest struct {
// An arbitrary unique identifier for this auth realm. This can be anything.
// Using the same ID will REPLACE the entire AuthRealm with the new information.
ID string
// The type of auth realm. This determines which code is loaded to execute the
// auth realm. It must be a known type.
Type string
// AuthRealm specific config information. See the docs for the auth realm you're interested in.
Config json.RawMessage
}
// RequestAuthSessionRequest is a request to /requestAuthSession
type RequestAuthSessionRequest struct {
// The realm ID to request a new auth session on. The realm MUST already exist.
RealmID string
// The user ID of user requesting the auth session. If the auth is successful,
// this user ID will be associated with the third-party credentials obtained.
UserID string
// AuthRealm specific config information. See the docs for the auth realm you're interested in.
Config json.RawMessage
}
// ConfigureServiceRequest is a request to /configureService
type ConfigureServiceRequest struct {
// An arbitrary unique identifier for this service. This can be anything.
// Using the same ID will REPLACE the entire Service with the new information.
ID string
// The type of service. This determines which code is loaded to execute the
// service. It must be a known type.
Type string
// The user ID of the configured client who will be controlled by this service.
// The user MUST already be configured.
UserID string
// Service-specific config information. See the docs for the service you're interested in.
Config json.RawMessage
}
// A ClientConfig is the configuration for a matrix client for a bot to use. It is
// a request to /configureClient
// A ClientConfig contains the configuration information for a matrix client so that
// Go-NEB can drive it. It forms the HTTP body to /configureClient requests.
type ClientConfig struct {
UserID string // The matrix UserId to connect with.
HomeserverURL string // A URL with the host and port of the matrix server. E.g. https://matrix.org:8448
AccessToken string // The matrix access token to authenticate the requests with.
Sync bool // True to start a sync stream for this user
AutoJoinRooms bool // True to automatically join all rooms for this user
DisplayName string // The display name to set for the matrix client
// The matrix User ID to connect with. E.g. @alice:matrix.org
UserID string
// A URL with the host and port of the matrix server. E.g. https://matrix.org:8448
HomeserverURL string
// The matrix access token to authenticate the requests with.
AccessToken string
// True to start a sync stream for this user. If false, no /sync goroutine will be
// created and this client will be unable to receive new events from Matrix. For services
// which only SEND events into Matrix, it may be desirable to set Sync to false to reduce the
// number of goroutines Go-NEB has to maintain. For services which respond to !commands,
// Sync MUST be set to true in order to receive those commands.
Sync bool
// True to automatically join every room this client is invited to.
// This is desirable for services which have !commands as that means anyone can pull the bot
// into the room. It is up to the service to decide which, if any, users to respond to however.
AutoJoinRooms bool
// The desired display name for this client.
// This does not automatically set the display name for this client. See /configureClient.
DisplayName string
}
// SessionRequest are usually multi-stage things so this type only exists for the config form
// SessionRequests are usually multi-stage things so this type only exists for the config form
// for use with ConfigFile.
type SessionRequest struct {
SessionID string
RealmID string
@ -72,7 +117,7 @@ func (c *SessionRequest) Check() error {
return nil
}
// Check that the client has the correct fields.
// Check that the client has supplied the correct fields.
func (c *ClientConfig) Check() error {
if c.UserID == "" || c.HomeserverURL == "" || c.AccessToken == "" {
return errors.New(`Must supply a "UserID", a "HomeserverURL", and an "AccessToken"`)
@ -82,3 +127,11 @@ func (c *ClientConfig) Check() error {
}
return nil
}
// Check that the request is valid.
func (r *RequestAuthSessionRequest) Check() error {
if r.UserID == "" || r.RealmID == "" || r.Config == nil {
return errors.New(`Must supply a "UserID", a "RealmID" and a "Config"`)
}
return nil
}

102
src/github.com/matrix-org/go-neb/api/handlers/auth.go

@ -15,19 +15,36 @@ import (
"github.com/matrix-org/go-neb/types"
)
// RequestAuthSession represents an HTTP handler capable of processing /admin/requestAuthSession requests.
type RequestAuthSession struct {
Db *database.ServiceDB
}
// OnIncomingRequest handles POST requests to /admin/requestAuthSession. The HTTP body MUST be
// a JSON object representing type "api.RequestAuthSessionRequest".
//
// This will return HTTP 400 if there are missing fields or the Realm ID is unknown.
// For the format of the response, see the specific AuthRealm that the Realm ID corresponds to.
//
// Request:
// POST /admin/requestAuthSession
// {
// "RealmID": "github_realm_id",
// "UserID": "@my_user:localhost",
// "Config": {
// // AuthRealm specific config info
// }
// }
// Response:
// HTTP/1.1 200 OK
// {
// // AuthRealm-specific information
// }
func (h *RequestAuthSession) OnIncomingRequest(req *http.Request) (interface{}, *errors.HTTPError) {
if req.Method != "POST" {
return nil, &errors.HTTPError{nil, "Unsupported Method", 405}
}
var body struct {
RealmID string
UserID string
Config json.RawMessage
}
var body api.RequestAuthSessionRequest
if err := json.NewDecoder(req.Body).Decode(&body); err != nil {
return nil, &errors.HTTPError{err, "Error parsing request JSON", 400}
}
@ -36,8 +53,8 @@ func (h *RequestAuthSession) OnIncomingRequest(req *http.Request) (interface{},
"user_id": body.UserID,
}).Print("Incoming auth session request")
if body.UserID == "" || body.RealmID == "" || body.Config == nil {
return nil, &errors.HTTPError{nil, `Must supply a "UserID", a "RealmID" and a "Config"`, 400}
if err := body.Check(); err != nil {
return nil, &errors.HTTPError{err, err.Error(), 400}
}
realm, err := h.Db.LoadAuthRealm(body.RealmID)
@ -55,10 +72,24 @@ func (h *RequestAuthSession) OnIncomingRequest(req *http.Request) (interface{},
return response, nil
}
// RemoveAuthSession represents an HTTP handler capable of processing /admin/removeAuthSession requests.
type RemoveAuthSession struct {
Db *database.ServiceDB
}
// OnIncomingRequest handles POST requests to /admin/removeAuthSession.
//
// The JSON object MUST contain the keys "RealmID" and "UserID" to identify the session to remove.
//
// Request
// POST /admin/removeAuthSession
// {
// "RealmID": "github-realm",
// "UserID": "@my_user:localhost"
// }
// Response:
// HTTP/1.1 200 OK
// {}
func (h *RemoveAuthSession) OnIncomingRequest(req *http.Request) (interface{}, *errors.HTTPError) {
if req.Method != "POST" {
return nil, &errors.HTTPError{nil, "Unsupported Method", 405}
@ -91,10 +122,15 @@ func (h *RemoveAuthSession) OnIncomingRequest(req *http.Request) (interface{}, *
return []byte(`{}`), nil
}
// RealmRedirect represents an HTTP handler which can process incoming redirects for auth realms.
type RealmRedirect struct {
Db *database.ServiceDB
}
// Handle requests for an auth realm.
//
// The last path segment of the URL MUST be the base64 form of the Realm ID. What response
// this returns depends on the specific AuthRealm implementation.
func (rh *RealmRedirect) Handle(w http.ResponseWriter, req *http.Request) {
segments := strings.Split(req.URL.Path, "/")
// last path segment is the base64d realm ID which we will pass the incoming request to
@ -121,10 +157,35 @@ func (rh *RealmRedirect) Handle(w http.ResponseWriter, req *http.Request) {
realm.OnReceiveRedirect(w, req)
}
// ConfigureAuthRealm represents an HTTP handler capable of processing /admin/configureAuthRealm requests.
type ConfigureAuthRealm struct {
Db *database.ServiceDB
}
// OnIncomingRequest handles POST requests to /admin/configureAuthRealm. The JSON object
// provided is of type "api.ConfigureAuthRealmRequest".
//
// Request:
// POST /admin/configureAuthRealm
// {
// "ID": "my-realm-id",
// "Type": "github",
// "Config": {
// // Realm-specific configuration information
// }
// }
// Response:
// HTTP/1.1 200 OK
// {
// "ID": "my-realm-id",
// "Type": "github",
// "OldConfig": {
// // Old auth realm config information
// },
// "NewConfig": {
// // New auth realm config information
// },
// }
func (h *ConfigureAuthRealm) OnIncomingRequest(req *http.Request) (interface{}, *errors.HTTPError) {
if req.Method != "POST" {
return nil, &errors.HTTPError{nil, "Unsupported Method", 405}
@ -160,10 +221,37 @@ func (h *ConfigureAuthRealm) OnIncomingRequest(req *http.Request) (interface{},
}{body.ID, body.Type, oldRealm, realm}, nil
}
// GetSession represents an HTTP handler capable of processing /admin/getSession requests.
type GetSession struct {
Db *database.ServiceDB
}
// OnIncomingRequest handles POST requests to /admin/getSession.
//
// The JSON object provided MUST have a "RealmID" and "UserID" in order to fetch the
// correct AuthSession. If there is no session for this tuple of realm and user ID,
// a 200 OK is still returned with "Authenticated" set to false.
//
// Request:
// POST /admin/getSession
// {
// "RealmID": "my-realm",
// "UserID": "@my_user:localhost"
// }
// Response:
// HTTP/1.1 200 OK
// {
// "ID": "session_id",
// "Authenticated": true,
// "Info": {
// // Session-specific config info
// }
// }
// Response if session not found:
// HTTP/1.1 200 OK
// {
// "Authenticated": false
// }
func (h *GetSession) OnIncomingRequest(req *http.Request) (interface{}, *errors.HTTPError) {
if req.Method != "POST" {
return nil, &errors.HTTPError{nil, "Unsupported Method", 405}

27
src/github.com/matrix-org/go-neb/api/handlers/client.go

@ -9,11 +9,36 @@ import (
"github.com/matrix-org/go-neb/errors"
)
// ConfigureClient represents an HTTP handler capable of processing /configureClient requests
// ConfigureClient represents an HTTP handler capable of processing /admin/configureClient requests.
type ConfigureClient struct {
Clients *clients.Clients
}
// OnIncomingRequest handles POST requests to /admin/configureClient. The JSON object provided
// is of type "api.ClientConfig".
//
// If a DisplayName is supplied, this request will set this client's display name
// if the old ClientConfig DisplayName differs from the new ClientConfig DisplayName.
//
// Request:
// POST /admin/configureClient
// {
// "UserID": "@my_bot:localhost",
// "HomeserverURL": "http://localhost:8008",
// "Sync": true,
// "DisplayName": "My Bot"
// }
//
// Response:
// HTTP/1.1 200 OK
// {
// "OldClient": {
// // The old api.ClientConfig
// },
// "NewClient": {
// // The new api.ClientConfig
// }
// }
func (s *ConfigureClient) OnIncomingRequest(req *http.Request) (interface{}, *errors.HTTPError) {
if req.Method != "POST" {
return nil, &errors.HTTPError{nil, "Unsupported Method", 405}

17
src/github.com/matrix-org/go-neb/api/handlers/heartbeat.go

@ -1,8 +1,17 @@
// Package handlers contains the HTTP handlers for Go-NEB.
//
// This includes detail on the API paths and top-level JSON keys. For specific service JSON,
// see the service you're interested in.
//
// See also
//
// Package "api" for the format of the JSON request bodies.
package handlers
import (
"github.com/matrix-org/go-neb/errors"
"net/http"
"github.com/matrix-org/go-neb/errors"
)
// Heartbeat implements the heartbeat API
@ -11,16 +20,12 @@ type Heartbeat struct{}
// OnIncomingRequest returns an empty JSON object which can be used to detect liveness of Go-NEB.
//
// Request:
// ```
// GET /test
// ```
//
//
// Response:
// ```
// HTTP/1.1 200 OK
// Content-Type: applicatoin/json
// {}
// ```
func (*Heartbeat) OnIncomingRequest(req *http.Request) (interface{}, *errors.HTTPError) {
return &struct{}{}, nil
}

48
src/github.com/matrix-org/go-neb/api/handlers/service.go

@ -16,6 +16,7 @@ import (
"github.com/matrix-org/go-neb/types"
)
// ConfigureService represents an HTTP handler which can process /admin/configureService requests.
type ConfigureService struct {
db *database.ServiceDB
clients *clients.Clients
@ -23,6 +24,7 @@ type ConfigureService struct {
mutexByServiceID map[string]*sync.Mutex
}
// NewConfigureService creates a new ConfigureService handler
func NewConfigureService(db *database.ServiceDB, clients *clients.Clients) *ConfigureService {
return &ConfigureService{
db: db,
@ -45,6 +47,32 @@ func (s *ConfigureService) getMutexForServiceID(serviceID string) *sync.Mutex {
return m
}
// OnIncomingRequest handles POST requests to /admin/configureService.
//
// The request body MUST be of type "api.ConfigureServiceRequest".
//
// Request:
// POST /admin/configureService
// {
// "ID": "my_service_id",
// "Type": "service-type",
// "UserID": "@my_bot:localhost",
// "Config": {
// // service-specific config information
// }
// }
// Response:
// HTTP/1.1 200 OK
// {
// "ID": "my_service_id",
// "Type": "service-type",
// "OldConfig": {
// // old service-specific config information
// },
// "NewConfig": {
// // new service-specific config information
// },
// }
func (s *ConfigureService) OnIncomingRequest(req *http.Request) (interface{}, *errors.HTTPError) {
if req.Method != "POST" {
return nil, &errors.HTTPError{nil, "Unsupported Method", 405}
@ -123,10 +151,30 @@ func (s *ConfigureService) createService(req *http.Request) (types.Service, *err
return service, nil
}
// GetService represents an HTTP handler which can process /admin/getService requests.
type GetService struct {
Db *database.ServiceDB
}
// OnIncomingRequest handles POST requests to /admin/getService.
//
// The request body MUST be a JSON body which has an "ID" key which represents
// the service ID to get.
//
// Request:
// POST /admin/getService
// {
// "ID": "my_service_id"
// }
// Response:
// HTTP/1.1 200 OK
// {
// "ID": "my_service_id",
// "Type": "github",
// "Config": {
// // service-specific config information
// }
// }
func (h *GetService) OnIncomingRequest(req *http.Request) (interface{}, *errors.HTTPError) {
if req.Method != "POST" {
return nil, &errors.HTTPError{nil, "Unsupported Method", 405}

9
src/github.com/matrix-org/go-neb/api/handlers/webhook.go

@ -2,12 +2,13 @@ package handlers
import (
"encoding/base64"
"net/http"
"strings"
log "github.com/Sirupsen/logrus"
"github.com/matrix-org/go-neb/clients"
"github.com/matrix-org/go-neb/database"
"github.com/matrix-org/go-neb/metrics"
"net/http"
"strings"
)
// Webhook represents an HTTP handler capable of accepting webhook requests on behalf of services.
@ -24,7 +25,9 @@ func NewWebhook(db *database.ServiceDB, cli *clients.Clients) *Webhook {
// Handle an incoming webhook HTTP request.
//
// The webhook MUST have a known base64 encoded service ID as the last path segment
// in order for this request to be passed to the correct service.
// in order for this request to be passed to the correct service, or else this will return
// HTTP 400. If the base64 encoded service ID is unknown, this will return HTTP 404.
// Beyond this, the exact response is determined by the specific Service implementation.
func (wh *Webhook) Handle(w http.ResponseWriter, req *http.Request) {
log.WithField("path", req.URL.Path).Print("Incoming webhook request")
segments := strings.Split(req.URL.Path, "/")

Loading…
Cancel
Save