diff --git a/client/client.go b/client/client.go
index 4e03c7a..16a3679 100644
--- a/client/client.go
+++ b/client/client.go
@@ -28,12 +28,24 @@ func NewClient(url, username, password string) (client *Client, err error) {
}
func (c *Client) doRequest(method, path string, data io.Reader) (statusCode int, body string, err error) {
+ return c.doTypedRequest(method, path, data, "application/xml")
+}
+
+func (c *Client) doTypedRequest(method, path string, data io.Reader, contentType string) (statusCode int, body string, err error) {
+ return c.doFullyTypedRequest(method, path, data, contentType, contentType)
+}
+
+func (c *Client) doFullyTypedRequest(method, path string, data io.Reader, contentType string, acceptType string) (statusCode int, body string, err error) {
request, err := http.NewRequest(method, c.URL+path, data)
if err != nil {
return
}
- request.Header.Set("Content-Type", "application/xml")
- request.Header.Set("Accept", "application/xml")
+ if contentType != "" {
+ request.Header.Set("Content-Type", contentType)
+ }
+ if acceptType != "" {
+ request.Header.Set("Accept", acceptType)
+ }
if c.Username != "" && c.Password != "" {
request.SetBasicAuth(c.Username, c.Password)
}
diff --git a/client/datastore.go b/client/datastore.go
index 592b4c4..e402e79 100644
--- a/client/datastore.go
+++ b/client/datastore.go
@@ -6,7 +6,7 @@ import (
"fmt"
)
-// DatastoreRef is a reference to a Datastore
+// DatastoreReference is a reference to a Datastore
type DatastoreReference struct {
Name string `xml:"name"`
}
diff --git a/client/datastore_test.go b/client/datastore_test.go
index 1217b2c..130357f 100644
--- a/client/datastore_test.go
+++ b/client/datastore_test.go
@@ -53,18 +53,18 @@ func TestGetDatastoresSuccess(t *testing.T) {
defer testServer.Close()
expectedResult := []*Datastore{
- &Datastore{
+ {
XMLName: xml.Name{
Local: "dataStore",
},
Name: "sf",
Enabled: true,
ConnectionParameters: []*DatastoreConnectionParameter{
- &DatastoreConnectionParameter{
+ {
Key: "url",
Value: "file:data/sf",
},
- &DatastoreConnectionParameter{
+ {
Key: "namespace",
Value: "http://www.openplans.org/spearfish",
},
@@ -123,11 +123,11 @@ func TestGetDatastoreSuccess(t *testing.T) {
Name: "sf",
Enabled: true,
ConnectionParameters: []*DatastoreConnectionParameter{
- &DatastoreConnectionParameter{
+ {
Key: "url",
Value: "file:data/sf",
},
- &DatastoreConnectionParameter{
+ {
Key: "namespace",
Value: "http://www.openplans.org/spearfish",
},
@@ -229,11 +229,11 @@ func TestCreateDatastoreSuccess(t *testing.T) {
Name: "sf",
Enabled: true,
ConnectionParameters: []*DatastoreConnectionParameter{
- &DatastoreConnectionParameter{
+ {
Key: "url",
Value: "file:data/sf",
},
- &DatastoreConnectionParameter{
+ {
Key: "namespace",
Value: "http://www.openplans.org/spearfish",
},
@@ -254,11 +254,11 @@ func TestCreateDatastoreSuccess(t *testing.T) {
Name: "sf",
Enabled: true,
ConnectionParameters: []*DatastoreConnectionParameter{
- &DatastoreConnectionParameter{
+ {
Key: "url",
Value: "file:data/sf",
},
- &DatastoreConnectionParameter{
+ {
Key: "namespace",
Value: "http://www.openplans.org/spearfish",
},
@@ -285,11 +285,11 @@ func TestUpdateDatastoreSuccess(t *testing.T) {
Name: "sf",
Enabled: true,
ConnectionParameters: []*DatastoreConnectionParameter{
- &DatastoreConnectionParameter{
+ {
Key: "url",
Value: "file:data/sf",
},
- &DatastoreConnectionParameter{
+ {
Key: "namespace",
Value: "http://www.openplans.org/spearfish",
},
@@ -310,11 +310,11 @@ func TestUpdateDatastoreSuccess(t *testing.T) {
Name: "sf",
Enabled: true,
ConnectionParameters: []*DatastoreConnectionParameter{
- &DatastoreConnectionParameter{
+ {
Key: "url",
Value: "file:data/sf",
},
- &DatastoreConnectionParameter{
+ {
Key: "namespace",
Value: "http://www.openplans.org/spearfish",
},
diff --git a/client/featuretype.go b/client/featuretype.go
index 9824e04..9a52702 100644
--- a/client/featuretype.go
+++ b/client/featuretype.go
@@ -17,11 +17,13 @@ type FeatureTypeKeywords struct {
Keywords []string `xml:"string"`
}
+// FeatureTypeCRS describes CRS information
type FeatureTypeCRS struct {
Class string `xml:"class,attr,omitempty"`
Value string `xml:",chardata"`
}
+// FeatureTypes is a list of FeatureType
type FeatureTypes struct {
XMLName xml.Name `xml:"featureTypes"`
List []*FeatureType `xml:"featureType"`
diff --git a/client/featuretype_test.go b/client/featuretype_test.go
index f742c41..846e1a7 100644
--- a/client/featuretype_test.go
+++ b/client/featuretype_test.go
@@ -39,7 +39,7 @@ func TestGetFeatureTypesNoDatastoreSuccess(t *testing.T) {
defer testServer.Close()
expectedResult := []*FeatureType{
- &FeatureType{
+ {
XMLName: xml.Name{
Space: "",
Local: "featureType",
@@ -88,7 +88,7 @@ func TestGetFeatureTypesInDatastoreSuccess(t *testing.T) {
defer testServer.Close()
expectedResult := []*FeatureType{
- &FeatureType{
+ {
XMLName: xml.Name{
Local: "featureType",
},
@@ -241,17 +241,17 @@ func TestGetFeatureTypeNoDatastoreSuccess(t *testing.T) {
ProjectionPolicy: "NONE",
Enabled: true,
Metadata: []*FeatureTypeMetadata{
- &FeatureTypeMetadata{
+ {
Key: "time",
Value: "false",
},
- &FeatureTypeMetadata{
+ {
Key: "cachingEnabled",
Value: "true",
},
},
Attributes: []*FeatureTypeAttribute{
- &FeatureTypeAttribute{
+ {
Name: "the_geom",
MinOccurs: 0,
MaxOccurs: 1,
@@ -406,17 +406,17 @@ func TestGetFeatureTypeInDatastoreSuccess(t *testing.T) {
ProjectionPolicy: "NONE",
Enabled: true,
Metadata: []*FeatureTypeMetadata{
- &FeatureTypeMetadata{
+ {
Key: "time",
Value: "false",
},
- &FeatureTypeMetadata{
+ {
Key: "cachingEnabled",
Value: "true",
},
},
Attributes: []*FeatureTypeAttribute{
- &FeatureTypeAttribute{
+ {
Name: "the_geom",
MinOccurs: 0,
MaxOccurs: 1,
@@ -554,17 +554,17 @@ func TestCreateFeatureTypeNoDatastoreSuccess(t *testing.T) {
ProjectionPolicy: "NONE",
Enabled: true,
Metadata: []*FeatureTypeMetadata{
- &FeatureTypeMetadata{
+ {
Key: "time",
Value: "false",
},
- &FeatureTypeMetadata{
+ {
Key: "cachingEnabled",
Value: "true",
},
},
Attributes: []*FeatureTypeAttribute{
- &FeatureTypeAttribute{
+ {
Name: "the_geom",
MinOccurs: 0,
MaxOccurs: 1,
@@ -628,17 +628,17 @@ func TestCreateFeatureTypeNoDatastoreSuccess(t *testing.T) {
ProjectionPolicy: "NONE",
Enabled: true,
Metadata: []*FeatureTypeMetadata{
- &FeatureTypeMetadata{
+ {
Key: "time",
Value: "false",
},
- &FeatureTypeMetadata{
+ {
Key: "cachingEnabled",
Value: "true",
},
},
Attributes: []*FeatureTypeAttribute{
- &FeatureTypeAttribute{
+ {
Name: "the_geom",
MinOccurs: 0,
MaxOccurs: 1,
@@ -705,17 +705,17 @@ func TestCreateFeatureTypeInDatastoreSuccess(t *testing.T) {
ProjectionPolicy: "NONE",
Enabled: true,
Metadata: []*FeatureTypeMetadata{
- &FeatureTypeMetadata{
+ {
Key: "time",
Value: "false",
},
- &FeatureTypeMetadata{
+ {
Key: "cachingEnabled",
Value: "true",
},
},
Attributes: []*FeatureTypeAttribute{
- &FeatureTypeAttribute{
+ {
Name: "the_geom",
MinOccurs: 0,
MaxOccurs: 1,
@@ -779,17 +779,17 @@ func TestCreateFeatureTypeInDatastoreSuccess(t *testing.T) {
ProjectionPolicy: "NONE",
Enabled: true,
Metadata: []*FeatureTypeMetadata{
- &FeatureTypeMetadata{
+ {
Key: "time",
Value: "false",
},
- &FeatureTypeMetadata{
+ {
Key: "cachingEnabled",
Value: "true",
},
},
Attributes: []*FeatureTypeAttribute{
- &FeatureTypeAttribute{
+ {
Name: "the_geom",
MinOccurs: 0,
MaxOccurs: 1,
@@ -856,17 +856,17 @@ func TestUpdateFeatureTypeNoDatastoreSuccess(t *testing.T) {
ProjectionPolicy: "NONE",
Enabled: true,
Metadata: []*FeatureTypeMetadata{
- &FeatureTypeMetadata{
+ {
Key: "time",
Value: "false",
},
- &FeatureTypeMetadata{
+ {
Key: "cachingEnabled",
Value: "true",
},
},
Attributes: []*FeatureTypeAttribute{
- &FeatureTypeAttribute{
+ {
Name: "the_geom",
MinOccurs: 0,
MaxOccurs: 1,
@@ -930,17 +930,17 @@ func TestUpdateFeatureTypeNoDatastoreSuccess(t *testing.T) {
ProjectionPolicy: "NONE",
Enabled: true,
Metadata: []*FeatureTypeMetadata{
- &FeatureTypeMetadata{
+ {
Key: "time",
Value: "false",
},
- &FeatureTypeMetadata{
+ {
Key: "cachingEnabled",
Value: "true",
},
},
Attributes: []*FeatureTypeAttribute{
- &FeatureTypeAttribute{
+ {
Name: "the_geom",
MinOccurs: 0,
MaxOccurs: 1,
diff --git a/client/style.go b/client/style.go
new file mode 100644
index 0000000..e23f57a
--- /dev/null
+++ b/client/style.go
@@ -0,0 +1,300 @@
+package client
+
+import (
+ "bytes"
+ "encoding/xml"
+ "fmt"
+ "strings"
+)
+
+// Styles is a list of Style
+type Styles struct {
+ XMLName xml.Name `xml:"styles"`
+ List []*Style `xml:"style"`
+}
+
+// Style is GeoServer Resource
+type Style struct {
+ XMLName xml.Name `xml:"style"`
+ Name string `xml:"name"`
+ Workspace *WorkspaceRef `xml:"workspace,omitempty"`
+ Format string `xml:"format,omitempty"`
+ Version *LanguageVersion `xml:"languageVersion,omitempty"`
+ FileName string `xml:"filename"`
+}
+
+// WorkspaceRef is a reference to a GeoServer workspace
+type WorkspaceRef struct {
+ Name string `xml:"name,omitempty"`
+}
+
+// LanguageVersion is the version of the language used to described the style
+type LanguageVersion struct {
+ Version string `xml:"version,omitempty"`
+}
+
+// GetHTTPContentTypeFor computes the content type of a http request for the required format and version
+func (c *Client) GetHTTPContentTypeFor(format string, version string) (contentType string) {
+ switch format {
+ case "sld":
+ if version == "1.0.0" {
+ return "application/vnd.ogc.sld+xml"
+ }
+ return "application/vnd.ogc.se+xml "
+ case "css":
+ return "application/vnd.geoserver.geocss+css"
+ case "yaml":
+ return "application/vnd.geoserver.ysld+yaml"
+ case "json":
+ return "application/vnd.geoserver.mbstyle+json "
+ default:
+ return "application/vnd.ogc.sld+xml"
+ }
+}
+
+// GetStyles returns all the styles
+func (c *Client) GetStyles(workspace string) (styles []*Style, err error) {
+ var endpoint string
+
+ if workspace == "" {
+ endpoint = fmt.Sprintf("/styles")
+ } else {
+ endpoint = fmt.Sprintf("/workspaces/%s/styles", workspace)
+ }
+
+ statusCode, body, err := c.doRequest("GET", endpoint, nil)
+ if err != nil {
+ return
+ }
+
+ switch statusCode {
+ case 401:
+ err = fmt.Errorf("unauthorized")
+ return
+ case 200:
+ break
+ default:
+ err = fmt.Errorf("unknown error: %d - %s", statusCode, body)
+ return
+ }
+
+ var data Styles
+
+ if err := xml.Unmarshal([]byte(body), &data); err != nil {
+ return styles, err
+ }
+
+ for _, styleRef := range data.List {
+ style, err := c.GetStyle(workspace, styleRef.Name)
+ if err != nil {
+ return styles, err
+ }
+
+ styles = append(styles, style)
+ }
+
+ return
+}
+
+// GetStyle return a single style based on its name
+func (c *Client) GetStyle(workspace, name string) (style *Style, err error) {
+ var endpoint string
+
+ if workspace == "" {
+ endpoint = fmt.Sprintf("/styles/%s", name)
+ } else {
+ endpoint = fmt.Sprintf("/workspaces/%s/styles/%s", workspace, name)
+ }
+
+ statusCode, body, err := c.doRequest("GET", endpoint, nil)
+ if err != nil {
+ return
+ }
+
+ switch statusCode {
+ case 401:
+ err = fmt.Errorf("unauthorized")
+ return
+ case 404:
+ err = fmt.Errorf("not found")
+ return
+ case 200:
+ break
+ default:
+ err = fmt.Errorf("unknown error: %d - %s", statusCode, body)
+ return
+ }
+
+ var data Style
+ if err := xml.Unmarshal([]byte(body), &data); err != nil {
+ return style, err
+ }
+
+ style = &data
+
+ return
+}
+
+// GetStyleFile retrieves the style definition of a given format
+func (c *Client) GetStyleFile(workspace, name string, styleFormat string, formatVersion string) (styleFile string, err error) {
+ var endpoint string
+
+ if workspace == "" {
+ endpoint = fmt.Sprintf("/styles/%s", name)
+ } else {
+ endpoint = fmt.Sprintf("/workspaces/%s/styles/%s", workspace, name)
+ }
+
+ // Try to retrieve the style file based on the style format
+ contentType := c.GetHTTPContentTypeFor(styleFormat, formatVersion)
+
+ statusCode, styleFile, err := c.doTypedRequest("GET", endpoint, nil, contentType)
+ if err != nil {
+ return
+ }
+
+ switch statusCode {
+ case 401:
+ err = fmt.Errorf("unauthorized")
+ return
+ case 404:
+ err = fmt.Errorf("not found")
+ return
+ case 200:
+ break
+ default:
+ err = fmt.Errorf("unknown error: %d - %s", statusCode, styleFile)
+ return
+ }
+
+ return styleFile, err
+}
+
+// CreateStyle creates a style
+func (c *Client) CreateStyle(workspace string, style *Style) (err error) {
+ var endpoint string
+
+ if workspace == "" {
+ endpoint = fmt.Sprintf("/styles")
+ } else {
+ endpoint = fmt.Sprintf("/workspaces/%s/styles", workspace)
+ }
+
+ style.XMLName = xml.Name{
+ Local: "style",
+ }
+ payload, err := xml.Marshal(style)
+ if err != nil {
+ return
+ }
+ statusCode, body, err := c.doFullyTypedRequest("POST", endpoint, bytes.NewBuffer(payload), "application/xml", "")
+
+ if err != nil {
+ return
+ }
+
+ switch statusCode {
+ case 401:
+ err = fmt.Errorf("unauthorized")
+ return
+ case 201:
+ return
+ default:
+ err = fmt.Errorf("unknown error: %d - %s - %s", statusCode, body, string(payload))
+ return
+ }
+}
+
+// UpdateStyle creates a style
+func (c *Client) UpdateStyle(workspace string, style *Style, styleDefinition string) (err error) {
+ var endpoint string
+
+ if workspace == "" {
+ endpoint = fmt.Sprintf("/styles")
+ } else {
+ endpoint = fmt.Sprintf("/workspaces/%s/styles", workspace)
+ }
+
+ contentType := c.GetHTTPContentTypeFor(style.Format, style.Version.Version)
+
+ statusCode, body, err := c.doFullyTypedRequest("POST", endpoint, strings.NewReader(styleDefinition), contentType, "")
+ if err != nil {
+ return
+ }
+
+ switch statusCode {
+ case 401:
+ err = fmt.Errorf("unauthorized")
+ return
+ case 201:
+ return
+ default:
+ err = fmt.Errorf("unknown error: %d - %s - %s", statusCode, body, styleDefinition)
+ return
+ }
+}
+
+// UpdateStyleContent changes the style definition
+func (c *Client) UpdateStyleContent(workspace string, style *Style, styleDefinition string) (err error) {
+ var endpoint string
+
+ if workspace == "" {
+ endpoint = fmt.Sprintf("/styles/%s", style.Name)
+ } else {
+ endpoint = fmt.Sprintf("/workspaces/%s/styles/%s", workspace, style.Name)
+ }
+
+ contentType := c.GetHTTPContentTypeFor(style.Format, style.Version.Version)
+
+ statusCode, body, err := c.doFullyTypedRequest("PUT", endpoint, strings.NewReader(styleDefinition), contentType, "")
+ if err != nil {
+ return
+ }
+
+ switch statusCode {
+ case 401:
+ err = fmt.Errorf("unauthorized")
+ return
+ case 200:
+ return
+ default:
+ err = fmt.Errorf("unknown error: %d - %s - %s", statusCode, body, styleDefinition)
+ return
+ }
+}
+
+// DeleteStyle deletes style from GeoServer
+func (c *Client) DeleteStyle(workspace string, style string, purge bool, recurse bool) (err error) {
+ var endpoint string
+
+ if workspace == "" {
+ endpoint = fmt.Sprintf("/styles/%s?purge=%t&recurse=%t", style, purge, recurse)
+ } else {
+ endpoint = fmt.Sprintf("/workspaces/%s/styles/%s?purge=%t&recurse=%t", workspace, style, purge, recurse)
+ }
+
+ statusCode, body, err := c.doRequest("DELETE", endpoint, nil)
+ if err != nil {
+ return
+ }
+
+ switch statusCode {
+ case 401:
+ err = fmt.Errorf("unauthorized")
+ return
+ case 403:
+ err = fmt.Errorf("workspace is not empty")
+ return
+ case 404:
+ err = fmt.Errorf("not found")
+ return
+ case 405:
+ err = fmt.Errorf("forbidden")
+ return
+ case 200:
+ return
+ default:
+ err = fmt.Errorf("unknown error: %d - %s", statusCode, body)
+ return
+ }
+}
diff --git a/client/style_test.go b/client/style_test.go
new file mode 100644
index 0000000..6e2dead
--- /dev/null
+++ b/client/style_test.go
@@ -0,0 +1,554 @@
+package client
+
+import (
+ "encoding/xml"
+ "io/ioutil"
+ "net/http"
+ "net/http/httptest"
+ "testing"
+
+ "github.com/stretchr/testify/assert"
+)
+
+func TestGetStylesNoWorkspaceSuccess(t *testing.T) {
+ mux := http.NewServeMux()
+ mux.HandleFunc("/styles", func(w http.ResponseWriter, r *http.Request) {
+ assert.Equal(t, r.Method, "GET")
+
+ w.WriteHeader(200)
+ w.Write([]byte(`
+
+
+
+ `))
+ })
+ mux.HandleFunc("/styles/line", func(w http.ResponseWriter, r *http.Request) {
+ assert.Equal(t, r.Method, "GET")
+
+ w.WriteHeader(200)
+ w.Write([]byte(`
+
+ `))
+ })
+
+ testServer := httptest.NewServer(mux)
+ defer testServer.Close()
+
+ expectedResult := []*Style{
+ {
+ XMLName: xml.Name{Space: "", Local: "style"},
+ Name: "line",
+ Workspace: &WorkspaceRef{},
+ Format: "sld",
+ Version: &LanguageVersion{Version: "1.0.0"},
+ FileName: "default_line.sld",
+ },
+ }
+
+ cli := &Client{
+ URL: testServer.URL,
+ HTTPClient: &http.Client{},
+ }
+
+ styles, err := cli.GetStyles("")
+
+ assert.Nil(t, err)
+ assert.Equal(t, expectedResult, styles)
+}
+
+func TestGetStylesWorkspaceSuccess(t *testing.T) {
+ mux := http.NewServeMux()
+ mux.HandleFunc("/workspaces/foo/styles", func(w http.ResponseWriter, r *http.Request) {
+ assert.Equal(t, r.Method, "GET")
+
+ w.WriteHeader(200)
+ w.Write([]byte(`
+
+
+
+ `))
+ })
+ mux.HandleFunc("/workspaces/foo/styles/line", func(w http.ResponseWriter, r *http.Request) {
+ assert.Equal(t, r.Method, "GET")
+
+ w.WriteHeader(200)
+ w.Write([]byte(`
+
+ `))
+ })
+
+ testServer := httptest.NewServer(mux)
+ defer testServer.Close()
+
+ expectedResult := []*Style{
+ {
+ XMLName: xml.Name{
+ Space: "",
+ Local: "style",
+ },
+ Workspace: &WorkspaceRef{Name: "foo"},
+ Name: "line",
+ Format: "sld",
+ Version: &LanguageVersion{Version: "1.0.0"},
+ FileName: "default_line.sld",
+ },
+ }
+
+ cli := &Client{
+ URL: testServer.URL,
+ HTTPClient: &http.Client{},
+ }
+
+ styles, err := cli.GetStyles("foo")
+
+ assert.Nil(t, err)
+ assert.Equal(t, expectedResult, styles)
+}
+
+func TestGetStyleNoWorkspaceSuccess(t *testing.T) {
+ mux := http.NewServeMux()
+ mux.HandleFunc("/styles/line", func(w http.ResponseWriter, r *http.Request) {
+ assert.Equal(t, r.Method, "GET")
+
+ w.WriteHeader(200)
+ w.Write([]byte(`
+
+ `))
+ })
+
+ testServer := httptest.NewServer(mux)
+ defer testServer.Close()
+
+ expectedResult := &Style{
+ XMLName: xml.Name{
+ Space: "",
+ Local: "style",
+ },
+ Name: "line",
+ Format: "sld",
+ Version: &LanguageVersion{Version: "1.0.0"},
+ FileName: "default_line.sld",
+ }
+
+ cli := &Client{
+ URL: testServer.URL,
+ HTTPClient: &http.Client{},
+ }
+
+ style, err := cli.GetStyle("", "line")
+
+ assert.Nil(t, err)
+ assert.Equal(t, expectedResult, style)
+}
+
+func TestGetStyleWorkspaceSuccess(t *testing.T) {
+ mux := http.NewServeMux()
+ mux.HandleFunc("/workspaces/foo/styles/line", func(w http.ResponseWriter, r *http.Request) {
+ assert.Equal(t, r.Method, "GET")
+
+ w.WriteHeader(200)
+ w.Write([]byte(`
+
+ `))
+ })
+
+ testServer := httptest.NewServer(mux)
+ defer testServer.Close()
+
+ expectedResult := &Style{
+ XMLName: xml.Name{
+ Space: "",
+ Local: "style",
+ },
+ Workspace: &WorkspaceRef{Name: "foo"},
+ Name: "line",
+ Format: "sld",
+ Version: &LanguageVersion{Version: "1.0.0"},
+ FileName: "default_line.sld",
+ }
+
+ cli := &Client{
+ URL: testServer.URL,
+ HTTPClient: &http.Client{},
+ }
+
+ style, err := cli.GetStyle("foo", "line")
+
+ assert.Nil(t, err)
+ assert.Equal(t, expectedResult, style)
+}
+
+func TestGetStyleUnauthorized(t *testing.T) {
+ testServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ assert.Equal(t, r.Method, "GET")
+ assert.Equal(t, r.URL.Path, "/styles/toto")
+
+ w.WriteHeader(401)
+ w.Write([]byte(``))
+ }))
+ defer testServer.Close()
+
+ cli := &Client{
+ URL: testServer.URL,
+ HTTPClient: &http.Client{},
+ }
+
+ style, err := cli.GetStyle("", "toto")
+
+ assert.Error(t, err, "Unauthorized")
+ assert.Nil(t, style)
+}
+
+func TestGetStyleNotFound(t *testing.T) {
+ testServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ assert.Equal(t, r.Method, "GET")
+ assert.Equal(t, r.URL.Path, "/styles/toto")
+
+ w.WriteHeader(404)
+ w.Write([]byte(``))
+ }))
+ defer testServer.Close()
+
+ cli := &Client{
+ URL: testServer.URL,
+ HTTPClient: &http.Client{},
+ }
+
+ style, err := cli.GetStyle("", "toto")
+
+ assert.Error(t, err, "Not Found")
+ assert.Nil(t, style)
+}
+
+func TestGetStyleUnknownError(t *testing.T) {
+ testServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ assert.Equal(t, r.Method, "GET")
+ assert.Equal(t, r.URL.Path, "/styles/toto")
+
+ w.WriteHeader(418)
+ w.Write([]byte(`I'm a teapot!`))
+ }))
+ defer testServer.Close()
+
+ cli := &Client{
+ URL: testServer.URL,
+ HTTPClient: &http.Client{},
+ }
+
+ style, err := cli.GetStyle("", "toto")
+
+ assert.Error(t, err, "Unknown error: 418 - I'm a teapot!")
+ assert.Nil(t, style)
+}
+
+func TestGetStyleSLDNoWorkspaceSuccess(t *testing.T) {
+ mux := http.NewServeMux()
+ mux.HandleFunc("/styles/point", func(w http.ResponseWriter, r *http.Request) {
+ assert.Equal(t, r.Method, "GET")
+
+ w.WriteHeader(200)
+ w.Write([]byte(`
+
+
+
+
+
+ Default Point
+
+
+
+ Red Square point
+ A sample style that just prints out a red square
+
+
+
+
+
+
+ Rule 1
+ Red Square point
+ A red fill with 6 pixels size
+
+
+
+
+
+ square
+
+ #FF0000
+
+
+ 6
+
+
+
+
+
+
+
+
+
+ `))
+ })
+
+ testServer := httptest.NewServer(mux)
+ defer testServer.Close()
+
+ cli := &Client{
+ URL: testServer.URL,
+ HTTPClient: &http.Client{},
+ }
+
+ styleFile, err := cli.GetStyleFile("", "point", "sld", "1.0.0")
+
+ assert.Nil(t, err)
+ assert.NotEmpty(t, styleFile)
+}
+
+func TestGetStyleCssNoWOrkspaceSuccess(t *testing.T) {
+ mux := http.NewServeMux()
+ mux.HandleFunc("/styles/point", func(w http.ResponseWriter, r *http.Request) {
+ assert.Equal(t, r.Method, "GET")
+
+ w.WriteHeader(200)
+ w.Write([]byte(`
+ /* @title teal point */
+ * {
+ mark: symbol(square);
+ mark-size: 6px;
+ :mark {
+ fill: #00cc33;
+ }
+ }
+ `))
+ })
+
+ testServer := httptest.NewServer(mux)
+ defer testServer.Close()
+
+ cli := &Client{
+ URL: testServer.URL,
+ HTTPClient: &http.Client{},
+ }
+
+ styleFile, err := cli.GetStyleFile("", "point", "css", "1.0.0")
+
+ assert.Nil(t, err)
+ assert.NotEmpty(t, styleFile)
+}
+
+func TestCreateStyleNoWorkspaceSuccess(t *testing.T) {
+ testServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ assert.Equal(t, r.Method, "POST")
+ assert.Equal(t, r.URL.Path, "/styles")
+
+ rawBody, err := ioutil.ReadAll(r.Body)
+ assert.Nil(t, err)
+ var payload *Style
+ err = xml.Unmarshal(rawBody, &payload)
+ assert.Nil(t, err)
+ assert.Equal(t, payload, &Style{
+ XMLName: xml.Name{
+ Local: "style",
+ },
+ Name: "test_style",
+ FileName: "test_style.sld",
+ })
+
+ w.WriteHeader(201)
+ w.Write([]byte(``))
+ }))
+ defer testServer.Close()
+
+ cli := &Client{
+ URL: testServer.URL,
+ HTTPClient: &http.Client{},
+ }
+
+ err := cli.CreateStyle("", &Style{
+ XMLName: xml.Name{
+ Local: "style",
+ },
+ Name: "test_style",
+ FileName: "test_style.sld",
+ })
+
+ assert.Nil(t, err)
+}
+
+func TestUpdateStyleContentSldSuccess(t *testing.T) {
+ const styleDefinition = `
+
+
+
+ go_test
+
+ A teal polygon style
+
+
+ teal polygon
+
+
+ #00cc33
+
+
+
+ #000000
+ 0.5
+
+
+
+
+
+
+
+
+
+
+ `
+ testServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ assert.Equal(t, r.Method, "PUT")
+ assert.Equal(t, r.URL.Path, "/styles/toto")
+ assert.Equal(t, r.Header.Get("Content-Type"), "application/vnd.ogc.sld+xml")
+
+ rawBody, err := ioutil.ReadAll(r.Body)
+ assert.Nil(t, err)
+ assert.Equal(t, string(rawBody), styleDefinition)
+
+ w.WriteHeader(200)
+ w.Write([]byte(``))
+ }))
+ defer testServer.Close()
+
+ cli := &Client{
+ URL: testServer.URL,
+ HTTPClient: &http.Client{},
+ }
+
+ styleToCreate := &Style{
+ XMLName: xml.Name{
+ Local: "style",
+ },
+ Name: "toto",
+ FileName: "test_style.sld",
+ Format: "sld",
+ Version: &LanguageVersion{Version: "1.0.0"},
+ }
+
+ err := cli.UpdateStyleContent("", styleToCreate, styleDefinition)
+
+ assert.Nil(t, err)
+}
+
+func TestUpdateStyleContentCssSuccess(t *testing.T) {
+ const styleDefinition = `
+ /* @title cyan line */
+ * {
+ stroke: #0099cc;
+ }
+ `
+ testServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ assert.Equal(t, r.Method, "PUT")
+ assert.Equal(t, r.URL.Path, "/styles/toto")
+ assert.Equal(t, r.Header.Get("Content-Type"), "application/vnd.geoserver.geocss+css")
+
+ rawBody, err := ioutil.ReadAll(r.Body)
+ assert.Nil(t, err)
+ assert.Equal(t, string(rawBody), styleDefinition)
+
+ w.WriteHeader(200)
+ w.Write([]byte(``))
+ }))
+ defer testServer.Close()
+
+ cli := &Client{
+ URL: testServer.URL,
+ HTTPClient: &http.Client{},
+ }
+
+ styleToCreate := &Style{
+ XMLName: xml.Name{
+ Local: "style",
+ },
+ Name: "toto",
+ FileName: "test_style.css",
+ Format: "css",
+ Version: &LanguageVersion{Version: "1.0.0"},
+ }
+
+ err := cli.UpdateStyleContent("", styleToCreate, styleDefinition)
+
+ assert.Nil(t, err)
+}
+
+func TestDeleteStyleSuccess(t *testing.T) {
+ testServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ assert.Equal(t, r.Method, "DELETE")
+ assert.Equal(t, r.URL.Path, "/styles/toto")
+ keys, ok := r.URL.Query()["recurse"]
+ assert.True(t, ok)
+ assert.Equal(t, keys[0], "true")
+ keys2, ok2 := r.URL.Query()["purge"]
+ assert.True(t, ok2)
+ assert.Equal(t, keys2[0], "true")
+
+ w.WriteHeader(200)
+ w.Write([]byte(``))
+ }))
+ defer testServer.Close()
+
+ cli := &Client{
+ URL: testServer.URL,
+ HTTPClient: &http.Client{},
+ }
+
+ err := cli.DeleteStyle("", "toto", true, true)
+
+ assert.Nil(t, err)
+}
diff --git a/client/workspaces_test.go b/client/workspaces_test.go
index f5a7a54..d078135 100644
--- a/client/workspaces_test.go
+++ b/client/workspaces_test.go
@@ -30,10 +30,10 @@ func TestGetWorkspacesSuccess(t *testing.T) {
defer testServer.Close()
expectedResult := []*Workspace{
- &Workspace{
+ {
Name: "topp",
},
- &Workspace{
+ {
Name: "it.geosolutions",
},
}
diff --git a/readme.md b/readme.md
new file mode 100644
index 0000000..38925df
--- /dev/null
+++ b/readme.md
@@ -0,0 +1,4 @@
+# Requirements
+
+- Install the Go package as explained on https://go.dev/doc/install
+- Install the golint tool as explained on https://github.com/golang/lint