Browse Source

Merge pull request #108 from matrix-org/kegan/all-the-docs

Add HTTP API docs
kegan/docs-services
Kegsay 8 years ago
committed by GitHub
parent
commit
32553179b0
  1. 79
      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

79
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,32 +16,69 @@ import (
// ConfigureAuthRealmRequest is a request to /configureAuthRealm
type ConfigureAuthRealmRequest struct {
// An arbitrary unique identifier for this auth realm. This can be anything.
// Using an existing ID will REPLACE the entire existing 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. E.g. "github".
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 Matrix user ID 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 an existing 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, e.g. "github".
Type string
// The user ID of the configured client that this service will use to communicate with Matrix.
// 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 won't listen for 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
type SessionRequest struct {
// Session contains the complete auth session information for a given user on a given realm.
// They are created for use with ConfigFile.
type Session struct {
SessionID string
RealmID string
UserID string
@ -45,7 +90,7 @@ type ConfigFile struct {
Clients []ClientConfig
Realms []ConfigureAuthRealmRequest
Services []ConfigureServiceRequest
Sessions []SessionRequest
Sessions []Session
}
// Check validates the /configureService request
@ -65,14 +110,14 @@ func (c *ConfigureAuthRealmRequest) Check() error {
}
// Check validates the session config request
func (c *SessionRequest) Check() error {
func (c *Session) Check() error {
if c.SessionID == "" || c.UserID == "" || c.RealmID == "" || c.Config == nil {
return errors.New(`Must supply a "SessionID", a "RealmID", a "UserID" and a "Config"`)
}
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