From 487dc2107045c0420a52e51e968e6bf703be9950 Mon Sep 17 00:00:00 2001 From: totpero Date: Sat, 2 Dec 2023 17:30:36 +0200 Subject: [PATCH] new GeoServer .Net Client example --- GeoServer.Net.sln | 45 +++++++++++ README.md | 3 + src/GeoServer.Net/Constants/ApiConsts.cs | 34 +++++++++ .../Contracts/IGeoServerAboutClient.cs | 26 +++++++ .../Contracts/IGeoServerClient.cs | 3 + .../Contracts/IGeoServerLayersClient.cs | 16 ++++ .../GeoServerHttpRequestException.cs | 3 + src/GeoServer.Net/GeoServer.Net.csproj | 12 +++ src/GeoServer.Net/GeoServerClient.cs | 74 +++++++++++++++++++ .../About/Base/GeoServerAboutBaseResponse.cs | 9 +++ .../About/Base/GeoServerAboutResponse.cs | 9 +++ .../About/GeoServerAboutManifestResponse.cs | 21 ++++++ .../GeoServerAboutSystemStatusResponse.cs | 35 +++++++++ .../About/GeoServerAboutVersionResponse.cs | 22 ++++++ .../Layers/GeoServerLayersListResponse.cs | 24 ++++++ .../AboutEndpointsTests.cs | 33 +++++++++ .../GeneralConfigurations.cs | 8 ++ .../GeoServer.Net.Tests.csproj | 30 ++++++++ test/GeoServer.Net.Tests/GlobalUsings.cs | 1 + .../LayersEndpointsTests.cs | 17 +++++ 20 files changed, 425 insertions(+) create mode 100644 GeoServer.Net.sln create mode 100644 src/GeoServer.Net/Constants/ApiConsts.cs create mode 100644 src/GeoServer.Net/Contracts/IGeoServerAboutClient.cs create mode 100644 src/GeoServer.Net/Contracts/IGeoServerClient.cs create mode 100644 src/GeoServer.Net/Contracts/IGeoServerLayersClient.cs create mode 100644 src/GeoServer.Net/Exceptions/GeoServerHttpRequestException.cs create mode 100644 src/GeoServer.Net/GeoServer.Net.csproj create mode 100644 src/GeoServer.Net/GeoServerClient.cs create mode 100644 src/GeoServer.Net/Responses/About/Base/GeoServerAboutBaseResponse.cs create mode 100644 src/GeoServer.Net/Responses/About/Base/GeoServerAboutResponse.cs create mode 100644 src/GeoServer.Net/Responses/About/GeoServerAboutManifestResponse.cs create mode 100644 src/GeoServer.Net/Responses/About/GeoServerAboutSystemStatusResponse.cs create mode 100644 src/GeoServer.Net/Responses/About/GeoServerAboutVersionResponse.cs create mode 100644 src/GeoServer.Net/Responses/Layers/GeoServerLayersListResponse.cs create mode 100644 test/GeoServer.Net.Tests/AboutEndpointsTests.cs create mode 100644 test/GeoServer.Net.Tests/GeneralConfigurations.cs create mode 100644 test/GeoServer.Net.Tests/GeoServer.Net.Tests.csproj create mode 100644 test/GeoServer.Net.Tests/GlobalUsings.cs create mode 100644 test/GeoServer.Net.Tests/LayersEndpointsTests.cs diff --git a/GeoServer.Net.sln b/GeoServer.Net.sln new file mode 100644 index 0000000..6f24df9 --- /dev/null +++ b/GeoServer.Net.sln @@ -0,0 +1,45 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.8.34322.80 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "GeoServer.Net", "src\GeoServer.Net\GeoServer.Net.csproj", "{FFFBE134-B411-439C-9B06-0580A261CFD4}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{EDE9678A-A3B8-489A-9559-3E1CCFB5E1E5}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{F6F66F4D-A0D3-4D09-9590-027DEBB7321A}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GeoServer.Net.Tests", "test\GeoServer.Net.Tests\GeoServer.Net.Tests.csproj", "{3964813D-42DD-4DD1-BF8F-02B153460E22}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{74CC736A-110C-46D4-AC52-90A9CC701B6F}" + ProjectSection(SolutionItems) = preProject + LICENSE = LICENSE + README.md = README.md + EndProjectSection +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {FFFBE134-B411-439C-9B06-0580A261CFD4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {FFFBE134-B411-439C-9B06-0580A261CFD4}.Debug|Any CPU.Build.0 = Debug|Any CPU + {FFFBE134-B411-439C-9B06-0580A261CFD4}.Release|Any CPU.ActiveCfg = Release|Any CPU + {FFFBE134-B411-439C-9B06-0580A261CFD4}.Release|Any CPU.Build.0 = Release|Any CPU + {3964813D-42DD-4DD1-BF8F-02B153460E22}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {3964813D-42DD-4DD1-BF8F-02B153460E22}.Debug|Any CPU.Build.0 = Debug|Any CPU + {3964813D-42DD-4DD1-BF8F-02B153460E22}.Release|Any CPU.ActiveCfg = Release|Any CPU + {3964813D-42DD-4DD1-BF8F-02B153460E22}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {FFFBE134-B411-439C-9B06-0580A261CFD4} = {EDE9678A-A3B8-489A-9559-3E1CCFB5E1E5} + {3964813D-42DD-4DD1-BF8F-02B153460E22} = {F6F66F4D-A0D3-4D09-9590-027DEBB7321A} + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {79ADD1A3-B69D-4589-A7B5-4FD1AC02A17A} + EndGlobalSection +EndGlobal diff --git a/README.md b/README.md index d3d0618..ae72c40 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,5 @@ # GeoServer.Net .Net Client for geoserver https://github.com/geoserver/geoserver + +# GeoServer API documentation: +https://docs.geoserver.org/stable/en/user/rest/index.html \ No newline at end of file diff --git a/src/GeoServer.Net/Constants/ApiConsts.cs b/src/GeoServer.Net/Constants/ApiConsts.cs new file mode 100644 index 0000000..9859a78 --- /dev/null +++ b/src/GeoServer.Net/Constants/ApiConsts.cs @@ -0,0 +1,34 @@ +namespace GeoServer.Net.Constants; + +public static class ApiConsts +{ + public const string Version = "2.24.1"; + public const string OutputFormat = ".json"; + + public static class Endpoints + { + //public const string BaseApiUrl = "http://localhost:8080/geoserver"; + //public const string BaseApiPath = BaseApiUrl + "/rest"; + public const string BaseApiPath = "/rest"; + + /// + /// The REST API allows you to set and retrieve information about the server itself. + /// + public static class About + { + public const string BaseAboutApiPath = BaseApiPath + "/about"; + + public const string VersionApiPath = BaseAboutApiPath + "/version" + OutputFormat; + public const string ManifestApiPath = BaseAboutApiPath + "/manifest" + OutputFormat; + public const string SystemStatusApiPath = BaseAboutApiPath + "/system-status" + OutputFormat; + } + + public static class Layers + { + public const string BaseLayersApiPath = BaseApiPath + "/layers"; + public const string GetLayersApiPath = BaseLayersApiPath + OutputFormat; + + } + } + +} \ No newline at end of file diff --git a/src/GeoServer.Net/Contracts/IGeoServerAboutClient.cs b/src/GeoServer.Net/Contracts/IGeoServerAboutClient.cs new file mode 100644 index 0000000..0f3150b --- /dev/null +++ b/src/GeoServer.Net/Contracts/IGeoServerAboutClient.cs @@ -0,0 +1,26 @@ +using GeoServer.Net.Responses.About; + +namespace GeoServer.Net.Contracts; + +public interface IGeoServerAboutClient +{ + /// + /// Retrieve the versions of the main components: GeoServer, GeoTools, and GeoWebCache + /// http://localhost:8080/geoserver/rest/about/version.json + /// + /// + Task GetVersionAsync(); + /// + /// Retrieve the full manifest and subsets of the manifest as known to the ClassLoader + /// http://localhost:8080/geoserver/rest/about/manifest.json + /// + /// + Task GetManifestAsync(); + /// + /// It is possible to request the available system information (monitoring data) through the GeoServer REST API. + /// The supported formats are XML, JSON and HTML. + /// https://docs.geoserver.org/stable/en/user/rest/about.html#system-status + /// + /// + Task GetSystemStatusAsync(); +} \ No newline at end of file diff --git a/src/GeoServer.Net/Contracts/IGeoServerClient.cs b/src/GeoServer.Net/Contracts/IGeoServerClient.cs new file mode 100644 index 0000000..f61a07d --- /dev/null +++ b/src/GeoServer.Net/Contracts/IGeoServerClient.cs @@ -0,0 +1,3 @@ +namespace GeoServer.Net.Contracts; + +public interface IGeoServerClient: IGeoServerAboutClient, IGeoServerLayersClient; \ No newline at end of file diff --git a/src/GeoServer.Net/Contracts/IGeoServerLayersClient.cs b/src/GeoServer.Net/Contracts/IGeoServerLayersClient.cs new file mode 100644 index 0000000..a2c5120 --- /dev/null +++ b/src/GeoServer.Net/Contracts/IGeoServerLayersClient.cs @@ -0,0 +1,16 @@ +using GeoServer.Net.Responses.Layers; + +namespace GeoServer.Net.Contracts; + +/// +/// https://docs.geoserver.org/latest/en/api/#1.0.0/layers.yaml +/// +public interface IGeoServerLayersClient +{ + /// + /// Displays a list of all layers on the server. You must use the “Accept:” header to specify format or append an extension to the endpoint (example “/layers.xml” for XML) + /// http://localhost:8080/geoserver/rest/layers.json + /// + /// + Task GetLayersListAsync(); +} \ No newline at end of file diff --git a/src/GeoServer.Net/Exceptions/GeoServerHttpRequestException.cs b/src/GeoServer.Net/Exceptions/GeoServerHttpRequestException.cs new file mode 100644 index 0000000..f3ba571 --- /dev/null +++ b/src/GeoServer.Net/Exceptions/GeoServerHttpRequestException.cs @@ -0,0 +1,3 @@ +namespace GeoServer.Net.Exceptions; + +public class GeoServerHttpRequestException(string message) : HttpRequestException(message); \ No newline at end of file diff --git a/src/GeoServer.Net/GeoServer.Net.csproj b/src/GeoServer.Net/GeoServer.Net.csproj new file mode 100644 index 0000000..d9ec37c --- /dev/null +++ b/src/GeoServer.Net/GeoServer.Net.csproj @@ -0,0 +1,12 @@ + + + + net8.0 + enable + + + + + + + diff --git a/src/GeoServer.Net/GeoServerClient.cs b/src/GeoServer.Net/GeoServerClient.cs new file mode 100644 index 0000000..e9125ec --- /dev/null +++ b/src/GeoServer.Net/GeoServerClient.cs @@ -0,0 +1,74 @@ +using System.Net.Http.Headers; +using System.Net.Http.Json; +using System.Text; +using GeoServer.Net.Constants; +using GeoServer.Net.Contracts; +using GeoServer.Net.Exceptions; +using GeoServer.Net.Responses.About; +using GeoServer.Net.Responses.Layers; + +namespace GeoServer.Net; + +/// +/// https://docs.geoserver.org/stable/en/user/rest/about.html +/// +public class GeoServerClient: HttpClient, IGeoServerClient +{ + private readonly string _url; + + public GeoServerClient(string url, string username, string password) + { + _url = url; + var baseUri = new Uri(_url); + BaseAddress = baseUri; + DefaultRequestHeaders.Clear(); + DefaultRequestHeaders.ConnectionClose = true; + + var authenticationString = $"{username}:{password}"; + var base64EncodedAuthenticationString = Convert.ToBase64String(Encoding.UTF8.GetBytes(authenticationString)); + + DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", base64EncodedAuthenticationString); + + } + + /// + /// Retrieve the versions of the main components: GeoServer, GeoTools, and GeoWebCache + /// https://docs.geoserver.org/stable/en/user/rest/about.html + /// + /// + public async Task GetVersionAsync() + { + var response = await GetAsync(_url + ApiConsts.Endpoints.About.VersionApiPath); + if(!response.IsSuccessStatusCode) throw new GeoServerHttpRequestException(response.StatusCode.ToString()); + //var result = await response.Content.ReadAsStringAsync(); + var result = await response.Content.ReadFromJsonAsync(); + return result; + } + + public async Task GetManifestAsync() + { + var response = await GetAsync(_url + ApiConsts.Endpoints.About.ManifestApiPath); + if (!response.IsSuccessStatusCode) throw new GeoServerHttpRequestException(response.StatusCode.ToString()); + //var result = await response.Content.ReadAsStringAsync(); + var result = await response.Content.ReadFromJsonAsync(); + return result; + } + + public async Task GetSystemStatusAsync() + { + var response = await GetAsync(_url + ApiConsts.Endpoints.About.SystemStatusApiPath); + if (!response.IsSuccessStatusCode) throw new GeoServerHttpRequestException(response.StatusCode.ToString()); + //var result = await response.Content.ReadAsStringAsync(); + var result = await response.Content.ReadFromJsonAsync(); + return result; + } + + public async Task GetLayersListAsync() + { + var response = await GetAsync(_url + ApiConsts.Endpoints.Layers.GetLayersApiPath); + if (!response.IsSuccessStatusCode) throw new GeoServerHttpRequestException(response.StatusCode.ToString()); + //var result = await response.Content.ReadAsStringAsync(); + var result = await response.Content.ReadFromJsonAsync(); + return result; + } +} \ No newline at end of file diff --git a/src/GeoServer.Net/Responses/About/Base/GeoServerAboutBaseResponse.cs b/src/GeoServer.Net/Responses/About/Base/GeoServerAboutBaseResponse.cs new file mode 100644 index 0000000..551fe3f --- /dev/null +++ b/src/GeoServer.Net/Responses/About/Base/GeoServerAboutBaseResponse.cs @@ -0,0 +1,9 @@ +using System.Text.Json.Serialization; + +namespace GeoServer.Net.Responses.About.Base; + +public abstract class GeoServerAboutBaseResponse +{ + [JsonPropertyName("about")] + public GeoServerAboutResponse About { get; set; } +} \ No newline at end of file diff --git a/src/GeoServer.Net/Responses/About/Base/GeoServerAboutResponse.cs b/src/GeoServer.Net/Responses/About/Base/GeoServerAboutResponse.cs new file mode 100644 index 0000000..4a5c2e8 --- /dev/null +++ b/src/GeoServer.Net/Responses/About/Base/GeoServerAboutResponse.cs @@ -0,0 +1,9 @@ +using System.Text.Json.Serialization; + +namespace GeoServer.Net.Responses.About.Base; + +public class GeoServerAboutResponse +{ + [JsonPropertyName("resource")] + public TResource[] Resources { get; set; } +} \ No newline at end of file diff --git a/src/GeoServer.Net/Responses/About/GeoServerAboutManifestResponse.cs b/src/GeoServer.Net/Responses/About/GeoServerAboutManifestResponse.cs new file mode 100644 index 0000000..f2f047e --- /dev/null +++ b/src/GeoServer.Net/Responses/About/GeoServerAboutManifestResponse.cs @@ -0,0 +1,21 @@ +using GeoServer.Net.Responses.About.Base; +using System.Text.Json.Serialization; + +namespace GeoServer.Net.Responses.About; + +public class GeoServerAboutManifestResponse:GeoServerAboutBaseResponse +{ + +} + +public class Manifest +{ + [JsonPropertyName("@name")] + public string Name { get; set; } + [JsonPropertyName("Archiver-Version")] + public string ArchiverVersion { get; set; } + [JsonPropertyName("Bundle-License")] + public string BundleLicense { get; set; } + [JsonPropertyName("Specification-Version")] + public object SpecificationVersion { get; set; } +} diff --git a/src/GeoServer.Net/Responses/About/GeoServerAboutSystemStatusResponse.cs b/src/GeoServer.Net/Responses/About/GeoServerAboutSystemStatusResponse.cs new file mode 100644 index 0000000..9a4b919 --- /dev/null +++ b/src/GeoServer.Net/Responses/About/GeoServerAboutSystemStatusResponse.cs @@ -0,0 +1,35 @@ +using System.Text.Json.Serialization; + +namespace GeoServer.Net.Responses.About; + +public class GeoServerAboutSystemStatusResponse +{ + [JsonPropertyName("metrics")] + public Metrics Metrics { get; set; } +} + +public class Metrics +{ + [JsonPropertyName("metric")] + public Metric[] Metric { get; set; } +} + +public class Metric +{ + [JsonPropertyName("available")] + public bool Available { get; set; } + [JsonPropertyName("description")] + public string Description { get; set; } + [JsonPropertyName("name")] + public string Name { get; set; } + [JsonPropertyName("unit")] + public string Unit { get; set; } + [JsonPropertyName("category")] + public string Category { get; set; } + [JsonPropertyName("identifier")] + public string Identifier { get; set; } + [JsonPropertyName("priority")] + public int Priority { get; set; } + [JsonPropertyName("value")] + public string Value { get; set; } +} diff --git a/src/GeoServer.Net/Responses/About/GeoServerAboutVersionResponse.cs b/src/GeoServer.Net/Responses/About/GeoServerAboutVersionResponse.cs new file mode 100644 index 0000000..c51f789 --- /dev/null +++ b/src/GeoServer.Net/Responses/About/GeoServerAboutVersionResponse.cs @@ -0,0 +1,22 @@ +using System.Text.Json.Serialization; +using GeoServer.Net.Responses.About.Base; + +namespace GeoServer.Net.Responses.About; + +public class GeoServerAboutVersionResponse: GeoServerAboutBaseResponse +{ +} + + + +public class Resource +{ + [JsonPropertyName("@name")] + public string Name { get; set; } + [JsonPropertyName("Build-Timestamp")] + public string BuildTimestamp { get; set; } + [JsonPropertyName("Version")] + public object Version { get; set; } + [JsonPropertyName("Git-Revision")] + public string GitRevision { get; set; } +} diff --git a/src/GeoServer.Net/Responses/Layers/GeoServerLayersListResponse.cs b/src/GeoServer.Net/Responses/Layers/GeoServerLayersListResponse.cs new file mode 100644 index 0000000..80baf28 --- /dev/null +++ b/src/GeoServer.Net/Responses/Layers/GeoServerLayersListResponse.cs @@ -0,0 +1,24 @@ +using System.Text.Json.Serialization; + +namespace GeoServer.Net.Responses.Layers; + +public class GeoServerLayersListResponse +{ + [JsonPropertyName("layers")] + public Layers Layers { get; set; } +} + + +public class Layers +{ + [JsonPropertyName("layer")] + public Layer[] Layer { get; set; } +} + +public class Layer +{ + [JsonPropertyName("name")] + public string Name { get; set; } + [JsonPropertyName("href")] + public string Href { get; set; } +} diff --git a/test/GeoServer.Net.Tests/AboutEndpointsTests.cs b/test/GeoServer.Net.Tests/AboutEndpointsTests.cs new file mode 100644 index 0000000..53964d1 --- /dev/null +++ b/test/GeoServer.Net.Tests/AboutEndpointsTests.cs @@ -0,0 +1,33 @@ +using FluentAssertions; +using GeoServer.Net.Contracts; + +namespace GeoServer.Net.Tests; + +public class AboutEndpointsTests +{ + private readonly IGeoServerAboutClient _geoServerClient = new GeoServerClient(GeneralConfigurations.GeoServerUrl, GeneralConfigurations.UserName, GeneralConfigurations.Password); + + [Fact] + public async Task GetVersionTest() + { + var response = await _geoServerClient.GetVersionAsync(); + response.Should().NotBeNull(); + response.About.Resources.Length.Should().BeGreaterThan(0); + } + + [Fact] + public async Task GetManifestTest() + { + var response = await _geoServerClient.GetManifestAsync(); + response.Should().NotBeNull(); + response.About.Resources.Length.Should().BeGreaterThan(0); + } + + [Fact] + public async Task GetSystemStatusTest() + { + var response = await _geoServerClient.GetSystemStatusAsync(); + response.Should().NotBeNull(); + response.Metrics.Metric.Length.Should().BeGreaterThan(0); + } +} \ No newline at end of file diff --git a/test/GeoServer.Net.Tests/GeneralConfigurations.cs b/test/GeoServer.Net.Tests/GeneralConfigurations.cs new file mode 100644 index 0000000..ac72307 --- /dev/null +++ b/test/GeoServer.Net.Tests/GeneralConfigurations.cs @@ -0,0 +1,8 @@ +namespace GeoServer.Net.Tests; + +public static class GeneralConfigurations +{ + internal const string GeoServerUrl = "http://localhost:8080/geoserver"; + internal const string UserName = "admin"; + internal const string Password = "geoserver"; +} \ No newline at end of file diff --git a/test/GeoServer.Net.Tests/GeoServer.Net.Tests.csproj b/test/GeoServer.Net.Tests/GeoServer.Net.Tests.csproj new file mode 100644 index 0000000..0c5a17b --- /dev/null +++ b/test/GeoServer.Net.Tests/GeoServer.Net.Tests.csproj @@ -0,0 +1,30 @@ + + + + net8.0 + enable + enable + + false + true + + + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + + + + + + + diff --git a/test/GeoServer.Net.Tests/GlobalUsings.cs b/test/GeoServer.Net.Tests/GlobalUsings.cs new file mode 100644 index 0000000..8c927eb --- /dev/null +++ b/test/GeoServer.Net.Tests/GlobalUsings.cs @@ -0,0 +1 @@ +global using Xunit; \ No newline at end of file diff --git a/test/GeoServer.Net.Tests/LayersEndpointsTests.cs b/test/GeoServer.Net.Tests/LayersEndpointsTests.cs new file mode 100644 index 0000000..6ee7a78 --- /dev/null +++ b/test/GeoServer.Net.Tests/LayersEndpointsTests.cs @@ -0,0 +1,17 @@ +using FluentAssertions; +using GeoServer.Net.Contracts; + +namespace GeoServer.Net.Tests; + +public class LayersEndpointsTests +{ + private readonly IGeoServerLayersClient _geoServerClient = new GeoServerClient(GeneralConfigurations.GeoServerUrl, GeneralConfigurations.UserName, GeneralConfigurations.Password); + + [Fact] + public async Task GetLayersListTest() + { + var response = await _geoServerClient.GetLayersListAsync(); + response.Should().NotBeNull(); + response.Layers.Layer.Length.Should().BeGreaterThan(0); + } +} \ No newline at end of file