changeset 390:6f77f33af651

merge
author Thomas Junk <thomas.junk@intevation.de>
date Mon, 13 Aug 2018 16:21:38 +0200
parents e7d5383bc358 (current diff) 56897dd9a2b2 (diff)
children 13c5fc6cbaea c57b952c60be
files docs/schnittstellen.txt
diffstat 7 files changed, 291 insertions(+), 68 deletions(-) [+]
line wrap: on
line diff
--- a/README.md	Mon Aug 13 16:21:26 2018 +0200
+++ b/README.md	Mon Aug 13 16:21:38 2018 +0200
@@ -38,7 +38,7 @@
   cp cmd/gemma/gemma.toml.example gemma.toml
   ```
 
-- Edit `gemma.toml`, some parameters you propably want to change:
+- Edit `gemma.toml`, some parameters you probably want to change:
 
   * `host` and `port` to make gemma listen on a public interface
   * `service-password` to match the password for "gemma_service" user
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cmd/gemma/geoserver.go	Mon Aug 13 16:21:38 2018 +0200
@@ -0,0 +1,188 @@
+package main
+
+import (
+	"bytes"
+	"encoding/json"
+	"fmt"
+	"log"
+	"net/http"
+
+	"gemma.intevation.de/gemma/config"
+	"gemma.intevation.de/gemma/misc"
+)
+
+const (
+	workspaceName  = "gemma"
+	datastoreName  = "gemma"
+	databaseScheme = "waterway"
+	databaseType   = "postgis"
+)
+
+const startupSQL = `SET SESSION AUTHORIZATION waterway_user;`
+
+func basicAuth(user, password string) func(req *http.Request) {
+	auth := "Basic " + misc.BasicAuth(user, password)
+	return func(req *http.Request) {
+		req.Header.Add("Authorization", auth)
+	}
+}
+
+func asJSON(req *http.Request) {
+	req.Header.Set("Content-Type", "application/json")
+}
+
+func ensureWorkspace() error {
+	var (
+		url      = config.GeoServerURL()
+		user     = config.GeoServerUser()
+		password = config.GeoServerPassword()
+		auth     = basicAuth(user, password)
+	)
+
+	// Probe  workspace.
+	req, err := http.NewRequest(
+		http.MethodGet,
+		url+"/rest/workspaces/"+workspaceName+".json",
+		nil)
+	if err != nil {
+		return err
+	}
+	auth(req)
+	resp, err := http.DefaultClient.Do(req)
+	if err != nil {
+		return err
+	}
+
+	if resp.StatusCode != http.StatusNotFound {
+		log.Println("info: workspace " + workspaceName + " already exists.")
+		return nil
+	}
+
+	// Create workspace
+	const createJSON = `{"workspace":{"name":"` + workspaceName + `"}}`
+
+	req, err = http.NewRequest(
+		http.MethodPost,
+		url+"/rest/workspaces",
+		bytes.NewReader([]byte(createJSON)))
+	if err != nil {
+		return err
+	}
+	auth(req)
+	asJSON(req)
+	if resp, err = http.DefaultClient.Do(req); err != nil {
+		return err
+	}
+
+	if resp.StatusCode != http.StatusCreated {
+		err = fmt.Errorf("Status code '%s' (%d)",
+			http.StatusText(resp.StatusCode),
+			resp.StatusCode)
+	}
+
+	return err
+}
+
+func ensureDataStore() error {
+	var (
+		url      = config.GeoServerURL()
+		user     = config.GeoServerUser()
+		password = config.GeoServerPassword()
+		auth     = basicAuth(user, password)
+	)
+
+	// Probe  workspace.
+	req, err := http.NewRequest(
+		http.MethodGet,
+		url+"/rest/workspaces/"+workspaceName+"/datastores/"+datastoreName+".json",
+		nil)
+	if err != nil {
+		return err
+	}
+	auth(req)
+	resp, err := http.DefaultClient.Do(req)
+	if err != nil {
+		return err
+	}
+
+	if resp.StatusCode != http.StatusNotFound {
+		log.Println("info: datastore " + datastoreName + " already exists.")
+		return nil
+	}
+
+	type entry struct {
+		Key   interface{} `json:"@key"`
+		Value interface{} `json:"$"`
+	}
+
+	// Create datastore.
+	ds := map[string]interface{}{
+		"dataStore": map[string]interface{}{
+			"name": datastoreName,
+			"connectionParameters": map[string]interface{}{
+				"entry": []entry{
+					{"host", config.DBHost()},
+					{"port", config.DBPort()},
+					{"schema", databaseScheme},
+					{"user", config.SysAdmin()},
+					{"passwd", config.SysAdminPassword()},
+					{"dbtype", databaseType},
+					{"Session startup SQL", startupSQL},
+				},
+			},
+		},
+	}
+	var out bytes.Buffer
+	enc := json.NewEncoder(&out)
+	if err := enc.Encode(&ds); err != nil {
+		return err
+	}
+
+	req, err = http.NewRequest(
+		http.MethodPost,
+		url+"/rest/workspaces/"+workspaceName+"/datastores",
+		bytes.NewReader(out.Bytes()))
+	if err != nil {
+		return err
+	}
+	auth(req)
+	asJSON(req)
+	resp, err = http.DefaultClient.Do(req)
+	if err != nil {
+		return err
+	}
+
+	if resp.StatusCode != http.StatusCreated {
+		err = fmt.Errorf("Status code '%s' (%d)",
+			http.StatusText(resp.StatusCode),
+			resp.StatusCode)
+	}
+
+	return err
+}
+
+func prepareGeoServer() error {
+
+	var (
+		url = config.GeoServerURL()
+		//tables   = config.GeoServerTables()
+	)
+
+	//if url == "" || len(tables) == 0 {
+	if url == "" {
+		log.Println("info: No tables to publish on GeoServer")
+		return nil
+	}
+
+	if err := ensureWorkspace(); err != nil {
+		return err
+	}
+
+	if err := ensureDataStore(); err != nil {
+		return err
+	}
+
+	// TODO: Create layers.
+
+	return nil
+}
--- a/cmd/gemma/main.go	Mon Aug 13 16:21:26 2018 +0200
+++ b/cmd/gemma/main.go	Mon Aug 13 16:21:38 2018 +0200
@@ -37,6 +37,13 @@
 
 	prepareConnectionPool()
 
+	// Do GeoServer setup in background.
+	go func() {
+		if err := prepareGeoServer(); err != nil {
+			log.Printf("warn: preparing GeoServer: %v\n", err)
+		}
+	}()
+
 	m := mux.NewRouter()
 	controllers.BindRoutes(m)
 
--- a/config/config.go	Mon Aug 13 16:21:26 2018 +0200
+++ b/config/config.go	Mon Aug 13 16:21:38 2018 +0200
@@ -25,6 +25,9 @@
 func ServiceUser() string     { return viper.GetString("service-user") }
 func ServicePassword() string { return viper.GetString("service-password") }
 
+func SysAdmin() string         { return viper.GetString("sys-admin") }
+func SysAdminPassword() string { return viper.GetString("sys-admin-password") }
+
 func MailHost() string     { return viper.GetString("mail-host") }
 func MailPort() uint       { return uint(viper.GetInt32("mail-port")) }
 func MailUser() string     { return viper.GetString("mail-user") }
@@ -36,63 +39,82 @@
 
 func ExternalWFSs() map[string]interface{} { return viper.GetStringMap("external-wfs") }
 
+func GeoServerURL() string      { return viper.GetString("geoserver-url") }
+func GeoServerUser() string     { return viper.GetString("geoserver-user") }
+func GeoServerPassword() string { return viper.GetString("geoserver-password") }
+func GeoServerTables() []string { return viper.GetStringSlice("geoserver-tables") }
+
 var RootCmd = &cobra.Command{
 	Use:   "gemma",
 	Short: "gemma is a server for waterway monitoring and management",
 }
 
+var allowedOrigins = []string{
+	// TODO: Fill me!
+}
+
+var geoTables = []string{
+	// TODO: Fill me!
+}
+
 func init() {
 	cobra.OnInitialize(initConfig)
 	fl := RootCmd.PersistentFlags()
 	fl.StringVarP(&configFile, "config", "c", "", "config file (default is $HOME/.gemma.toml)")
 
-	fl.StringP("dbhost", "H", "localhost", "host of the database")
-	fl.UintP("dbport", "P", 5432, "port of the database")
-	fl.StringP("dbname", "d", "gemma", "name of the database")
-	fl.StringP("dbssl", "S", "prefer", "SSL mode of the database")
-
-	fl.StringP("sessions", "s", "", "path to the sessions file")
-
-	fl.StringP("web", "w", "", "path to the web files")
-	fl.StringP("host", "o", "localhost", "host of the web app")
-	fl.UintP("port", "p", 8000, "port of the web app")
-
-	fl.String("service-user", "postgres", "user to do service tasks")
-	fl.String("service-password", "", "password of user to do service tasks")
-
-	fl.String("mail-host", "localhost", "server to send mail with")
-	fl.Uint("mail-port", 465, "port of server to send mail with")
-	fl.String("mail-user", "gemma", "user to send mail with")
-	fl.String("mail-password", "", "password of user to send mail with")
-	fl.String("mail-from", "noreplay@localhost", "from line of mails")
-	fl.String("mail-helo", "localhost", "name of server to send mail from.")
-
-	fl.StringSlice("allowed-origins", []string{"foo.org"}, "allow access for remote origins")
-
 	vbind := func(name string) { viper.BindPFlag(name, fl.Lookup(name)) }
 
-	vbind("dbhost")
-	vbind("dbport")
-	vbind("dbname")
-	vbind("dbssl")
+	str := func(name, value, usage string) {
+		fl.String(name, value, usage)
+		vbind(name)
+	}
+	strP := func(name, shorthand, value, usage string) {
+		fl.StringP(name, shorthand, value, usage)
+		vbind(name)
+	}
+	ui := func(name string, value uint, usage string) {
+		fl.Uint(name, value, usage)
+		vbind(name)
+	}
+	uiP := func(name, shorthand string, value uint, usage string) {
+		fl.UintP(name, shorthand, value, usage)
+		vbind(name)
+	}
+	strSl := func(name string, value []string, usage string) {
+		fl.StringSlice(name, value, usage)
+		vbind(name)
+	}
 
-	vbind("sessions")
-
-	vbind("web")
-	vbind("host")
-	vbind("port")
+	strP("dbhost", "H", "localhost", "host of the database")
+	uiP("dbport", "P", 5432, "port of the database")
+	strP("dbname", "d", "gemma", "name of the database")
+	strP("dbssl", "S", "prefer", "SSL mode of the database")
 
-	vbind("service-user")
-	vbind("service-password")
+	strP("sessions", "s", "", "path to the sessions file")
+
+	strP("web", "w", "", "path to the web files")
+	strP("host", "o", "localhost", "host of the web app")
+	uiP("port", "p", 8000, "port of the web app")
+
+	str("service-user", "postgres", "user to do service tasks")
+	str("service-password", "", "password of user to do service tasks")
+
+	str("sys-admin", "postgres", "user to do admin tasks")
+	str("sys-admin-password", "", "password of user to do admin tasks")
 
-	vbind("mail-host")
-	vbind("mail-port")
-	vbind("mail-user")
-	vbind("mail-password")
-	vbind("mail-from")
-	vbind("mail-helo")
+	str("mail-host", "localhost", "server to send mail with")
+	ui("mail-port", 465, "port of server to send mail with")
+	str("mail-user", "gemma", "user to send mail with")
+	str("mail-password", "", "password of user to send mail with")
+	str("mail-from", "noreplay@localhost", "from line of mails")
+	str("mail-helo", "localhost", "name of server to send mail from.")
 
-	vbind("allowed-origins")
+	strSl("allowed-origins", allowedOrigins, "allow access for remote origins")
+
+	str("geoserver-url", "http://localhost:8080/geoserver", "URL to GeoServer")
+	str("geoserver-user", "admin", "GeoServer user")
+	str("geoserver-password", "geoserver", "GeoServer password")
+	strSl("geoserver-tables", geoTables, "tables to publish with GeoServer")
 }
 
 func initConfig() {
--- a/docs/schnittstellen.txt	Mon Aug 13 16:21:26 2018 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,26 +0,0 @@
-| Route                    | Methode  | Daten                                                 | *PUC         | JSON                                               | HTTP-Code     | Anmerkung                                      |
-|--------------------------+----------+-------------------------------------------------------+--------------+----------------------------------------------------+---------------+------------------------------------------------+
-| /api/users               | GET      | Liste an Usern                                        |              |                                                    | 200, 500      |                                                |
-| /api/users               | POST     | user, password, role, email, country, *extent         | APUC3, APUC4 |                                                    | 201, 400, 500 |                                                |
-| /api/users/{user}        | GET      |                                                       | APUC3, APUC4 | login, password, role, email, country, extent zoom | 201, 500      |                                                |
-| /api/users/{user}        | DELETE   |                                                       | APUC3        |                                                    | 204, 500      |                                                |
-| /api/users/{user}        | PUT      | user, password, role, email, country, *extent         | APUC3        | login, password, role, email, country, extent zoom | 200, 500      |                                                |
-| /api/users/passwordreset | POST     | user                                                  | GPUC3        | user                                               | 200, 500      |                                                |
-| /api/login               | GET/POST | user, password -> token im Result-JSON                | APUC1        |                                                    | 200, 500      |                                                |
-| /api/logout              | GET/POST |                                                       | APUC2        |                                                    | 200, 500      |                                                |
-| /api/renew               | GET/POST |                                                       | APUC2        |                                                    | 200, 500      |                                                |
-| /api/health/hardware     | GET      |                                                       | APUC8        |                                                    | 200, 500      | Optionale Queryparameter: limit, from, from+to |
-| /api/health/system       | GET      |                                                       | APUC8        |                                                    | 200, 500      | Optionale Queryparameter: limit, from, from+to |
-| /api/health/access       | GET      |                                                       | APUC8        |                                                    | 200, 500      | Optionale Queryparameter: limit, from, from+to |
-| /api/sendtestmail        | POST     | recipients: userids / an SAdmins, an WWAdmins         | APUC9        |                                                    | 200, 500      | !Throttle!                                     |
-| /api/management          | GET      | aktuelle Konfiguration                                | APUC10       |                                                    | 200, 500      |                                                |
-| /api/management          | PATCH    | zu ändernde Parameter                                 | APUC10       |                                                    | 200, 500      |                                                |
-| /api/templates           | GET      | Liste an Templates                                    | APUC6        |                                                    | 200, 500      |                                                |
-| /api/templates           | POST     | Daten für ein neues Template                          | APUC6        |                                                    | 201, 500      |                                                |
-| /api/templates/{id}      | GET      | Metadaten für das Template                            | APUC6        |                                                    | 200, 500      |                                                |
-| /api/template/{id}       | PATCH    | zu ändernde Parameter                                 | APUC6        |                                                    | 200, 500      |                                                |
-| /api/template/{id}       | DELETE   |                                                       | APUC6        |                                                    | 200, 500      |                                                |
-| /api/maps/print          | POST     | Metadaten für den Druck(?)                            | GPUC7        |                                                    | 200, 500      |                                                |
-| /api/search (?)          | POST     | Das zu suchende                                       | GPUC10       |                                                    | 200, 500      |                                                |
-| /api/bottlenecks         | GET      |                                                       | SPUC1        |                                                    | 200, 500      | Limit?                                         |
-| /api/fairwaydimension    | GET      | Flusskilometerangabe, Zeitpunkt(?)                    | SPUC3        |                                                    | 200, 500      | SVG?                                           |
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/docs/server-interface.txt	Mon Aug 13 16:21:38 2018 +0200
@@ -0,0 +1,26 @@
+| Route                    | Methode  | Daten                                                 | *PUC         | JSON                                               | HTTP-Code     | Anmerkung                                      |
+|--------------------------+----------+-------------------------------------------------------+--------------+----------------------------------------------------+---------------+------------------------------------------------+
+| /api/users               | GET      | Liste an Usern                                        |              |                                                    | 200, 500      |                                                |
+| /api/users               | POST     | user, password, role, email, country, *extent         | APUC3, APUC4 |                                                    | 201, 400, 500 |                                                |
+| /api/users/{user}        | GET      |                                                       | APUC3, APUC4 | login, password, role, email, country, extent zoom | 201, 500      |                                                |
+| /api/users/{user}        | DELETE   |                                                       | APUC3        |                                                    | 204, 500      |                                                |
+| /api/users/{user}        | PUT      | user, password, role, email, country, *extent         | APUC3        | login, password, role, email, country, extent zoom | 200, 500      |                                                |
+| /api/users/passwordreset | POST     | user                                                  | GPUC3        | user                                               | 200, 500      |                                                |
+| /api/login               | GET/POST | user, password -> token im Result-JSON                | APUC1        |                                                    | 200, 500      |                                                |
+| /api/logout              | GET/POST |                                                       | APUC2        |                                                    | 200, 500      |                                                |
+| /api/renew               | GET/POST |                                                       | APUC2        |                                                    | 200, 500      |                                                |
+| /api/health/hardware     | GET      |                                                       | APUC8        |                                                    | 200, 500      | Optionale Queryparameter: limit, from, from+to |
+| /api/health/system       | GET      |                                                       | APUC8        |                                                    | 200, 500      | Optionale Queryparameter: limit, from, from+to |
+| /api/health/access       | GET      |                                                       | APUC8        |                                                    | 200, 500      | Optionale Queryparameter: limit, from, from+to |
+| /api/sendtestmail        | POST     | recipients: userids / an SAdmins, an WWAdmins         | APUC9        |                                                    | 200, 500      | !Throttle!                                     |
+| /api/management          | GET      | aktuelle Konfiguration                                | APUC10       |                                                    | 200, 500      |                                                |
+| /api/management          | PATCH    | zu ändernde Parameter                                 | APUC10       |                                                    | 200, 500      |                                                |
+| /api/templates           | GET      | Liste an Templates                                    | APUC6        |                                                    | 200, 500      |                                                |
+| /api/templates           | POST     | Daten für ein neues Template                          | APUC6        |                                                    | 201, 500      |                                                |
+| /api/templates/{id}      | GET      | Metadaten für das Template                            | APUC6        |                                                    | 200, 500      |                                                |
+| /api/template/{id}       | PATCH    | zu ändernde Parameter                                 | APUC6        |                                                    | 200, 500      |                                                |
+| /api/template/{id}       | DELETE   |                                                       | APUC6        |                                                    | 200, 500      |                                                |
+| /api/maps/print          | POST     | Metadaten für den Druck(?)                            | GPUC7        |                                                    | 200, 500      |                                                |
+| /api/search (?)          | POST     | Das zu suchende                                       | GPUC10       |                                                    | 200, 500      |                                                |
+| /api/bottlenecks         | GET      |                                                       | SPUC1        |                                                    | 200, 500      | Limit?                                         |
+| /api/fairwaydimension    | GET      | Flusskilometerangabe, Zeitpunkt(?)                    | SPUC3        |                                                    | 200, 500      | SVG?                                           |
--- a/misc/encode.go	Mon Aug 13 16:21:26 2018 +0200
+++ b/misc/encode.go	Mon Aug 13 16:21:38 2018 +0200
@@ -1,6 +1,7 @@
 package misc
 
 import (
+	"encoding/base64"
 	"encoding/binary"
 	"io"
 )
@@ -68,3 +69,8 @@
 		w.Err = binary.Write(w.Writer, binary.BigEndian, []byte(s))
 	}
 }
+
+func BasicAuth(user, password string) string {
+	auth := user + ":" + password
+	return base64.StdEncoding.EncodeToString([]byte(auth))
+}