# HG changeset patch # User Sascha L. Teichmann # Date 1538329336 -7200 # Node ID 8b9bd9ccdd93a9763cfdf270f1ee7392bd8f2d83 # Parent 371c756f0370649b07b49f7a6addcbe520cf8dff Upload style during boot. TODO: Connect with layer. diff -r 371c756f0370 -r 8b9bd9ccdd93 pkg/geoserver/boot.go --- 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() == "" { diff -r 371c756f0370 -r 8b9bd9ccdd93 pkg/models/intservices.go --- 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()