Browse Source

Merge pull request #98 from matrix-org/kegan/config-yaml

Support config.yaml in addition to HTTP endpoints
kegan/tests
Kegsay 8 years ago
committed by GitHub
parent
commit
9d059d2e55
  1. 106
      config.sample.yaml
  2. 84
      src/github.com/matrix-org/go-neb/api/api.go
  3. 9
      src/github.com/matrix-org/go-neb/clients/clients.go
  4. 61
      src/github.com/matrix-org/go-neb/database/db.go
  5. 11
      src/github.com/matrix-org/go-neb/database/schema.go
  6. 161
      src/github.com/matrix-org/go-neb/goneb.go
  7. 30
      src/github.com/matrix-org/go-neb/handlers.go
  8. 22
      src/github.com/matrix-org/go-neb/types/types.go

106
config.sample.yaml

@ -0,0 +1,106 @@
# Go-NEB Configuration File
#
# This file provides an alternative way to configure Go-NEB which does not involve HTTP APIs.
#
# This file can be supplied to go-neb by the environment variable `CONFIG_FILE=config.yaml`.
# It will force Go-NEB to operate in "config" mode. This means:
# - Go-NEB will ONLY use the data contained inside this file.
# - All of Go-NEB's /admin HTTP listeners will be disabled. You will be unable to add new services at runtime.
# - The environment variable `DATABASE_URL` will be ignored and an in-memory database will be used instead.
#
# This file is broken down into 4 sections which matches the following HTTP APIs:
# - /configureClient
# - /configureAuthRealm
# - /configureService
# - /requestAuthSession (redirects not supported)
# The list of clients which Go-NEB is aware of.
# Delete or modify this list as appropriate.
# See the docs for /configureClient for the full list of options.
clients:
- UserID: "@goneb:localhost"
AccessToken: "MDASDASJDIASDJASDAFGFRGER"
HomeserverURL: "http://localhost:8008"
Sync: true
AutoJoinRooms: true
DisplayName: "Go-NEB!"
- UserID: "@another_goneb:localhost"
AccessToken: "MDASDASJDIASDJASDAFGFRGER"
HomeserverURL: "http://localhost:8008"
Sync: false
AutoJoinRooms: false
DisplayName: "Go-NEB!"
# The list of realms which Go-NEB is aware of.
# Delete or modify this list as appropriate.
# See the docs for /configureAuthRealm for the full list of options.
realms:
- ID: "github_realm"
Type: "github"
Config: {} # No need for client ID or Secret as Go-NEB isn't generating OAuth URLs
# The list of *authenticated* sessions which Go-NEB is aware of.
# Delete or modify this list as appropriate.
# The full list of options are shown below: there is no single HTTP endpoint
# which maps to this section.
sessions:
- SessionID: "your_github_session"
RealmID: "github_realm"
UserID: "@YOUR_USER_ID:localhost"
Config:
# Populate these fields by generating a "Personal Access Token" on github.com
AccessToken: "YOUR_GITHUB_ACCESS_TOKEN"
Scopes: "admin:org_hook,admin:repo_hook,repo,user"
# The list of services which Go-NEB is aware of.
# Delete or modify this list as appropriate.
# See the docs for /configureService for the full list of options.
services:
- ID: "giphy_service"
Type: "giphy"
UserID: "@goneb:localhost" # requires a Syncing client
Config:
api_key: "qwg4672vsuyfsfe"
- ID: "guggy_service"
Type: "guggy"
UserID: "@goneb:localhost" # requires a Syncing client
Config:
api_key: "2356saaqfhgfe"
- ID: "rss_service"
Type: "rssbot"
UserID: "@another_goneb:localhost"
Config:
feeds:
"http://lorem-rss.herokuapp.com/feed?unit=second&interval=60":
rooms: ["!qmElAGdFYCHoCJuaNt:localhost"]
- ID: "github_cmd_service"
Type: "github"
UserID: "@goneb:localhost" # requires a Syncing client
Config:
RealmID: "github_realm"
# Make sure your BASE_URL can be accessed by Github!
- ID: "github_webhook_service"
Type: "github-webhook"
UserID: "@another_goneb:localhost"
Config:
RealmID: "github_realm"
ClientUserID: "@YOUR_USER_ID:localhost" # needs to be an authenticated user so Go-NEB can create webhooks.
Rooms:
"!someroom:id":
Repos:
"matrix-org/synapse":
Events: ["push", "issues"]
"matrix-org/dendron":
Events: ["pull_request"]
"!anotherroom:id":
Repos:
"matrix-org/synapse":
Events: ["push", "issues"]
"matrix-org/dendron":
Events: ["pull_request"]

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

