changeset 876:8b9bd9ccdd93 geo-style

Upload style during boot. TODO: Connect with layer.
author Sascha L. Teichmann <teichmann@intevation.de>
date Sun, 30 Sep 2018 19:42:16 +0200
parents 371c756f0370
children 254cd247826d
files pkg/geoserver/boot.go pkg/models/intservices.go
diffstat 2 files changed, 122 insertions(+), 12 deletions(-) [+]
line wrap: on
line diff
--- a/pkg/geoserver/boot.go	Sun Sep 30 18:15:11 2018 +0200
+++ b/pkg/geoserver/boot.go	Sun Sep 30 19:42:16 2018 +0200
@@ -3,7 +3,9 @@
 import (
 	"bytes"
 	"encoding/json"
+	"encoding/xml"
 	"fmt"
+	"io"
 	"log"
 	"net"
 	"net/http"
@@ -37,9 +39,13 @@
 	req.Header.Set("Content-Type", "application/json")
 }
 
+func asContentType(req *http.Request, contentType string) {
+	req.Header.Set("Content-Type", contentType)
+}
+
 func ensureWorkspace() error {
 	var (
-		url      = config.GeoServerURL()
+		geoURL   = config.GeoServerURL()
 		user     = config.GeoServerUser()
 		password = config.GeoServerPassword()
 		auth     = basicAuth(user, password)
@@ -48,7 +54,7 @@
 	// Probe  workspace.
 	req, err := http.NewRequest(
 		http.MethodGet,
-		url+"/rest/workspaces/"+workspaceName+".json",
+		geoURL+"/rest/workspaces/"+workspaceName+".json",
 		nil)
 	if err != nil {
 		return err
@@ -72,7 +78,7 @@
 
 	req, err = http.NewRequest(
 		http.MethodPost,
-		url+"/rest/workspaces",
+		geoURL+"/rest/workspaces",
 		strings.NewReader(createJSON))
 	if err != nil {
 		return err
@@ -94,7 +100,7 @@
 
 func ensureDataStore() error {
 	var (
-		url      = config.GeoServerURL()
+		geoURL   = config.GeoServerURL()
 		user     = config.GeoServerUser()
 		password = config.GeoServerPassword()
 		auth     = basicAuth(user, password)
@@ -103,7 +109,7 @@
 	// Probe datastore.
 	req, err := http.NewRequest(
 		http.MethodGet,
-		url+"/rest/workspaces/"+workspaceName+"/datastores/"+datastoreName+".json",
+		geoURL+"/rest/workspaces/"+workspaceName+"/datastores/"+datastoreName+".json",
 		nil)
 	if err != nil {
 		return err
@@ -154,7 +160,7 @@
 
 	req, err = http.NewRequest(
 		http.MethodPost,
-		url+"/rest/workspaces/"+workspaceName+"/datastores",
+		geoURL+"/rest/workspaces/"+workspaceName+"/datastores",
 		bytes.NewReader(out.Bytes()))
 	if err != nil {
 		return err
@@ -177,7 +183,7 @@
 
 func ensureFeatures() error {
 	var (
-		url      = config.GeoServerURL()
+		geoURL   = config.GeoServerURL()
 		user     = config.GeoServerUser()
 		password = config.GeoServerPassword()
 		auth     = basicAuth(user, password)
@@ -211,7 +217,7 @@
 	// Fetch all featuretypes.
 	req, err := http.NewRequest(
 		http.MethodGet,
-		url+"/rest/workspaces/"+workspaceName+
+		geoURL+"/rest/workspaces/"+workspaceName+
 			"/datastores/"+datastoreName+
 			"/featuretypes.json",
 		nil)
@@ -263,7 +269,7 @@
 
 		req, err := http.NewRequest(
 			http.MethodPost,
-			url+"/rest/workspaces/"+workspaceName+
+			geoURL+"/rest/workspaces/"+workspaceName+
 				"/datastores/"+datastoreName+
 				"/featuretypes",
 			bytes.NewReader(out.Bytes()))
@@ -296,7 +302,7 @@
 
 	log.Println("info: delete workspace " + workspaceName)
 	var (
-		url      = config.GeoServerURL()
+		geoURL   = config.GeoServerURL()
 		user     = config.GeoServerUser()
 		password = config.GeoServerPassword()
 		auth     = basicAuth(user, password)
@@ -304,7 +310,7 @@
 
 	req, err := http.NewRequest(
 		http.MethodDelete,
-		url+"/rest/workspaces/"+workspaceName+"?recurse=true",
+		geoURL+"/rest/workspaces/"+workspaceName+"?recurse=true",
 		nil)
 	if err != nil {
 		return err
@@ -317,11 +323,111 @@
 func ensureStyles() error {
 	log.Println("info: creating styles")
 
-	// TODO: Implement me!
+	var (
+		geoURL   = config.GeoServerURL()
+		user     = config.GeoServerUser()
+		password = config.GeoServerPassword()
+		auth     = basicAuth(user, password)
+	)
+
+	type styles struct {
+		Styles struct {
+			Style []struct {
+				Name string `json:"name"`
+			} `json:"style"`
+		} `json:"styles"`
+	}
+
+	var stls styles
+
+	hasStyle := func(name string) bool {
+		for i := range stls.Styles.Style {
+			if stls.Styles.Style[i].Name == name {
+				return true
+			}
+		}
+		return false
+	}
+
+	req, err := http.NewRequest(
+		http.MethodGet,
+		geoURL+"/rest/workspaces/"+workspaceName+"/styles.json",
+		nil)
+	if err != nil {
+		return err
+	}
+	auth(req)
+	resp, err := http.DefaultClient.Do(req)
+	if err != nil {
+		return err
+	}
+
+	// Fetch all styles
+	if err := json.NewDecoder(resp.Body).Decode(&stls); err != nil {
+		// XXX: Same quirk as with featuretypes.
+	}
+	resp.Body.Close()
+
+	entries := models.InternalServices.Filter(
+		models.IntAnd(models.IntWMS, models.IntWithStyle))
+
+	for i := range entries {
+		if hasStyle(entries[i].Name) {
+			log.Printf("already has style for %s\n", entries[i].Name)
+			continue
+		}
+
+		log.Printf("creating style %s\n", entries[i].Name)
+
+		req, err := http.NewRequest(
+			http.MethodPost,
+			geoURL+"/rest/workspaces/"+workspaceName+"/styles?name="+
+				url.QueryEscape(entries[i].Name),
+			strings.NewReader(entries[i].Style.String))
+		if err != nil {
+			return err
+		}
+		auth(req)
+		if isSymbologyEncoding(entries[i].Style.String) {
+			asContentType(req, "application/vnd.ogc.se+xml")
+		} else {
+			asContentType(req, "application/vnd.ogc.sld+xml")
+		}
+		resp, err := http.DefaultClient.Do(req)
+		if err != nil {
+			return err
+		}
+
+		if resp.StatusCode != http.StatusCreated {
+			return fmt.Errorf("cannot create style %s (%s)",
+				entries[i].Name, http.StatusText(resp.StatusCode))
+		}
+
+		// TODO: Connect with feature.
+	}
 
 	return nil
 }
 
+// isSymbologyEncoding tries to figure out if its plain SLD or SE.
+func isSymbologyEncoding(data string) bool {
+	decoder := xml.NewDecoder(strings.NewReader(data))
+	for {
+		tok, err := decoder.Token()
+		switch {
+		case tok == nil && err == io.EOF:
+			return false
+		case err != nil:
+			log.Printf("warn: invalid XML: %v\n", err)
+			return false
+		}
+		if t, ok := tok.(xml.StartElement); ok &&
+			t.Name.Space == "http://www.opengis.net/se" {
+			return true
+		}
+	}
+}
+
 func prepareGeoServer() error {
 
 	if config.DBUser() == "" {
--- a/pkg/models/intservices.go	Sun Sep 30 18:15:11 2018 +0200
+++ b/pkg/models/intservices.go	Sun Sep 30 19:42:16 2018 +0200
@@ -155,6 +155,10 @@
 	return func(entry IntEntry) bool { return a(entry) && b(entry) }
 }
 
+func IntWithStyle(entry IntEntry) bool {
+	return entry.Style.Valid
+}
+
 func (ps *IntServices) Filter(accept func(IntEntry) bool) []IntEntry {
 	ps.mu.Lock()
 	defer ps.mu.Unlock()