@ -0,0 +1,84 @@
package api
import (
"encoding/json"
"errors"
"net/url"
)
// ConfigureAuthRealmRequest is a request to /configureAuthRealm
type ConfigureAuthRealmRequest struct {
ID string
Type string
Config json.RawMessage
}
// ConfigureServiceRequest is a request to /configureService
type ConfigureServiceRequest struct {
ID string
Type string
UserID string
Config json.RawMessage
}
// A ClientConfig is the configuration for a matrix client for a bot to use. It is
// a request to /configureClient
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
}
// SessionRequest are usually multi-stage things so this type only exists for the config form
type SessionRequest struct {
SessionID string
RealmID string
UserID string
Config json.RawMessage
}
// ConfigFile represents config.sample.yaml
type ConfigFile struct {
Clients []ClientConfig
Realms []ConfigureAuthRealmRequest
Services []ConfigureServiceRequest
Sessions []SessionRequest
}
// Check validates the /configureService request
func (c *ConfigureServiceRequest) Check() error {
if c.ID == "" || c.Type == "" || c.UserID == "" || c.Config == nil {
return errors.New(`Must supply an "ID", a "Type", a "UserID" and a "Config"`)
}
return nil
}
// Check validates the /configureAuthRealm request
func (c *ConfigureAuthRealmRequest) Check() error {
if c.ID == "" || c.Type == "" || c.Config == nil {
return errors.New(`Must supply a "ID", a "Type" and a "Config"`)
}
return nil
}
// Check validates the session config request
func (c *SessionRequest) 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.
func (c *ClientConfig) Check() error {
if c.UserID == "" || c.HomeserverURL == "" || c.AccessToken == "" {
return errors.New(`Must supply a "UserID", a "HomeserverURL", and an "AccessToken"`)
}
if _, err := url.Parse(c.HomeserverURL); err != nil {
return err
}
return nil
}

9
src/github.com/matrix-org/go-neb/clients/clients.go

@ -2,6 +2,7 @@ package clients
import (
log "github.com/Sirupsen/logrus"
"github.com/matrix-org/go-neb/api"
"github.com/matrix-org/go-neb/database"
"github.com/matrix-org/go-neb/matrix"
"github.com/matrix-org/go-neb/plugin"
@ -64,7 +65,7 @@ func (c *Clients) Client(userID string) (*matrix.Client, error) {
}
// Update updates the config for a matrix client
func (c *Clients) Update(config types.ClientConfig) (types.ClientConfig, error) {
func (c *Clients) Update(config api.ClientConfig) (api.ClientConfig, error) {
_, old, err := c.updateClientInDB(config)
return old.config, err
}
@ -86,7 +87,7 @@ func (c *Clients) Start() error {
}
type clientEntry struct {
config types.ClientConfig
config api.ClientConfig
client *matrix.Client
}
@ -123,7 +124,7 @@ func (c *Clients) loadClientFromDB(userID string) (entry clientEntry, err error)
return
}
func (c *Clients) updateClientInDB(newConfig types.ClientConfig) (new clientEntry, old clientEntry, err error) {
func (c *Clients) updateClientInDB(newConfig api.ClientConfig) (new clientEntry, old clientEntry, err error) {
c.dbMutex.Lock()
defer c.dbMutex.Unlock()
@ -231,7 +232,7 @@ func (c *Clients) onRoomMemberEvent(client *matrix.Client, event *matrix.Event)
}
}
func (c *Clients) newClient(config types.ClientConfig) (*matrix.Client, error) {
func (c *Clients) newClient(config api.ClientConfig) (*matrix.Client, error) {
homeserverURL, err := url.Parse(config.HomeserverURL)
if err != nil {
return nil, err

61
src/github.com/matrix-org/go-neb/database/db.go

@ -2,6 +2,9 @@ package database
import (
"database/sql"
"encoding/json"
"fmt"
"github.com/matrix-org/go-neb/api"
"github.com/matrix-org/go-neb/types"
"time"
)
@ -48,7 +51,7 @@ func Open(databaseType, databaseURL string) (serviceDB *ServiceDB, err error) {
// StoreMatrixClientConfig stores the Matrix client config for a bot service.
// If a config already exists then it will be updated, otherwise a new config
// will be inserted. The previous config is returned.
func (d *ServiceDB) StoreMatrixClientConfig(config types.ClientConfig) (oldConfig types.ClientConfig, err error) {
func (d *ServiceDB) StoreMatrixClientConfig(config api.ClientConfig) (oldConfig api.ClientConfig, err error) {
err = runTransaction(d.db, func(txn *sql.Tx) error {
oldConfig, err = selectMatrixClientConfigTxn(txn, config.UserID)
now := time.Now()
@ -64,7 +67,7 @@ func (d *ServiceDB) StoreMatrixClientConfig(config types.ClientConfig) (oldConfi
}
// LoadMatrixClientConfigs loads all Matrix client configs from the database.
func (d *ServiceDB) LoadMatrixClientConfigs() (configs []types.ClientConfig, err error) {
func (d *ServiceDB) LoadMatrixClientConfigs() (configs []api.ClientConfig, err error) {
err = runTransaction(d.db, func(txn *sql.Tx) error {
configs, err = selectMatrixClientConfigsTxn(txn)
return err
@ -74,7 +77,7 @@ func (d *ServiceDB) LoadMatrixClientConfigs() (configs []types.ClientConfig, err
// LoadMatrixClientConfig loads a Matrix client config from the database.
// Returns sql.ErrNoRows if the client isn't in the database.
func (d *ServiceDB) LoadMatrixClientConfig(userID string) (config types.ClientConfig, err error) {
func (d *ServiceDB) LoadMatrixClientConfig(userID string) (config api.ClientConfig, err error) {
err = runTransaction(d.db, func(txn *sql.Tx) error {
config, err = selectMatrixClientConfigTxn(txn, userID)
return err
@ -272,6 +275,58 @@ func (d *ServiceDB) StoreBotOptions(opts types.BotOptions) (oldOpts types.BotOpt
return
}
// InsertFromConfig inserts entries from the config file into the database. This only really
// makes sense for in-memory databases.
func (d *ServiceDB) InsertFromConfig(cfg *api.ConfigFile) error {
// Insert clients
for _, cli := range cfg.Clients {
if _, err := d.StoreMatrixClientConfig(cli); err != nil {
return err
}
}
// Keep a map of realms for inserting sessions
realms := map[string]types.AuthRealm{} // by realm ID
// Insert realms
for _, r := range cfg.Realms {
if err := r.Check(); err != nil {
return err
}
realm, err := types.CreateAuthRealm(r.ID, r.Type, r.Config)
if err != nil {
return err
}
if _, err := d.StoreAuthRealm(realm); err != nil {
return err
}
realms[realm.ID()] = realm
}
// Insert sessions
for _, s := range cfg.Sessions {
if err := s.Check(); err != nil {
return err
}
r := realms[s.RealmID]
if r == nil {
return fmt.Errorf("Session %s specifies an unknown realm ID %s", s.SessionID, s.RealmID)
}
session := r.AuthSession(s.SessionID, s.UserID, s.RealmID)
// dump the raw JSON config directly into the session. This is what
// selectAuthSessionByUserTxn does.
if err := json.Unmarshal(s.Config, session); err != nil {
return err
}
if _, err := d.StoreAuthSession(session); err != nil {
return err
}
}
// Do not insert services yet, they require more work to set up.
return nil
}
func runTransaction(db *sql.DB, fn func(txn *sql.Tx) error) (err error) {
txn, err := db.Begin()
if err != nil {

11
src/github.com/matrix-org/go-neb/database/schema.go

@ -4,6 +4,7 @@ import (
"database/sql"
"encoding/json"
"fmt"
"github.com/matrix-org/go-neb/api"
"github.com/matrix-org/go-neb/types"
"time"
)
@ -64,7 +65,7 @@ const selectMatrixClientConfigSQL = `
SELECT client_json FROM matrix_clients WHERE user_id = $1
`
func selectMatrixClientConfigTxn(txn *sql.Tx, userID string) (config types.ClientConfig, err error) {
func selectMatrixClientConfigTxn(txn *sql.Tx, userID string) (config api.ClientConfig, err error) {
var configJSON []byte
err = txn.QueryRow(selectMatrixClientConfigSQL, userID).Scan(&configJSON)
if err != nil {
@ -78,14 +79,14 @@ const selectMatrixClientConfigsSQL = `
SELECT client_json FROM matrix_clients
`
func selectMatrixClientConfigsTxn(txn *sql.Tx) (configs []types.ClientConfig, err error) {
func selectMatrixClientConfigsTxn(txn *sql.Tx) (configs []api.ClientConfig, err error) {
rows, err := txn.Query(selectMatrixClientConfigsSQL)
if err != nil {
return
}
defer rows.Close()
for rows.Next() {
var config types.ClientConfig
var config api.ClientConfig
var configJSON []byte
if err = rows.Scan(&configJSON); err != nil {
return
@ -104,7 +105,7 @@ INSERT INTO matrix_clients(
) VALUES ($1, $2, '', $3, $4)
`
func insertMatrixClientConfigTxn(txn *sql.Tx, now time.Time, config types.ClientConfig) error {
func insertMatrixClientConfigTxn(txn *sql.Tx, now time.Time, config api.ClientConfig) error {
t := now.UnixNano() / 1000000
configJSON, err := json.Marshal(&config)
if err != nil {
@ -119,7 +120,7 @@ UPDATE matrix_clients SET client_json = $1, time_updated_ms = $2
WHERE user_id = $3
`
func updateMatrixClientConfigTxn(txn *sql.Tx, now time.Time, config types.ClientConfig) error {
func updateMatrixClientConfigTxn(txn *sql.Tx, now time.Time, config api.ClientConfig) error {
t := now.UnixNano() / 1000000
configJSON, err := json.Marshal(&config)
if err != nil {

161
src/github.com/matrix-org/go-neb/goneb.go

@ -1,8 +1,11 @@
package main
import (
"encoding/json"
"fmt"
log "github.com/Sirupsen/logrus"
"github.com/matrix-org/dugong"
"github.com/matrix-org/go-neb/api"
"github.com/matrix-org/go-neb/clients"
"github.com/matrix-org/go-neb/database"
_ "github.com/matrix-org/go-neb/metrics"
@ -19,18 +22,130 @@ import (
"github.com/matrix-org/go-neb/types"
_ "github.com/mattn/go-sqlite3"
"github.com/prometheus/client_golang/prometheus"
"gopkg.in/yaml.v2"
"io/ioutil"
"net/http"
_ "net/http/pprof"
"os"
"path/filepath"
)
// loadFromConfig loads a config file and returns a ConfigFile
func loadFromConfig(db *database.ServiceDB, configFilePath string) (*api.ConfigFile, error) {
// ::Horrible hacks ahead::
// The config is represented as YAML, and we want to convert that into NEB types.
// However, NEB types make liberal use of json.RawMessage which the YAML parser
// doesn't like. We can't implement MarshalYAML/UnmarshalYAML as a custom type easily
// because YAML is insane and supports numbers as keys. The YAML parser therefore has the
// generic form of map[interface{}]interface{} - but the JSON parser doesn't know
// how to parse that.
//
// The hack that follows gets around this by type asserting all parsed YAML keys as
// strings then re-encoding/decoding as JSON. That is:
// YAML bytes -> map[interface]interface -> map[string]interface -> JSON bytes -> NEB types
// Convert to YAML bytes
contents, err := ioutil.ReadFile(configFilePath)
if err != nil {
return nil, err
}
// Convert to map[interface]interface
var cfg map[interface{}]interface{}
if err = yaml.Unmarshal(contents, &cfg); err != nil {
return nil, fmt.Errorf("Failed to unmarshal YAML: %s", err)
}
// Convert to map[string]interface
dict := convertKeysToStrings(cfg)
// Convert to JSON bytes
b, err := json.Marshal(dict)
if err != nil {
return nil, fmt.Errorf("Failed to marshal config as JSON: %s", err)
}
// Finally, Convert to NEB types
var c api.ConfigFile
if err := json.Unmarshal(b, &c); err != nil {
return nil, fmt.Errorf("Failed to convert to config file: %s", err)
}
// sanity check (at least 1 client and 1 service)
if len(c.Clients) == 0 || len(c.Services) == 0 {
return nil, fmt.Errorf("At least 1 client and 1 service must be specified")
}
return &c, nil
}
func convertKeysToStrings(iface interface{}) interface{} {
obj, isObj := iface.(map[interface{}]interface{})
if isObj {
strObj := make(map[string]interface{})
for k, v := range obj {
strObj[k.(string)] = convertKeysToStrings(v) // handle nested objects
}
return strObj
}
arr, isArr := iface.([]interface{})
if isArr {
for i := range arr {
arr[i] = convertKeysToStrings(arr[i]) // handle nested objects
}
return arr
}
return iface // base type like string or number
}
func insertServicesFromConfig(clis *clients.Clients, serviceReqs []api.ConfigureServiceRequest) error {
for i, s := range serviceReqs {
if err := s.Check(); err != nil {
return fmt.Errorf("config: Service[%d] : %s", i, err)
}
service, err := types.CreateService(s.ID, s.Type, s.UserID, s.Config)
if err != nil {
return fmt.Errorf("config: Service[%d] : %s", i, err)
}
// Fetch the client for this service and register/poll
c, err := clis.Client(s.UserID)
if err != nil {
return fmt.Errorf("config: Service[%d] : %s", i, err)
}
if err = service.Register(nil, c); err != nil {
return fmt.Errorf("config: Service[%d] : %s", i, err)
}
if _, err := database.GetServiceDB().StoreService(service); err != nil {
return fmt.Errorf("config: Service[%d] : %s", i, err)
}
service.PostRegister(nil)
}
return nil
}
func loadDatabase(databaseType, databaseURL, configYAML string) (*database.ServiceDB, error) {
if configYAML != "" {
databaseType = "sqlite3"
databaseURL = ":memory:?_busy_timeout=5000"
}
db, err := database.Open(databaseType, databaseURL)
if err == nil {
database.SetServiceDB(db) // set singleton
}
return db, err
}
func main() {
bindAddress := os.Getenv("BIND_ADDRESS")
databaseType := os.Getenv("DATABASE_TYPE")
databaseURL := os.Getenv("DATABASE_URL")
baseURL := os.Getenv("BASE_URL")
logDir := os.Getenv("LOG_DIR")
configYAML := os.Getenv("CONFIG_FILE")
if logDir != "" {
log.AddHook(dugong.NewFSHook(
@ -41,8 +156,8 @@ func main() {
}
log.Infof(
"Go-NEB (BIND_ADDRESS=%s DATABASE_TYPE=%s DATABASE_URL=%s BASE_URL=%s LOG_DIR=%s)",
bindAddress, databaseType, databaseURL, baseURL, logDir,
"Go-NEB (BIND_ADDRESS=%s DATABASE_TYPE=%s DATABASE_URL=%s BASE_URL=%s LOG_DIR=%s CONFIG_FILE=%s)",
bindAddress, databaseType, databaseURL, baseURL, logDir, configYAML,
)
err := types.BaseURL(baseURL)
@ -50,31 +165,55 @@ func main() {
log.WithError(err).Panic("Failed to get base url")
}
db, err := database.Open(databaseType, databaseURL)
db, err := loadDatabase(databaseType, databaseURL, configYAML)
if err != nil {
log.WithError(err).Panic("Failed to open database")
}
database.SetServiceDB(db)
// Populate the database from the config file if one was supplied.
var cfg *api.ConfigFile
if configYAML != "" {
if cfg, err = loadFromConfig(db, configYAML); err != nil {
log.WithError(err).WithField("config_file", configYAML).Panic("Failed to load config file")
}
if err := db.InsertFromConfig(cfg); err != nil {
log.WithError(err).Panic("Failed to persist config data into in-memory DB")
}
log.Info("Inserted ", len(cfg.Clients), " clients")
log.Info("Inserted ", len(cfg.Realms), " realms")
log.Info("Inserted ", len(cfg.Sessions), " sessions")
}
clients := clients.New(db)
if err := clients.Start(); err != nil {
log.WithError(err).Panic("Failed to start up clients")
}
// Handle non-admin paths for normal NEB functioning
http.Handle("/metrics", prometheus.Handler())
http.Handle("/test", prometheus.InstrumentHandler("test", server.MakeJSONAPI(&heartbeatHandler{})))
http.Handle("/admin/getService", prometheus.InstrumentHandler("getService", server.MakeJSONAPI(&getServiceHandler{db: db})))
http.Handle("/admin/getSession", prometheus.InstrumentHandler("getSession", server.MakeJSONAPI(&getSessionHandler{db: db})))
http.Handle("/admin/configureClient", prometheus.InstrumentHandler("configureClient", server.MakeJSONAPI(&configureClientHandler{db: db, clients: clients})))
http.Handle("/admin/configureService", prometheus.InstrumentHandler("configureService", server.MakeJSONAPI(newConfigureServiceHandler(db, clients))))
http.Handle("/admin/configureAuthRealm", prometheus.InstrumentHandler("configureAuthRealm", server.MakeJSONAPI(&configureAuthRealmHandler{db: db})))
http.Handle("/admin/requestAuthSession", prometheus.InstrumentHandler("requestAuthSession", server.MakeJSONAPI(&requestAuthSessionHandler{db: db})))
http.Handle("/admin/removeAuthSession", prometheus.InstrumentHandler("removeAuthSession", server.MakeJSONAPI(&removeAuthSessionHandler{db: db})))
wh := &webhookHandler{db: db, clients: clients}
http.HandleFunc("/services/hooks/", prometheus.InstrumentHandlerFunc("webhookHandler", wh.handle))
rh := &realmRedirectHandler{db: db}
http.HandleFunc("/realms/redirects/", prometheus.InstrumentHandlerFunc("realmRedirectHandler", rh.handle))
// Read exclusively from the config file if one was supplied.
// Otherwise, add HTTP listeners for new Services/Sessions/Clients/etc.
if configYAML != "" {
if err := insertServicesFromConfig(clients, cfg.Services); err != nil {
log.WithError(err).Panic("Failed to insert services")
}
log.Info("Inserted ", len(cfg.Services), " services")
} else {
http.Handle("/admin/getService", prometheus.InstrumentHandler("getService", server.MakeJSONAPI(&getServiceHandler{db: db})))
http.Handle("/admin/getSession", prometheus.InstrumentHandler("getSession", server.MakeJSONAPI(&getSessionHandler{db: db})))
http.Handle("/admin/configureClient", prometheus.InstrumentHandler("configureClient", server.MakeJSONAPI(&configureClientHandler{db: db, clients: clients})))
http.Handle("/admin/configureService", prometheus.InstrumentHandler("configureService", server.MakeJSONAPI(newConfigureServiceHandler(db, clients))))
http.Handle("/admin/configureAuthRealm", prometheus.InstrumentHandler("configureAuthRealm", server.MakeJSONAPI(&configureAuthRealmHandler{db: db})))
http.Handle("/admin/requestAuthSession", prometheus.InstrumentHandler("requestAuthSession", server.MakeJSONAPI(&requestAuthSessionHandler{db: db})))
http.Handle("/admin/removeAuthSession", prometheus.InstrumentHandler("removeAuthSession", server.MakeJSONAPI(&removeAuthSessionHandler{db: db})))
}
polling.SetClients(clients)
if err := polling.Start(); err != nil {
log.WithError(err).Panic("Failed to start polling")

30
src/github.com/matrix-org/go-neb/api.go → src/github.com/matrix-org/go-neb/handlers.go

@ -5,6 +5,7 @@ import (
"encoding/base64"
"encoding/json"
log "github.com/Sirupsen/logrus"
"github.com/matrix-org/go-neb/api"
"github.com/matrix-org/go-neb/clients"
"github.com/matrix-org/go-neb/database"
"github.com/matrix-org/go-neb/errors"
@ -136,17 +137,13 @@ func (h *configureAuthRealmHandler) OnIncomingRequest(req *http.Request) (interf
if req.Method != "POST" {
return nil, &errors.HTTPError{nil, "Unsupported Method", 405}
}
var body struct {
ID string
Type string
Config json.RawMessage
}
var body api.ConfigureAuthRealmRequest
if err := json.NewDecoder(req.Body).Decode(&body); err != nil {
return nil, &errors.HTTPError{err, "Error parsing request JSON", 400}
}
if body.ID == "" || body.Type == "" || body.Config == nil {
return nil, &errors.HTTPError{nil, `Must supply a "ID", a "Type" and a "Config"`, 400}
if err := body.Check(); err != nil {
return nil, &errors.HTTPError{err, err.Error(), 400}
}
realm, err := types.CreateAuthRealm(body.ID, body.Type, body.Config)
@ -223,7 +220,7 @@ func (s *configureClientHandler) OnIncomingRequest(req *http.Request) (interface
return nil, &errors.HTTPError{nil, "Unsupported Method", 405}
}
var body types.ClientConfig
var body api.ClientConfig
if err := json.NewDecoder(req.Body).Decode(&body); err != nil {
return nil, &errors.HTTPError{err, "Error parsing request JSON", 400}
}
@ -238,8 +235,8 @@ func (s *configureClientHandler) OnIncomingRequest(req *http.Request) (interface
}
return &struct {
OldClient types.ClientConfig
NewClient types.ClientConfig
OldClient api.ClientConfig
NewClient api.ClientConfig
}{oldClient, body}, nil
}
@ -334,20 +331,13 @@ func (s *configureServiceHandler) OnIncomingRequest(req *http.Request) (interfac
}
func (s *configureServiceHandler) createService(req *http.Request) (types.Service, *errors.HTTPError) {
var body struct {
ID string
Type string
UserID string
Config json.RawMessage
}
var body api.ConfigureServiceRequest
if err := json.NewDecoder(req.Body).Decode(&body); err != nil {
return nil, &errors.HTTPError{err, "Error parsing request JSON", 400}
}
if body.ID == "" || body.Type == "" || body.UserID == "" || body.Config == nil {
return nil, &errors.HTTPError{
nil, `Must supply an "ID", a "Type", a "UserID" and a "Config"`, 400,
}
if err := body.Check(); err != nil {
return nil, &errors.HTTPError{err, err.Error(), 400}
}
service, err := types.CreateService(body.ID, body.Type, body.UserID, body.Config)

22
src/github.com/matrix-org/go-neb/types/types.go

@ -7,32 +7,10 @@ import (
"github.com/matrix-org/go-neb/matrix"
"github.com/matrix-org/go-neb/plugin"
"net/http"
"net/url"
"strings"
"time"
)
// A ClientConfig is the configuration for a matrix client for a bot to use.
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
}
// Check that the client has 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"`)
}
if _, err := url.Parse(c.HomeserverURL); err != nil {
return err
}
return nil
}
// BotOptions for a given bot user in a given room
type BotOptions struct {
RoomID string

Loading…
Cancel
Save