From 6e874bd671aab01641edbf8d9c5902fe28cb6153 Mon Sep 17 00:00:00 2001 From: Mihai Stan Date: Sun, 19 May 2019 16:13:39 +0200 Subject: [PATCH 01/76] Add .gitignore and .gitattributes --- .gitattributes | 63 +++++++++ .gitignore | 340 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 403 insertions(+) create mode 100644 .gitattributes create mode 100644 .gitignore diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..1ff0c42 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,63 @@ +############################################################################### +# Set default behavior to automatically normalize line endings. +############################################################################### +* text=auto + +############################################################################### +# Set default behavior for command prompt diff. +# +# This is need for earlier builds of msysgit that does not have it on by +# default for csharp files. +# Note: This is only used by command line +############################################################################### +#*.cs diff=csharp + +############################################################################### +# Set the merge driver for project and solution files +# +# Merging from the command prompt will add diff markers to the files if there +# are conflicts (Merging from VS is not affected by the settings below, in VS +# the diff markers are never inserted). Diff markers may cause the following +# file extensions to fail to load in VS. An alternative would be to treat +# these files as binary and thus will always conflict and require user +# intervention with every merge. To do so, just uncomment the entries below +############################################################################### +#*.sln merge=binary +#*.csproj merge=binary +#*.vbproj merge=binary +#*.vcxproj merge=binary +#*.vcproj merge=binary +#*.dbproj merge=binary +#*.fsproj merge=binary +#*.lsproj merge=binary +#*.wixproj merge=binary +#*.modelproj merge=binary +#*.sqlproj merge=binary +#*.wwaproj merge=binary + +############################################################################### +# behavior for image files +# +# image files are treated as binary by default. +############################################################################### +#*.jpg binary +#*.png binary +#*.gif binary + +############################################################################### +# diff behavior for common document formats +# +# Convert binary document formats to text before diffing them. This feature +# is only available from the command line. Turn it on by uncommenting the +# entries below. +############################################################################### +#*.doc diff=astextplain +#*.DOC diff=astextplain +#*.docx diff=astextplain +#*.DOCX diff=astextplain +#*.dot diff=astextplain +#*.DOT diff=astextplain +#*.pdf diff=astextplain +#*.PDF diff=astextplain +#*.rtf diff=astextplain +#*.RTF diff=astextplain diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..4ce6fdd --- /dev/null +++ b/.gitignore @@ -0,0 +1,340 @@ +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. +## +## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore + +# User-specific files +*.rsuser +*.suo +*.user +*.userosscache +*.sln.docstates + +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +[Aa][Rr][Mm]/ +[Aa][Rr][Mm]64/ +bld/ +[Bb]in/ +[Oo]bj/ +[Ll]og/ + +# Visual Studio 2015/2017 cache/options directory +.vs/ +# Uncomment if you have tasks that create the project's static files in wwwroot +#wwwroot/ + +# Visual Studio 2017 auto generated files +Generated\ Files/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +# NUNIT +*.VisualState.xml +TestResult.xml + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +# Benchmark Results +BenchmarkDotNet.Artifacts/ + +# .NET Core +project.lock.json +project.fragment.lock.json +artifacts/ + +# StyleCop +StyleCopReport.xml + +# Files built by Visual Studio +*_i.c +*_p.c +*_h.h +*.ilk +*.meta +*.obj +*.iobj +*.pch +*.pdb +*.ipdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*_wpftmp.csproj +*.log +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc + +# Chutzpah Test files +_Chutzpah* + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opendb +*.opensdf +*.sdf +*.cachefile +*.VC.db +*.VC.VC.opendb + +# Visual Studio profiler +*.psess +*.vsp +*.vspx +*.sap + +# Visual Studio Trace Files +*.e2e + +# TFS 2012 Local Workspace +$tf/ + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# JustCode is a .NET coding add-in +.JustCode + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# AxoCover is a Code Coverage Tool +.axoCover/* +!.axoCover/settings.json + +# Visual Studio code coverage results +*.coverage +*.coveragexml + +# NCrunch +_NCrunch_* +.*crunch*.local.xml +nCrunchTemp_* + +# MightyMoose +*.mm.* +AutoTest.Net/ + +# Web workbench (sass) +.sass-cache/ + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.[Pp]ublish.xml +*.azurePubxml +# Note: Comment the next line if you want to checkin your web deploy settings, +# but database connection strings (with potential passwords) will be unencrypted +*.pubxml +*.publishproj + +# Microsoft Azure Web App publish settings. Comment the next line if you want to +# checkin your Azure Web App publish settings, but sensitive information contained +# in these scripts will be unencrypted +PublishScripts/ + +# NuGet Packages +*.nupkg +# The packages folder can be ignored because of Package Restore +**/[Pp]ackages/* +# except build/, which is used as an MSBuild target. +!**/[Pp]ackages/build/ +# Uncomment if necessary however generally it will be regenerated when needed +#!**/[Pp]ackages/repositories.config +# NuGet v3's project.json files produces more ignorable files +*.nuget.props +*.nuget.targets + +# Microsoft Azure Build Output +csx/ +*.build.csdef + +# Microsoft Azure Emulator +ecf/ +rcf/ + +# Windows Store app package directories and files +AppPackages/ +BundleArtifacts/ +Package.StoreAssociation.xml +_pkginfo.txt +*.appx + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!?*.[Cc]ache/ + +# Others +ClientBin/ +~$* +*~ +*.dbmdl +*.dbproj.schemaview +*.jfm +*.pfx +*.publishsettings +orleans.codegen.cs + +# Including strong name files can present a security risk +# (https://github.com/github/gitignore/pull/2483#issue-259490424) +#*.snk + +# Since there are multiple workflows, uncomment next line to ignore bower_components +# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) +#bower_components/ + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file +# to a newer Visual Studio version. Backup files are not needed, +# because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm +ServiceFabricBackup/ +*.rptproj.bak + +# SQL Server files +*.mdf +*.ldf +*.ndf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings +*.rptproj.rsuser +*- Backup*.rdl + +# Microsoft Fakes +FakesAssemblies/ + +# GhostDoc plugin setting file +*.GhostDoc.xml + +# Node.js Tools for Visual Studio +.ntvs_analysis.dat +node_modules/ + +# Visual Studio 6 build log +*.plg + +# Visual Studio 6 workspace options file +*.opt + +# Visual Studio 6 auto-generated workspace file (contains which files were open etc.) +*.vbw + +# Visual Studio LightSwitch build output +**/*.HTMLClient/GeneratedArtifacts +**/*.DesktopClient/GeneratedArtifacts +**/*.DesktopClient/ModelManifest.xml +**/*.Server/GeneratedArtifacts +**/*.Server/ModelManifest.xml +_Pvt_Extensions + +# Paket dependency manager +.paket/paket.exe +paket-files/ + +# FAKE - F# Make +.fake/ + +# JetBrains Rider +.idea/ +*.sln.iml + +# CodeRush personal settings +.cr/personal + +# Python Tools for Visual Studio (PTVS) +__pycache__/ +*.pyc + +# Cake - Uncomment if you are using it +# tools/** +# !tools/packages.config + +# Tabs Studio +*.tss + +# Telerik's JustMock configuration file +*.jmconfig + +# BizTalk build output +*.btp.cs +*.btm.cs +*.odx.cs +*.xsd.cs + +# OpenCover UI analysis results +OpenCover/ + +# Azure Stream Analytics local run output +ASALocalRun/ + +# MSBuild Binary and Structured Log +*.binlog + +# NVidia Nsight GPU debugger configuration file +*.nvuser + +# MFractors (Xamarin productivity tool) working folder +.mfractor/ + +# Local History for Visual Studio +.localhistory/ + +# BeatPulse healthcheck temp database +healthchecksdb \ No newline at end of file From 8150e15228d64f30816bddbab80c538a62296a5a Mon Sep 17 00:00:00 2001 From: Mihai Stan Date: Sun, 19 May 2019 16:21:39 +0200 Subject: [PATCH 02/76] Add initial project files --- src/Wps/Wps.Client.Tests/UnitTest1.cs | 13 ++++++++ .../Wps.Client.Tests/Wps.Client.Tests.csproj | 15 +++++++++ src/Wps/Wps.Client/Class1.cs | 6 ++++ src/Wps/Wps.Client/Wps.Client.csproj | 7 +++++ src/Wps/Wps.sln | 31 +++++++++++++++++++ 5 files changed, 72 insertions(+) create mode 100644 src/Wps/Wps.Client.Tests/UnitTest1.cs create mode 100644 src/Wps/Wps.Client.Tests/Wps.Client.Tests.csproj create mode 100644 src/Wps/Wps.Client/Class1.cs create mode 100644 src/Wps/Wps.Client/Wps.Client.csproj create mode 100644 src/Wps/Wps.sln diff --git a/src/Wps/Wps.Client.Tests/UnitTest1.cs b/src/Wps/Wps.Client.Tests/UnitTest1.cs new file mode 100644 index 0000000..64f709c --- /dev/null +++ b/src/Wps/Wps.Client.Tests/UnitTest1.cs @@ -0,0 +1,13 @@ +using Xunit; + +namespace Wps.Client.Tests +{ + public class UnitTest1 + { + [Fact] + public void Test1() + { + + } + } +} diff --git a/src/Wps/Wps.Client.Tests/Wps.Client.Tests.csproj b/src/Wps/Wps.Client.Tests/Wps.Client.Tests.csproj new file mode 100644 index 0000000..4ec64e7 --- /dev/null +++ b/src/Wps/Wps.Client.Tests/Wps.Client.Tests.csproj @@ -0,0 +1,15 @@ + + + + netcoreapp2.1 + + false + + + + + + + + + diff --git a/src/Wps/Wps.Client/Class1.cs b/src/Wps/Wps.Client/Class1.cs new file mode 100644 index 0000000..a3282dc --- /dev/null +++ b/src/Wps/Wps.Client/Class1.cs @@ -0,0 +1,6 @@ +namespace Wps.Client +{ + public class Class1 + { + } +} diff --git a/src/Wps/Wps.Client/Wps.Client.csproj b/src/Wps/Wps.Client/Wps.Client.csproj new file mode 100644 index 0000000..9f5c4f4 --- /dev/null +++ b/src/Wps/Wps.Client/Wps.Client.csproj @@ -0,0 +1,7 @@ + + + + netstandard2.0 + + + diff --git a/src/Wps/Wps.sln b/src/Wps/Wps.sln new file mode 100644 index 0000000..e725de3 --- /dev/null +++ b/src/Wps/Wps.sln @@ -0,0 +1,31 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.28803.452 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Wps.Client", "Wps.Client\Wps.Client.csproj", "{1D689A04-5629-484C-91F9-A904F923CC5C}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Wps.Client.Tests", "Wps.Client.Tests\Wps.Client.Tests.csproj", "{61ECB279-6288-48DE-BE3B-75A65EB04BFE}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {1D689A04-5629-484C-91F9-A904F923CC5C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {1D689A04-5629-484C-91F9-A904F923CC5C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {1D689A04-5629-484C-91F9-A904F923CC5C}.Release|Any CPU.ActiveCfg = Release|Any CPU + {1D689A04-5629-484C-91F9-A904F923CC5C}.Release|Any CPU.Build.0 = Release|Any CPU + {61ECB279-6288-48DE-BE3B-75A65EB04BFE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {61ECB279-6288-48DE-BE3B-75A65EB04BFE}.Debug|Any CPU.Build.0 = Debug|Any CPU + {61ECB279-6288-48DE-BE3B-75A65EB04BFE}.Release|Any CPU.ActiveCfg = Release|Any CPU + {61ECB279-6288-48DE-BE3B-75A65EB04BFE}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {0B9B52D4-8EB1-487A-8C29-CB56077E923C} + EndGlobalSection +EndGlobal From 3358a9c64da923db7760a16450d56c48446e3004 Mon Sep 17 00:00:00 2001 From: Mihai Stan Date: Wed, 22 May 2019 14:52:14 +0200 Subject: [PATCH 03/76] Remove unused default class --- src/Wps/Wps.Client/Class1.cs | 6 ------ 1 file changed, 6 deletions(-) delete mode 100644 src/Wps/Wps.Client/Class1.cs diff --git a/src/Wps/Wps.Client/Class1.cs b/src/Wps/Wps.Client/Class1.cs deleted file mode 100644 index a3282dc..0000000 --- a/src/Wps/Wps.Client/Class1.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace Wps.Client -{ - public class Class1 - { - } -} From 1d2d76d8f977fa7813785638a3cfb59d5d7bfcce Mon Sep 17 00:00:00 2001 From: Mihai Stan Date: Wed, 22 May 2019 21:39:25 +0200 Subject: [PATCH 04/76] Add XML namespaces for the WPS 2.* standard --- src/Wps/Wps.Client/Utils/ModelNamespaces.cs | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 src/Wps/Wps.Client/Utils/ModelNamespaces.cs diff --git a/src/Wps/Wps.Client/Utils/ModelNamespaces.cs b/src/Wps/Wps.Client/Utils/ModelNamespaces.cs new file mode 100644 index 0000000..3cfef81 --- /dev/null +++ b/src/Wps/Wps.Client/Utils/ModelNamespaces.cs @@ -0,0 +1,10 @@ +namespace Wps.Client.Utils +{ + public static class ModelNamespaces + { + + public const string Wps = "http://www.opengis.net/wps/2.0"; + public const string Ows = "http://www.opengis.net/ows/2.0"; + + } +} From f0a1d2d4ded865d1fd3ccdcac14702e07b536aa3 Mon Sep 17 00:00:00 2001 From: Mihai Stan Date: Wed, 22 May 2019 22:08:20 +0200 Subject: [PATCH 05/76] Add data Format model --- src/Wps/Wps.Client/Models/Format.cs | 50 +++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) create mode 100644 src/Wps/Wps.Client/Models/Format.cs diff --git a/src/Wps/Wps.Client/Models/Format.cs b/src/Wps/Wps.Client/Models/Format.cs new file mode 100644 index 0000000..c0518f3 --- /dev/null +++ b/src/Wps/Wps.Client/Models/Format.cs @@ -0,0 +1,50 @@ +using System.Text; +using System.Xml.Serialization; + +namespace Wps.Client.Models +{ + [XmlRoot("Format")] + public class Format + { + + /// + /// The media type of the data. + /// + /// + /// It is mandatory to precise the media type. + /// + [XmlAttribute("mimetype")] + public string MimeType { get; set; } + + /// + /// The encoding procedure or character set of the data. (e.g. raw or base64) + /// + /// + /// It is mandatory to precise the encoding. + /// + [XmlAttribute("encoding")] + public string Encoding { get; set; } + + /// + /// The identification of the data schema. + /// + /// + /// It is mandatory to precise the schema. + /// + [XmlAttribute("schema")] + public string Schema { get; set; } + + /// + /// The maximum size of the input data, in megabytes. + /// + [XmlAttribute("maximumMegabytes")] + public int MaximumMegabytes { get; set; } + + /// + /// Indicates that this format is the default format. + /// + [XmlAttribute("default")] + public bool IsDefault { get; set; } + + } +} From f86b78fd42ae4687319b5ca9f9f802234770b9ce Mon Sep 17 00:00:00 2001 From: Mihai Stan Date: Thu, 23 May 2019 16:27:05 +0200 Subject: [PATCH 06/76] Add FluentAssertions dependency and Model reference to test project --- src/Wps/Wps.Client.Tests/Wps.Client.Tests.csproj | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/src/Wps/Wps.Client.Tests/Wps.Client.Tests.csproj b/src/Wps/Wps.Client.Tests/Wps.Client.Tests.csproj index 4ec64e7..8385f1e 100644 --- a/src/Wps/Wps.Client.Tests/Wps.Client.Tests.csproj +++ b/src/Wps/Wps.Client.Tests/Wps.Client.Tests.csproj @@ -7,9 +7,17 @@ - - - + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + From b3fa7caa4cc431aac261958439fea89860150d08 Mon Sep 17 00:00:00 2001 From: Mihai Stan Date: Thu, 23 May 2019 16:28:58 +0200 Subject: [PATCH 07/76] Add Format deserialization test --- .../ModelDeserializationTests.cs | 35 +++++++++++++++++++ src/Wps/Wps.Client/Models/Format.cs | 11 +++--- 2 files changed, 42 insertions(+), 4 deletions(-) create mode 100644 src/Wps/Wps.Client.Tests/ModelDeserializationTests.cs diff --git a/src/Wps/Wps.Client.Tests/ModelDeserializationTests.cs b/src/Wps/Wps.Client.Tests/ModelDeserializationTests.cs new file mode 100644 index 0000000..cc55dbb --- /dev/null +++ b/src/Wps/Wps.Client.Tests/ModelDeserializationTests.cs @@ -0,0 +1,35 @@ +using FluentAssertions; +using System.IO; +using System.Xml.Serialization; +using Wps.Client.Models; +using Xunit; + +namespace Wps.Client.Tests +{ + public class ModelDeserializationTests + { + + [Fact] + public void DeserializeFormat_ValidFormatGiven_ShouldPass() + { + var serializer = new XmlSerializer(typeof(Format)); + var xml = + @""; + + using (var reader = new StringReader(xml)) + { + var format = serializer.Deserialize(reader) as Format; + format.Should().NotBeNull(); + if (format != null) + { + format.MimeType.Should().Be("application/x-geotiff"); + format.Encoding.Should().Be("base64"); + format.Schema.Should().Be("test.schema.tld"); + format.MaximumMegabytes.Should().Be(2048); + format.IsDefault.Should().BeTrue(); + } + } + } + + } +} diff --git a/src/Wps/Wps.Client/Models/Format.cs b/src/Wps/Wps.Client/Models/Format.cs index c0518f3..2a2c9e4 100644 --- a/src/Wps/Wps.Client/Models/Format.cs +++ b/src/Wps/Wps.Client/Models/Format.cs @@ -1,9 +1,12 @@ -using System.Text; -using System.Xml.Serialization; +using System.Xml.Serialization; +using Wps.Client.Utils; namespace Wps.Client.Models { - [XmlRoot("Format")] + /// + /// Format of the data. + /// + [XmlRoot("Format", Namespace = ModelNamespaces.Wps)] public class Format { @@ -13,7 +16,7 @@ public class Format /// /// It is mandatory to precise the media type. /// - [XmlAttribute("mimetype")] + [XmlAttribute("mimeType")] public string MimeType { get; set; } /// From 916f3bc8351f8773789f9fec5d2bc56ad8e3d9bb Mon Sep 17 00:00:00 2001 From: Mihai Stan Date: Thu, 23 May 2019 17:44:09 +0200 Subject: [PATCH 08/76] Add DataType model --- .../ModelDeserializationTests.cs | 16 ++++++ src/Wps/Wps.Client/Models/Data/DataType.cs | 51 +++++++++++++++++++ 2 files changed, 67 insertions(+) create mode 100644 src/Wps/Wps.Client/Models/Data/DataType.cs diff --git a/src/Wps/Wps.Client.Tests/ModelDeserializationTests.cs b/src/Wps/Wps.Client.Tests/ModelDeserializationTests.cs index cc55dbb..5822467 100644 --- a/src/Wps/Wps.Client.Tests/ModelDeserializationTests.cs +++ b/src/Wps/Wps.Client.Tests/ModelDeserializationTests.cs @@ -2,6 +2,7 @@ using System.IO; using System.Xml.Serialization; using Wps.Client.Models; +using Wps.Client.Models.Data; using Xunit; namespace Wps.Client.Tests @@ -31,5 +32,20 @@ public void DeserializeFormat_ValidFormatGiven_ShouldPass() } } + [Fact] + public void DeserializeDataType_ValidXmlGiven_ShouldPass() + { + var serialize = new XmlSerializer(typeof(DataType)); + var xml = @""; + + using (var reader = new StringReader(xml)) + { + var dataType = serialize.Deserialize(reader) as DataType; + dataType.Should().NotBeNull(); + dataType?.Reference.Should().Be("string"); + dataType?.GetReferenceType().Should().Be(typeof(string)); + } + } + } } diff --git a/src/Wps/Wps.Client/Models/Data/DataType.cs b/src/Wps/Wps.Client/Models/Data/DataType.cs new file mode 100644 index 0000000..cc56a6c --- /dev/null +++ b/src/Wps/Wps.Client/Models/Data/DataType.cs @@ -0,0 +1,51 @@ +using System; +using System.Xml.Schema; +using System.Xml.Serialization; +using Wps.Client.Utils; + +namespace Wps.Client.Models.Data +{ + [Serializable] + [XmlRoot(ElementName = "DataType", Namespace = ModelNamespaces.Ows)] + public class DataType + { + + [XmlAttribute(AttributeName = "reference", Form = XmlSchemaForm.Qualified, Namespace = ModelNamespaces.Ows)] + public string Reference { get; set; } + + public Type GetReferenceType() + { + if (Reference.Equals("string", StringComparison.InvariantCultureIgnoreCase)) + { + return typeof(string); + } + + if (Reference.Equals("integer", StringComparison.InvariantCultureIgnoreCase)) + { + return typeof(int); + } + + if (Reference.Equals("decimal", StringComparison.InvariantCultureIgnoreCase)) + { + return typeof(decimal); + } + + if (Reference.Equals("boolean", StringComparison.InvariantCultureIgnoreCase)) + { + return typeof(bool); + } + + if (Reference.Equals("double", StringComparison.InvariantCultureIgnoreCase)) + { + return typeof(double); + } + + if (Reference.Equals("float", StringComparison.InvariantCultureIgnoreCase)) + { + return typeof(float); + } + + return null; + } + } +} From 5b189a2ad3b56ed3ec238c4a7ebf13b72b67329c Mon Sep 17 00:00:00 2001 From: Mihai Stan Date: Thu, 23 May 2019 20:11:25 +0200 Subject: [PATCH 09/76] Add LiteralDataDomain model --- .../ModelDeserializationTests.cs | 79 +++++++++++++++++++ .../Wps.Client/Models/Data/AllowedValues.cs | 14 ++++ src/Wps/Wps.Client/Models/Data/AnyValue.cs | 10 +++ .../Models/Data/LiteralDataDomain.cs | 43 ++++++++++ .../Wps.Client/Models/Data/LiteralValue.cs | 5 ++ .../Wps.Client/Models/Data/ValueReference.cs | 9 +++ 6 files changed, 160 insertions(+) create mode 100644 src/Wps/Wps.Client/Models/Data/AllowedValues.cs create mode 100644 src/Wps/Wps.Client/Models/Data/AnyValue.cs create mode 100644 src/Wps/Wps.Client/Models/Data/LiteralDataDomain.cs create mode 100644 src/Wps/Wps.Client/Models/Data/LiteralValue.cs create mode 100644 src/Wps/Wps.Client/Models/Data/ValueReference.cs diff --git a/src/Wps/Wps.Client.Tests/ModelDeserializationTests.cs b/src/Wps/Wps.Client.Tests/ModelDeserializationTests.cs index 5822467..5596492 100644 --- a/src/Wps/Wps.Client.Tests/ModelDeserializationTests.cs +++ b/src/Wps/Wps.Client.Tests/ModelDeserializationTests.cs @@ -47,5 +47,84 @@ public void DeserializeDataType_ValidXmlGiven_ShouldPass() } } + [Fact] + public void DeserializeLiteralDataDomain_ValidXmlGiven_WithAllowedValues_ShouldPass() + { + var serialize = new XmlSerializer(typeof(LiteralDataDomain)); + var xml = @" + + uncorrected + dos1 + dos2 + dos2b + dos3 + dos4 + + + uncorrected + Meter + "; + + using (var reader = new StringReader(xml)) + { + var domain = serialize.Deserialize(reader) as LiteralDataDomain; + domain.Should().NotBeNull(); + domain?.IsDefault.Should().BeTrue(); + domain?.DefaultValue.Should().Be("uncorrected"); + domain?.PossibleLiteralValues.GetType().Should().Be(typeof(AllowedValues)); + domain?.DataType?.Reference.Should().Be("string"); + if(domain?.PossibleLiteralValues is AllowedValues values) + { + values.Values.Length.Should().Be(6); + } + } + } + + [Fact] + public void DeserializeLiteralDataDomain_ValidXmlGiven_WithAnyValue_ShouldPass() + { + var serialize = new XmlSerializer(typeof(LiteralDataDomain)); + var xml = @" + + + uncorrected + Meter + "; + + using (var reader = new StringReader(xml)) + { + var domain = serialize.Deserialize(reader) as LiteralDataDomain; + domain.Should().NotBeNull(); + domain?.IsDefault.Should().BeTrue(); + domain?.DefaultValue.Should().Be("uncorrected"); + domain?.PossibleLiteralValues.GetType().Should().Be(typeof(AnyValue)); + domain?.DataType?.Reference.Should().Be("string"); + + } + } + + [Fact] + public void DeserializeLiteralDataDomain_ValidXmlGiven_WithValueReference_ShouldPass() + { + var serialize = new XmlSerializer(typeof(LiteralDataDomain)); + var xml = @" + + + uncorrected + Meter + "; + + using (var reader = new StringReader(xml)) + { + var domain = serialize.Deserialize(reader) as LiteralDataDomain; + domain.Should().NotBeNull(); + domain?.IsDefault.Should().BeTrue(); + domain?.DefaultValue.Should().Be("uncorrected"); + domain?.PossibleLiteralValues.GetType().Should().Be(typeof(ValueReference)); + domain?.DataType?.Reference.Should().Be("string"); + + } + } + } } diff --git a/src/Wps/Wps.Client/Models/Data/AllowedValues.cs b/src/Wps/Wps.Client/Models/Data/AllowedValues.cs new file mode 100644 index 0000000..042a2fe --- /dev/null +++ b/src/Wps/Wps.Client/Models/Data/AllowedValues.cs @@ -0,0 +1,14 @@ +using System.Xml.Serialization; +using Wps.Client.Utils; + +namespace Wps.Client.Models.Data +{ + [XmlRoot("AllowedValues", Namespace = ModelNamespaces.Ows)] + public class AllowedValues : LiteralValue + { + + [XmlElement("Value", Namespace = ModelNamespaces.Ows)] + public string[] Values { get; set; } + + } +} diff --git a/src/Wps/Wps.Client/Models/Data/AnyValue.cs b/src/Wps/Wps.Client/Models/Data/AnyValue.cs new file mode 100644 index 0000000..796d763 --- /dev/null +++ b/src/Wps/Wps.Client/Models/Data/AnyValue.cs @@ -0,0 +1,10 @@ +using System.Xml.Serialization; +using Wps.Client.Utils; + +namespace Wps.Client.Models.Data +{ + [XmlRoot("AnyValue", Namespace = ModelNamespaces.Ows)] + public class AnyValue : LiteralValue + { + } +} diff --git a/src/Wps/Wps.Client/Models/Data/LiteralDataDomain.cs b/src/Wps/Wps.Client/Models/Data/LiteralDataDomain.cs new file mode 100644 index 0000000..dcdbf48 --- /dev/null +++ b/src/Wps/Wps.Client/Models/Data/LiteralDataDomain.cs @@ -0,0 +1,43 @@ +using System.Xml.Serialization; +using Wps.Client.Utils; + +namespace Wps.Client.Models.Data +{ + [XmlRoot("LiteralDataDomain", Namespace = ModelNamespaces.Wps)] + public class LiteralDataDomain + { + + /// + /// Identifies a valid format for an input or output. + /// + [XmlElement("AnyValue", Type = typeof(AnyValue), Namespace = ModelNamespaces.Ows), + XmlElement("AllowedValues", Type = typeof(AllowedValues), Namespace = ModelNamespaces.Ows), + XmlElement("ValueReference", Type = typeof(ValueReference), Namespace = ModelNamespaces.Ows)] + public LiteralValue PossibleLiteralValues { get; set; } + + /// + /// Reference to the data type of this set of values. + /// + [XmlElement("DataType", Namespace = ModelNamespaces.Ows)] + public DataType DataType { get; set; } + + /// + /// Indicates that this quantity has units and provides the unit of measurement. + /// + [XmlElement("ValuesUnit", Namespace = ModelNamespaces.Ows)] + public string UnitOfMeasure { get; set; } + + /// + /// The default value of this quantity. + /// + [XmlElement("DefaultValue", Namespace = ModelNamespaces.Ows)] + public string DefaultValue { get; set; } + + /// + /// Indicates that this is the default/native domain. + /// + [XmlAttribute("default")] + public bool IsDefault { get; set; } + + } +} diff --git a/src/Wps/Wps.Client/Models/Data/LiteralValue.cs b/src/Wps/Wps.Client/Models/Data/LiteralValue.cs new file mode 100644 index 0000000..d82e79f --- /dev/null +++ b/src/Wps/Wps.Client/Models/Data/LiteralValue.cs @@ -0,0 +1,5 @@ +namespace Wps.Client.Models.Data +{ + public abstract class LiteralValue + { } +} diff --git a/src/Wps/Wps.Client/Models/Data/ValueReference.cs b/src/Wps/Wps.Client/Models/Data/ValueReference.cs new file mode 100644 index 0000000..b6d4a15 --- /dev/null +++ b/src/Wps/Wps.Client/Models/Data/ValueReference.cs @@ -0,0 +1,9 @@ +using System.Xml.Serialization; +using Wps.Client.Utils; + +namespace Wps.Client.Models.Data +{ + [XmlRoot("ValueReference", Namespace = ModelNamespaces.Ows)] + public class ValueReference : LiteralValue + {} +} From 1114f8872b4d33c9ed6d908893411cd6709d763d Mon Sep 17 00:00:00 2001 From: Mihai Stan Date: Mon, 27 May 2019 12:35:33 +0200 Subject: [PATCH 10/76] Add base Data model --- src/Wps/Wps.Client/Models/Data/Data.cs | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 src/Wps/Wps.Client/Models/Data/Data.cs diff --git a/src/Wps/Wps.Client/Models/Data/Data.cs b/src/Wps/Wps.Client/Models/Data/Data.cs new file mode 100644 index 0000000..8ff5122 --- /dev/null +++ b/src/Wps/Wps.Client/Models/Data/Data.cs @@ -0,0 +1,15 @@ +using System.Xml.Serialization; + +namespace Wps.Client.Models.Data +{ + public abstract class Data + { + + /// + /// List of supported formats by the data, including mimetype, encoding and schema. + /// + [XmlElement("Format")] + public Format[] Formats { get; set; } + + } +} From bc7c7ce923c26fc8a5f62c9b7d5f501833d7f03a Mon Sep 17 00:00:00 2001 From: Mihai Stan Date: Mon, 27 May 2019 12:37:56 +0200 Subject: [PATCH 11/76] Add LiteralData model with deserialization tests --- .../ModelDeserializationTests.cs | 21 +++++++++++++++++++ src/Wps/Wps.Client/Models/Data/LiteralData.cs | 15 +++++++++++++ 2 files changed, 36 insertions(+) create mode 100644 src/Wps/Wps.Client/Models/Data/LiteralData.cs diff --git a/src/Wps/Wps.Client.Tests/ModelDeserializationTests.cs b/src/Wps/Wps.Client.Tests/ModelDeserializationTests.cs index 5596492..fb24ed6 100644 --- a/src/Wps/Wps.Client.Tests/ModelDeserializationTests.cs +++ b/src/Wps/Wps.Client.Tests/ModelDeserializationTests.cs @@ -126,5 +126,26 @@ public void DeserializeLiteralDataDomain_ValidXmlGiven_WithValueReference_Should } } + [Fact] + public void DeserializeLiteralData_ValidXmlGiven_WithLiteralDataDomain_ShouldPass() + { + var serialize = new XmlSerializer(typeof(LiteralData)); + var xml = @" + + + + + + + "; + + using (var reader = new StringReader(xml)) + { + var data = serialize.Deserialize(reader) as LiteralData; + data.Should().NotBeNull(); + data?.Formats.Length.Should().Be(2); + data?.LiteralDataDomains.Length.Should().Be(1); + } + } } } diff --git a/src/Wps/Wps.Client/Models/Data/LiteralData.cs b/src/Wps/Wps.Client/Models/Data/LiteralData.cs new file mode 100644 index 0000000..17672e1 --- /dev/null +++ b/src/Wps/Wps.Client/Models/Data/LiteralData.cs @@ -0,0 +1,15 @@ +using System.Xml.Schema; +using System.Xml.Serialization; +using Wps.Client.Utils; + +namespace Wps.Client.Models.Data +{ + [XmlRoot("LiteralData", Namespace = ModelNamespaces.Wps)] + public class LiteralData : Data + { + + [XmlElement("LiteralDataDomain", Type = typeof(LiteralDataDomain), Form = XmlSchemaForm.Unqualified)] + public LiteralDataDomain[] LiteralDataDomains { get; set; } + + } +} From 1c06ab12bc987bd97fcef6d3cacce0db8cbd689a Mon Sep 17 00:00:00 2001 From: Mihai Stan Date: Mon, 27 May 2019 13:49:38 +0200 Subject: [PATCH 12/76] Add base model for identifiable objects --- .../Wps.Client/Models/IdentifiableObject.cs | 34 +++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 src/Wps/Wps.Client/Models/IdentifiableObject.cs diff --git a/src/Wps/Wps.Client/Models/IdentifiableObject.cs b/src/Wps/Wps.Client/Models/IdentifiableObject.cs new file mode 100644 index 0000000..2a21142 --- /dev/null +++ b/src/Wps/Wps.Client/Models/IdentifiableObject.cs @@ -0,0 +1,34 @@ +using System.Xml.Serialization; +using Wps.Client.Utils; + +namespace Wps.Client.Models +{ + /// + /// Represents an object that has properties allowing itself to be identified. + /// + public abstract class IdentifiableObject + { + + /// + /// Human readable title for the object. (i.e. Input, Output, Process, etc.) + /// + /// + /// This property is required and shall not be null! + /// + [XmlElement("Title", Namespace = ModelNamespaces.Ows)] + public string Title { get; set; } + + /// + /// Human readable short description of the object. (i.e. Input, Output, Process, etc.) + /// + [XmlElement("Abstract", Namespace = ModelNamespaces.Ows)] + public string Abstract { get; set; } + + /// + /// Unambiguous identifier of the object. (i.e. Input, Output, Process, etc.) + /// + [XmlElement("Identifier", Namespace = ModelNamespaces.Ows)] + public string Identifier { get; set; } + + } +} From 7850f14682d4ab13e32fd6a01d827076efe2ffaa Mon Sep 17 00:00:00 2001 From: Mihai Stan Date: Mon, 27 May 2019 13:50:47 +0200 Subject: [PATCH 13/76] Add ComplexData base model --- src/Wps/Wps.Client/Models/Data/ComplexData.cs | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 src/Wps/Wps.Client/Models/Data/ComplexData.cs diff --git a/src/Wps/Wps.Client/Models/Data/ComplexData.cs b/src/Wps/Wps.Client/Models/Data/ComplexData.cs new file mode 100644 index 0000000..33c0d04 --- /dev/null +++ b/src/Wps/Wps.Client/Models/Data/ComplexData.cs @@ -0,0 +1,9 @@ +namespace Wps.Client.Models.Data +{ + public class ComplexData : Data + { + + + + } +} From 7c2a604d09476b70ce13eb056b2b39d8ca291998 Mon Sep 17 00:00:00 2001 From: Mihai Stan Date: Mon, 27 May 2019 13:51:23 +0200 Subject: [PATCH 14/76] Add Output model with deserialization tests --- .../ModelDeserializationTests.cs | 47 ++++++++++++++++++- src/Wps/Wps.Client/Models/Output.cs | 27 +++++++++++ 2 files changed, 72 insertions(+), 2 deletions(-) create mode 100644 src/Wps/Wps.Client/Models/Output.cs diff --git a/src/Wps/Wps.Client.Tests/ModelDeserializationTests.cs b/src/Wps/Wps.Client.Tests/ModelDeserializationTests.cs index fb24ed6..28ec4d9 100644 --- a/src/Wps/Wps.Client.Tests/ModelDeserializationTests.cs +++ b/src/Wps/Wps.Client.Tests/ModelDeserializationTests.cs @@ -73,7 +73,7 @@ public void DeserializeLiteralDataDomain_ValidXmlGiven_WithAllowedValues_ShouldP domain?.DefaultValue.Should().Be("uncorrected"); domain?.PossibleLiteralValues.GetType().Should().Be(typeof(AllowedValues)); domain?.DataType?.Reference.Should().Be("string"); - if(domain?.PossibleLiteralValues is AllowedValues values) + if (domain?.PossibleLiteralValues is AllowedValues values) { values.Values.Length.Should().Be(6); } @@ -98,7 +98,7 @@ public void DeserializeLiteralDataDomain_ValidXmlGiven_WithAnyValue_ShouldPass() domain?.IsDefault.Should().BeTrue(); domain?.DefaultValue.Should().Be("uncorrected"); domain?.PossibleLiteralValues.GetType().Should().Be(typeof(AnyValue)); - domain?.DataType?.Reference.Should().Be("string"); + domain?.DataType?.Reference.Should().Be("string"); } } @@ -147,5 +147,48 @@ public void DeserializeLiteralData_ValidXmlGiven_WithLiteralDataDomain_ShouldPas data?.LiteralDataDomains.Length.Should().Be(1); } } + + [Fact] + public void DeserializeOutput_ValidXmlGiven_WithNoNesting_ShouldPass() + { + var serialize = new XmlSerializer(typeof(Output)); + var xml = @" + Module output on stdout + The output of the module written to stdout + stdout + + + + + "; + + using (var reader = new StringReader(xml)) + { + var output = serialize.Deserialize(reader) as Output; + output?.Data.GetType().Should().Be(typeof(ComplexData)); + output?.Title.Should().Be("Module output on stdout"); + output?.Abstract.Should().Be("The output of the module written to stdout"); + output?.Identifier.Should().Be("stdout"); + output?.Outputs.Should().BeNull(); + } + } + + [Fact] + public void DeserializeOutput_ValidXmlGiven_WithOneLevelNesting_ShouldPass() + { + var serialize = new XmlSerializer(typeof(Output)); + var xml = @" + + + "; + + using (var reader = new StringReader(xml)) + { + var output = serialize.Deserialize(reader) as Output; + output?.Outputs.Length.Should().Be(1); + output?.Outputs[0].GetType().Should().Be(typeof(Output)); + } + } + } } diff --git a/src/Wps/Wps.Client/Models/Output.cs b/src/Wps/Wps.Client/Models/Output.cs new file mode 100644 index 0000000..8a3a4bb --- /dev/null +++ b/src/Wps/Wps.Client/Models/Output.cs @@ -0,0 +1,27 @@ +using System; +using System.Xml.Serialization; +using Wps.Client.Models.Data; +using Wps.Client.Utils; + +namespace Wps.Client.Models +{ + [Serializable] + [XmlRoot("Output", Namespace = ModelNamespaces.Wps)] + public class Output : IdentifiableObject + { + + /// + /// The data that will be present in this output. + /// + [XmlElement("LiteralData", Type = typeof(LiteralData), Namespace = ModelNamespaces.Wps), + XmlElement("ComplexData", Type = typeof(ComplexData), Namespace = ModelNamespaces.Wps)] + public Data.Data Data { get; set; } + + /// + /// Nested outputs. The nesting level should be as low as possible. + /// + [XmlElement("Output", Type = typeof(Output), Namespace = ModelNamespaces.Wps)] + public Output[] Outputs; + + } +} From ce0b2171ac0282b73d1c4c79b4b41296af487b9a Mon Sep 17 00:00:00 2001 From: Mihai Stan Date: Mon, 27 May 2019 14:14:07 +0200 Subject: [PATCH 15/76] Add Input model with deserialization tests --- .../ModelDeserializationTests.cs | 53 +++++++++++++++++++ src/Wps/Wps.Client/Models/Input.cs | 47 ++++++++++++++++ 2 files changed, 100 insertions(+) create mode 100644 src/Wps/Wps.Client/Models/Input.cs diff --git a/src/Wps/Wps.Client.Tests/ModelDeserializationTests.cs b/src/Wps/Wps.Client.Tests/ModelDeserializationTests.cs index 28ec4d9..ff33192 100644 --- a/src/Wps/Wps.Client.Tests/ModelDeserializationTests.cs +++ b/src/Wps/Wps.Client.Tests/ModelDeserializationTests.cs @@ -190,5 +190,58 @@ public void DeserializeOutput_ValidXmlGiven_WithOneLevelNesting_ShouldPass() } } + [Fact] + public void DeserializeInput_ValidXmlGiven_WithNoNesting_ShouldPass() + { + var serialize = new XmlSerializer(typeof(Input)); + var xml = @" + Default: center only + -n + Test abstract + + + + + + true + false + + + false + + + "; + + using (var reader = new StringReader(xml)) + { + var input = serialize.Deserialize(reader) as Input; + input?.Data.GetType().Should().Be(typeof(LiteralData)); + input?.MinimumOccurrences.Should().Be(0); + input?.MaximumOccurrences.Should().Be(1); + input?.Title.Should().Be("Default: center only"); + input?.Abstract.Should().Be("Test abstract"); + input?.Identifier.Should().Be("-n"); + input?.Inputs.Should().BeNull(); + } + } + + [Fact] + public void DeserializeInput_ValidXmlGiven_WithOneLevelNesting_ShouldPass() + { + var serialize = new XmlSerializer(typeof(Input)); + var xml = @" + + + "; + + using (var reader = new StringReader(xml)) + { + var input = serialize.Deserialize(reader) as Input; + input?.Inputs.Length.Should().Be(1); + input?.Inputs[0].GetType().Should().Be(typeof(Input)); + } + } + } } diff --git a/src/Wps/Wps.Client/Models/Input.cs b/src/Wps/Wps.Client/Models/Input.cs new file mode 100644 index 0000000..f109d35 --- /dev/null +++ b/src/Wps/Wps.Client/Models/Input.cs @@ -0,0 +1,47 @@ +using System; +using System.Xml.Schema; +using System.Xml.Serialization; +using Wps.Client.Models.Data; +using Wps.Client.Utils; + +namespace Wps.Client.Models +{ + [Serializable] + [XmlRoot(ElementName = "Input", Namespace = ModelNamespaces.Wps)] + public class Input : IdentifiableObject + { + + /// + /// Keywords that characterize the object. (i.e. Input, Output, Process, etc.) + /// + [XmlArray("Keywords", Namespace = ModelNamespaces.Ows)] + [XmlArrayItem(typeof(string))] + public string[] Keywords; + + /// + /// Minimum number of times that values for this parameter are required + /// + [XmlAttribute("minOccurs", Form = XmlSchemaForm.Unqualified)] + public int MinimumOccurrences { get; set; } = 1; + + /// + /// Maximum number of times that this parameter may be present + /// + [XmlAttribute("maxOccurs", Form = XmlSchemaForm.Unqualified)] + public int MaximumOccurrences { get; set; } = 1; + + /// + /// The data that will be present in this input. + /// + [XmlElement("LiteralData", Type = typeof(LiteralData), Namespace = ModelNamespaces.Wps), + XmlElement("ComplexData", Type = typeof(ComplexData), Namespace = ModelNamespaces.Wps)] + public Data.Data Data { get; set; } + + /// + /// Nested inputs. The nesting level should be as low as possible. + /// + [XmlElement("Input", Type = typeof(Input), Namespace = ModelNamespaces.Wps)] + public Input[] Inputs; + + } +} From d55d4c008245e4e62325a19dd9fc99ca069d4a80 Mon Sep 17 00:00:00 2001 From: Mihai Stan Date: Mon, 27 May 2019 14:33:23 +0200 Subject: [PATCH 16/76] Add Process model with deserialization tests --- .../ModelDeserializationTests.cs | 31 +++++++++++++++++++ src/Wps/Wps.Client/Models/Process.cs | 28 +++++++++++++++++ 2 files changed, 59 insertions(+) create mode 100644 src/Wps/Wps.Client/Models/Process.cs diff --git a/src/Wps/Wps.Client.Tests/ModelDeserializationTests.cs b/src/Wps/Wps.Client.Tests/ModelDeserializationTests.cs index ff33192..5f29969 100644 --- a/src/Wps/Wps.Client.Tests/ModelDeserializationTests.cs +++ b/src/Wps/Wps.Client.Tests/ModelDeserializationTests.cs @@ -243,5 +243,36 @@ public void DeserializeInput_ValidXmlGiven_WithOneLevelNesting_ShouldPass() } } + [Fact] + public void DeserializeProcess_ValidXmlGiven_ShouldPass() + { + var serializer = new XmlSerializer(typeof(Process)); + var xml = @" + Targets an imagery group to a GRASS location and mapset. + http://grass.osgeo.org/grass70/manuals/html70_user/i.target.html + i.target + + + + + + + + + + + "; + + using(var reader = new StringReader(xml)) + { + var process = serializer.Deserialize(reader) as Process; + process?.Inputs.Length.Should().Be(4); + process?.Outputs.Length.Should().Be(1); + process?.Title.Should().Be("Targets an imagery group to a GRASS location and mapset."); + process?.Abstract.Should().Be("http://grass.osgeo.org/grass70/manuals/html70_user/i.target.html"); + process?.Identifier.Should().Be("i.target"); + } + } + } } diff --git a/src/Wps/Wps.Client/Models/Process.cs b/src/Wps/Wps.Client/Models/Process.cs new file mode 100644 index 0000000..9533411 --- /dev/null +++ b/src/Wps/Wps.Client/Models/Process.cs @@ -0,0 +1,28 @@ +using System; +using System.Xml.Serialization; +using Wps.Client.Utils; + +namespace Wps.Client.Models +{ + /// + /// Specifies the requirements for a process offering. + /// + [Serializable] + [XmlRoot("Process", Namespace = ModelNamespaces.Wps)] + public class Process : IdentifiableObject + { + + /// + /// The inputs of the process + /// + [XmlElement("Input", Namespace = ModelNamespaces.Wps)] + public Input[] Inputs { get; set; } + + /// + /// The outputs of the process + /// + [XmlElement("Output", Namespace = ModelNamespaces.Wps)] + public Output[] Outputs { get; set; } + + } +} From 3bddd95d7334777fd0f8a130584faf5a45f0afa2 Mon Sep 17 00:00:00 2001 From: Mihai Stan Date: Mon, 27 May 2019 14:41:13 +0200 Subject: [PATCH 17/76] Cover UnitOfMeasure deserialization in tests --- src/Wps/Wps.Client.Tests/ModelDeserializationTests.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Wps/Wps.Client.Tests/ModelDeserializationTests.cs b/src/Wps/Wps.Client.Tests/ModelDeserializationTests.cs index 5f29969..71f52cc 100644 --- a/src/Wps/Wps.Client.Tests/ModelDeserializationTests.cs +++ b/src/Wps/Wps.Client.Tests/ModelDeserializationTests.cs @@ -73,6 +73,7 @@ public void DeserializeLiteralDataDomain_ValidXmlGiven_WithAllowedValues_ShouldP domain?.DefaultValue.Should().Be("uncorrected"); domain?.PossibleLiteralValues.GetType().Should().Be(typeof(AllowedValues)); domain?.DataType?.Reference.Should().Be("string"); + domain?.UnitOfMeasure.Should().Be("Meter"); if (domain?.PossibleLiteralValues is AllowedValues values) { values.Values.Length.Should().Be(6); @@ -99,7 +100,7 @@ public void DeserializeLiteralDataDomain_ValidXmlGiven_WithAnyValue_ShouldPass() domain?.DefaultValue.Should().Be("uncorrected"); domain?.PossibleLiteralValues.GetType().Should().Be(typeof(AnyValue)); domain?.DataType?.Reference.Should().Be("string"); - + domain?.UnitOfMeasure.Should().Be("Meter"); } } From 820d77d74bcdd437e801bf9be2626a2a8f70d555 Mon Sep 17 00:00:00 2001 From: Mihai Stan Date: Tue, 28 May 2019 11:51:27 +0200 Subject: [PATCH 18/76] Add ProcessOffering model with deserialization tests --- .../ModelDeserializationTests.cs | 22 +++++++++ src/Wps/Wps.Client/Models/ProcessOffering.cs | 48 +++++++++++++++++++ 2 files changed, 70 insertions(+) create mode 100644 src/Wps/Wps.Client/Models/ProcessOffering.cs diff --git a/src/Wps/Wps.Client.Tests/ModelDeserializationTests.cs b/src/Wps/Wps.Client.Tests/ModelDeserializationTests.cs index 71f52cc..d775bcf 100644 --- a/src/Wps/Wps.Client.Tests/ModelDeserializationTests.cs +++ b/src/Wps/Wps.Client.Tests/ModelDeserializationTests.cs @@ -275,5 +275,27 @@ public void DeserializeProcess_ValidXmlGiven_ShouldPass() } } + [Fact] + public void DeserializeProcessOffering_ValidXmlGiven_ShouldPass() + { + var serializer = new XmlSerializer(typeof(ProcessOffering)); + var xml = @" + + + "; + + using (var reader = new StringReader(xml)) + { + var processOffering = serializer.Deserialize(reader) as ProcessOffering; + processOffering?.ProcessVersion.Should().Be("1.0.0"); + processOffering?.JobControlOptions.Should().Be("sync-execute async-execute"); + processOffering?.OutputTransmission.Should().Be("value reference"); + processOffering?.ProcessModel.Should().Be("test"); + processOffering?.Process.Should().NotBeNull(); + processOffering?.Process.GetType().Should().Be(typeof(Process)); + } + } + } } diff --git a/src/Wps/Wps.Client/Models/ProcessOffering.cs b/src/Wps/Wps.Client/Models/ProcessOffering.cs new file mode 100644 index 0000000..1c47c83 --- /dev/null +++ b/src/Wps/Wps.Client/Models/ProcessOffering.cs @@ -0,0 +1,48 @@ +using System.Xml.Schema; +using System.Xml.Serialization; +using Wps.Client.Utils; + +namespace Wps.Client.Models +{ + [XmlRoot("ProcessOffering", Namespace = ModelNamespaces.Wps)] + public class ProcessOffering + { + + /// + /// Type of the process description. + /// + [XmlAttribute("processModel", Namespace = ModelNamespaces.Wps)] + public string ProcessModel { get; set; } = "native"; + + /// + /// Job control options supported for this process. (i.e. sync-execute, async-execute) + /// + /// + /// At least one option is required. + /// + [XmlAttribute("jobControlOptions", Namespace = ModelNamespaces.Wps)] + public string JobControlOptions { get; set; } + + /// + /// Supported transmission modes for the outputs. (i.e. by value, by reference) + /// + /// + /// At least one mode is required. + /// + [XmlAttribute("outputTransmission", Namespace = ModelNamespaces.Wps)] + public string OutputTransmission { get; set; } + + /// + /// Release version of the process. + /// + [XmlAttribute("processVersion", Namespace = ModelNamespaces.Wps)] + public string ProcessVersion { get; set; } + + /// + /// The process enclosed by the process offering. + /// + [XmlElement("Process", Namespace = ModelNamespaces.Wps)] + public Process Process { get; set; } + + } +} From 5216e44d5421eacc3994b783364360bfa3d266e4 Mon Sep 17 00:00:00 2001 From: Mihai Stan Date: Tue, 28 May 2019 14:33:10 +0200 Subject: [PATCH 19/76] Add ProcessOfferings model and deserialization tests --- .../ModelDeserializationTests.cs | 28 +++++- .../Models/ProcessOfferingCollection.cs | 91 +++++++++++++++++++ 2 files changed, 118 insertions(+), 1 deletion(-) create mode 100644 src/Wps/Wps.Client/Models/ProcessOfferingCollection.cs diff --git a/src/Wps/Wps.Client.Tests/ModelDeserializationTests.cs b/src/Wps/Wps.Client.Tests/ModelDeserializationTests.cs index d775bcf..fd88212 100644 --- a/src/Wps/Wps.Client.Tests/ModelDeserializationTests.cs +++ b/src/Wps/Wps.Client.Tests/ModelDeserializationTests.cs @@ -264,7 +264,7 @@ public void DeserializeProcess_ValidXmlGiven_ShouldPass() "; - using(var reader = new StringReader(xml)) + using (var reader = new StringReader(xml)) { var process = serializer.Deserialize(reader) as Process; process?.Inputs.Length.Should().Be(4); @@ -297,5 +297,31 @@ public void DeserializeProcessOffering_ValidXmlGiven_ShouldPass() } } + [Fact] + public void DeserializeProcessOfferingCollection_ValidXmlGiven_ShouldPass() + { + var serializer = new XmlSerializer(typeof(ProcessOfferingCollection)); + var xml = @" + + + + + "; + + using (var reader = new StringReader(xml)) + { + var processOfferingCollection = serializer.Deserialize(reader) as ProcessOfferingCollection; + processOfferingCollection?.Count.Should().Be(3); + if (processOfferingCollection != null) + { + foreach (var offering in processOfferingCollection) + { + offering.Should().NotBeNull(); + offering.GetType().Should().Be(typeof(ProcessOffering)); + } + } + } + } + } } diff --git a/src/Wps/Wps.Client/Models/ProcessOfferingCollection.cs b/src/Wps/Wps.Client/Models/ProcessOfferingCollection.cs new file mode 100644 index 0000000..74b6ed6 --- /dev/null +++ b/src/Wps/Wps.Client/Models/ProcessOfferingCollection.cs @@ -0,0 +1,91 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Xml.Serialization; +using Wps.Client.Utils; + +namespace Wps.Client.Models +{ + /// + /// A collection containing _processOfferings. + /// + [Serializable] + [XmlRoot("ProcessOfferings", Namespace = ModelNamespaces.Wps)] + public class ProcessOfferingCollection : IList + { + + #region Model Properties + + /// + /// An array containing the ProcessOffering/s. + /// + [XmlElement(ElementName = "ProcessOffering", Namespace = ModelNamespaces.Wps)] + private List _processOfferings = new List(); + + #endregion + + #region List Properties & Methods + + public IEnumerator GetEnumerator() + { + return _processOfferings.GetEnumerator(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + public void Add(ProcessOffering item) + { + _processOfferings.Add(item); + } + + public void Clear() + { + _processOfferings.Clear(); + } + + public bool Contains(ProcessOffering item) + { + return _processOfferings.Contains(item); + } + + public void CopyTo(ProcessOffering[] array, int arrayIndex) + { + _processOfferings.CopyTo(array, arrayIndex); + } + + public bool Remove(ProcessOffering item) + { + return _processOfferings.Remove(item); + } + + [XmlIgnore] public int Count => _processOfferings.Count; + [XmlIgnore] public bool IsReadOnly => false; + + public int IndexOf(ProcessOffering item) + { + return _processOfferings.IndexOf(item); + } + + public void Insert(int index, ProcessOffering item) + { + _processOfferings.Insert(index, item); + } + + public void RemoveAt(int index) + { + _processOfferings.RemoveAt(index); + } + + public ProcessOffering this[int index] + { + get => _processOfferings[index]; + set => _processOfferings[index] = value; + } + + #endregion + + } +} From af0b5fa9833a94124941bb2ffb34d7ac4a90fb3c Mon Sep 17 00:00:00 2001 From: Mihai Stan Date: Tue, 28 May 2019 16:48:58 +0200 Subject: [PATCH 20/76] Add XML serialization service Add one more abstraction layer to separate the hardly referenced XmlSerializer object from the consumers. --- .../ModelDeserializationTests.cs | 221 +++++++----------- .../XmlSerializationServiceTests.cs | 67 ++++++ src/Wps/Wps.Client/Services/IXmlSerializer.cs | 10 + .../Services/XmlSerializationService.cs | 40 ++++ 4 files changed, 207 insertions(+), 131 deletions(-) create mode 100644 src/Wps/Wps.Client.Tests/XmlSerializationServiceTests.cs create mode 100644 src/Wps/Wps.Client/Services/IXmlSerializer.cs create mode 100644 src/Wps/Wps.Client/Services/XmlSerializationService.cs diff --git a/src/Wps/Wps.Client.Tests/ModelDeserializationTests.cs b/src/Wps/Wps.Client.Tests/ModelDeserializationTests.cs index fd88212..ac7df22 100644 --- a/src/Wps/Wps.Client.Tests/ModelDeserializationTests.cs +++ b/src/Wps/Wps.Client.Tests/ModelDeserializationTests.cs @@ -1,57 +1,54 @@ using FluentAssertions; using System.IO; -using System.Xml.Serialization; using Wps.Client.Models; using Wps.Client.Models.Data; +using Wps.Client.Services; using Xunit; namespace Wps.Client.Tests { - public class ModelDeserializationTests + public class ModelDeserializationTests : IClassFixture { + private readonly IXmlSerializer _serializer; + + public ModelDeserializationTests(XmlSerializationService serializer) + { + _serializer = serializer; + } + [Fact] public void DeserializeFormat_ValidFormatGiven_ShouldPass() { - var serializer = new XmlSerializer(typeof(Format)); - var xml = - @""; + const string xml = @""; - using (var reader = new StringReader(xml)) + var format = _serializer.Deserialize(xml); + format.Should().NotBeNull(); + if (format != null) { - var format = serializer.Deserialize(reader) as Format; - format.Should().NotBeNull(); - if (format != null) - { - format.MimeType.Should().Be("application/x-geotiff"); - format.Encoding.Should().Be("base64"); - format.Schema.Should().Be("test.schema.tld"); - format.MaximumMegabytes.Should().Be(2048); - format.IsDefault.Should().BeTrue(); - } + format.MimeType.Should().Be("application/x-geotiff"); + format.Encoding.Should().Be("base64"); + format.Schema.Should().Be("test.schema.tld"); + format.MaximumMegabytes.Should().Be(2048); + format.IsDefault.Should().BeTrue(); } } [Fact] public void DeserializeDataType_ValidXmlGiven_ShouldPass() { - var serialize = new XmlSerializer(typeof(DataType)); - var xml = @""; + const string xml = @""; - using (var reader = new StringReader(xml)) - { - var dataType = serialize.Deserialize(reader) as DataType; - dataType.Should().NotBeNull(); - dataType?.Reference.Should().Be("string"); - dataType?.GetReferenceType().Should().Be(typeof(string)); - } + var dataType = _serializer.Deserialize(xml); + dataType.Should().NotBeNull(); + dataType?.Reference.Should().Be("string"); + dataType?.GetReferenceType().Should().Be(typeof(string)); } [Fact] public void DeserializeLiteralDataDomain_ValidXmlGiven_WithAllowedValues_ShouldPass() { - var serialize = new XmlSerializer(typeof(LiteralDataDomain)); - var xml = @" + const string xml = @" uncorrected dos1 @@ -65,27 +62,23 @@ public void DeserializeLiteralDataDomain_ValidXmlGiven_WithAllowedValues_ShouldP Meter "; - using (var reader = new StringReader(xml)) + var domain = _serializer.Deserialize(xml); + domain.Should().NotBeNull(); + domain?.IsDefault.Should().BeTrue(); + domain?.DefaultValue.Should().Be("uncorrected"); + domain?.PossibleLiteralValues.GetType().Should().Be(typeof(AllowedValues)); + domain?.DataType?.Reference.Should().Be("string"); + domain?.UnitOfMeasure.Should().Be("Meter"); + if (domain?.PossibleLiteralValues is AllowedValues values) { - var domain = serialize.Deserialize(reader) as LiteralDataDomain; - domain.Should().NotBeNull(); - domain?.IsDefault.Should().BeTrue(); - domain?.DefaultValue.Should().Be("uncorrected"); - domain?.PossibleLiteralValues.GetType().Should().Be(typeof(AllowedValues)); - domain?.DataType?.Reference.Should().Be("string"); - domain?.UnitOfMeasure.Should().Be("Meter"); - if (domain?.PossibleLiteralValues is AllowedValues values) - { - values.Values.Length.Should().Be(6); - } + values.Values.Length.Should().Be(6); } } [Fact] public void DeserializeLiteralDataDomain_ValidXmlGiven_WithAnyValue_ShouldPass() { - var serialize = new XmlSerializer(typeof(LiteralDataDomain)); - var xml = @" + const string xml = @" uncorrected @@ -94,7 +87,7 @@ public void DeserializeLiteralDataDomain_ValidXmlGiven_WithAnyValue_ShouldPass() using (var reader = new StringReader(xml)) { - var domain = serialize.Deserialize(reader) as LiteralDataDomain; + var domain = _serializer.Deserialize(xml); domain.Should().NotBeNull(); domain?.IsDefault.Should().BeTrue(); domain?.DefaultValue.Should().Be("uncorrected"); @@ -107,31 +100,25 @@ public void DeserializeLiteralDataDomain_ValidXmlGiven_WithAnyValue_ShouldPass() [Fact] public void DeserializeLiteralDataDomain_ValidXmlGiven_WithValueReference_ShouldPass() { - var serialize = new XmlSerializer(typeof(LiteralDataDomain)); - var xml = @" + const string xml = @" uncorrected Meter "; - using (var reader = new StringReader(xml)) - { - var domain = serialize.Deserialize(reader) as LiteralDataDomain; - domain.Should().NotBeNull(); - domain?.IsDefault.Should().BeTrue(); - domain?.DefaultValue.Should().Be("uncorrected"); - domain?.PossibleLiteralValues.GetType().Should().Be(typeof(ValueReference)); - domain?.DataType?.Reference.Should().Be("string"); - - } + var domain = _serializer.Deserialize(xml); + domain.Should().NotBeNull(); + domain?.IsDefault.Should().BeTrue(); + domain?.DefaultValue.Should().Be("uncorrected"); + domain?.PossibleLiteralValues.GetType().Should().Be(typeof(ValueReference)); + domain?.DataType?.Reference.Should().Be("string"); } [Fact] public void DeserializeLiteralData_ValidXmlGiven_WithLiteralDataDomain_ShouldPass() { - var serialize = new XmlSerializer(typeof(LiteralData)); - var xml = @" + const string xml = @" @@ -142,7 +129,7 @@ public void DeserializeLiteralData_ValidXmlGiven_WithLiteralDataDomain_ShouldPas using (var reader = new StringReader(xml)) { - var data = serialize.Deserialize(reader) as LiteralData; + var data = _serializer.Deserialize(xml); data.Should().NotBeNull(); data?.Formats.Length.Should().Be(2); data?.LiteralDataDomains.Length.Should().Be(1); @@ -152,8 +139,7 @@ public void DeserializeLiteralData_ValidXmlGiven_WithLiteralDataDomain_ShouldPas [Fact] public void DeserializeOutput_ValidXmlGiven_WithNoNesting_ShouldPass() { - var serialize = new XmlSerializer(typeof(Output)); - var xml = @" + const string xml = @" Module output on stdout The output of the module written to stdout stdout @@ -163,39 +149,31 @@ public void DeserializeOutput_ValidXmlGiven_WithNoNesting_ShouldPass() "; - using (var reader = new StringReader(xml)) - { - var output = serialize.Deserialize(reader) as Output; - output?.Data.GetType().Should().Be(typeof(ComplexData)); - output?.Title.Should().Be("Module output on stdout"); - output?.Abstract.Should().Be("The output of the module written to stdout"); - output?.Identifier.Should().Be("stdout"); - output?.Outputs.Should().BeNull(); - } + var output = _serializer.Deserialize(xml); + output?.Data.GetType().Should().Be(typeof(ComplexData)); + output?.Title.Should().Be("Module output on stdout"); + output?.Abstract.Should().Be("The output of the module written to stdout"); + output?.Identifier.Should().Be("stdout"); + output?.Outputs.Should().BeNull(); } [Fact] public void DeserializeOutput_ValidXmlGiven_WithOneLevelNesting_ShouldPass() { - var serialize = new XmlSerializer(typeof(Output)); - var xml = @" + const string xml = @" "; - using (var reader = new StringReader(xml)) - { - var output = serialize.Deserialize(reader) as Output; - output?.Outputs.Length.Should().Be(1); - output?.Outputs[0].GetType().Should().Be(typeof(Output)); - } + var output = _serializer.Deserialize(xml); + output?.Outputs.Length.Should().Be(1); + output?.Outputs[0].GetType().Should().Be(typeof(Output)); } [Fact] public void DeserializeInput_ValidXmlGiven_WithNoNesting_ShouldPass() { - var serialize = new XmlSerializer(typeof(Input)); - var xml = @" + const string xml = @" Default: center only -n Test abstract @@ -214,41 +192,33 @@ public void DeserializeInput_ValidXmlGiven_WithNoNesting_ShouldPass() "; - using (var reader = new StringReader(xml)) - { - var input = serialize.Deserialize(reader) as Input; - input?.Data.GetType().Should().Be(typeof(LiteralData)); - input?.MinimumOccurrences.Should().Be(0); - input?.MaximumOccurrences.Should().Be(1); - input?.Title.Should().Be("Default: center only"); - input?.Abstract.Should().Be("Test abstract"); - input?.Identifier.Should().Be("-n"); - input?.Inputs.Should().BeNull(); - } + var input = _serializer.Deserialize(xml); + input?.Data.GetType().Should().Be(typeof(LiteralData)); + input?.MinimumOccurrences.Should().Be(0); + input?.MaximumOccurrences.Should().Be(1); + input?.Title.Should().Be("Default: center only"); + input?.Abstract.Should().Be("Test abstract"); + input?.Identifier.Should().Be("-n"); + input?.Inputs.Should().BeNull(); } [Fact] public void DeserializeInput_ValidXmlGiven_WithOneLevelNesting_ShouldPass() { - var serialize = new XmlSerializer(typeof(Input)); - var xml = @" + const string xml = @" "; - using (var reader = new StringReader(xml)) - { - var input = serialize.Deserialize(reader) as Input; - input?.Inputs.Length.Should().Be(1); - input?.Inputs[0].GetType().Should().Be(typeof(Input)); - } + var input = _serializer.Deserialize(xml); + input?.Inputs.Length.Should().Be(1); + input?.Inputs[0].GetType().Should().Be(typeof(Input)); } [Fact] public void DeserializeProcess_ValidXmlGiven_ShouldPass() { - var serializer = new XmlSerializer(typeof(Process)); - var xml = @" + const string xml = @" Targets an imagery group to a GRASS location and mapset. http://grass.osgeo.org/grass70/manuals/html70_user/i.target.html i.target @@ -264,61 +234,50 @@ public void DeserializeProcess_ValidXmlGiven_ShouldPass() "; - using (var reader = new StringReader(xml)) - { - var process = serializer.Deserialize(reader) as Process; - process?.Inputs.Length.Should().Be(4); - process?.Outputs.Length.Should().Be(1); - process?.Title.Should().Be("Targets an imagery group to a GRASS location and mapset."); - process?.Abstract.Should().Be("http://grass.osgeo.org/grass70/manuals/html70_user/i.target.html"); - process?.Identifier.Should().Be("i.target"); - } + var process = _serializer.Deserialize(xml); + process?.Inputs.Length.Should().Be(4); + process?.Outputs.Length.Should().Be(1); + process?.Title.Should().Be("Targets an imagery group to a GRASS location and mapset."); + process?.Abstract.Should().Be("http://grass.osgeo.org/grass70/manuals/html70_user/i.target.html"); + process?.Identifier.Should().Be("i.target"); } [Fact] public void DeserializeProcessOffering_ValidXmlGiven_ShouldPass() { - var serializer = new XmlSerializer(typeof(ProcessOffering)); - var xml = @" "; - using (var reader = new StringReader(xml)) - { - var processOffering = serializer.Deserialize(reader) as ProcessOffering; - processOffering?.ProcessVersion.Should().Be("1.0.0"); - processOffering?.JobControlOptions.Should().Be("sync-execute async-execute"); - processOffering?.OutputTransmission.Should().Be("value reference"); - processOffering?.ProcessModel.Should().Be("test"); - processOffering?.Process.Should().NotBeNull(); - processOffering?.Process.GetType().Should().Be(typeof(Process)); - } + var processOffering = _serializer.Deserialize(xml); + processOffering?.ProcessVersion.Should().Be("1.0.0"); + processOffering?.JobControlOptions.Should().Be("sync-execute async-execute"); + processOffering?.OutputTransmission.Should().Be("value reference"); + processOffering?.ProcessModel.Should().Be("test"); + processOffering?.Process.Should().NotBeNull(); + processOffering?.Process.GetType().Should().Be(typeof(Process)); } [Fact] public void DeserializeProcessOfferingCollection_ValidXmlGiven_ShouldPass() { - var serializer = new XmlSerializer(typeof(ProcessOfferingCollection)); - var xml = @" + const string xml = @" "; - using (var reader = new StringReader(xml)) + var processOfferingCollection = _serializer.Deserialize(xml); + processOfferingCollection?.Count.Should().Be(3); + if (processOfferingCollection != null) { - var processOfferingCollection = serializer.Deserialize(reader) as ProcessOfferingCollection; - processOfferingCollection?.Count.Should().Be(3); - if (processOfferingCollection != null) + foreach (var offering in processOfferingCollection) { - foreach (var offering in processOfferingCollection) - { - offering.Should().NotBeNull(); - offering.GetType().Should().Be(typeof(ProcessOffering)); - } + offering.Should().NotBeNull(); + offering.GetType().Should().Be(typeof(ProcessOffering)); } } } diff --git a/src/Wps/Wps.Client.Tests/XmlSerializationServiceTests.cs b/src/Wps/Wps.Client.Tests/XmlSerializationServiceTests.cs new file mode 100644 index 0000000..4d9c18b --- /dev/null +++ b/src/Wps/Wps.Client.Tests/XmlSerializationServiceTests.cs @@ -0,0 +1,67 @@ +using FluentAssertions; +using System.Text.RegularExpressions; +using System.Xml.Serialization; +using Wps.Client.Services; +using Xunit; + +namespace Wps.Client.Tests +{ + public class XmlSerializationServiceTests : IClassFixture + { + + private readonly XmlSerializationService _serializer; + + public XmlSerializationServiceTests(XmlSerializationService serializer) + { + _serializer = serializer; + } + + [Fact] + public void SerializeObject_ValidObjectGiven_ShouldPass() + { + const string expectedXml = @" + + Test + 10 + "; + + // Remove white spaces and new line characters. They do not change the actual (de)serialization of the XML. + var trimmedExpectedXml = Regex.Replace(expectedXml, @"\s+", string.Empty); + + var obj = new TestObject + { + FirstProperty = "Test", + SecondProperty = 10 + }; + + var resultXml = _serializer.Serialize(obj); + var trimmedResult = Regex.Replace(resultXml, @"\s+", string.Empty); + trimmedResult.Should().Be(trimmedExpectedXml); + } + + [Fact] + public void DeserializeObject_ValidXmlGiven_ShouldPass() + { + const string xml = @" + + Test + 10 + "; + + var resultObject = _serializer.Deserialize(xml); + resultObject.FirstProperty.Should().Be("Test"); + resultObject.SecondProperty.Should().Be(10); + } + + [XmlRoot("TestObject")] + public class TestObject + { + [XmlElement("FirstProperty")] + public string FirstProperty { get; set; } + + [XmlElement("SecondProperty")] + public int SecondProperty { get; set; } + } + + } +} diff --git a/src/Wps/Wps.Client/Services/IXmlSerializer.cs b/src/Wps/Wps.Client/Services/IXmlSerializer.cs new file mode 100644 index 0000000..1e8773a --- /dev/null +++ b/src/Wps/Wps.Client/Services/IXmlSerializer.cs @@ -0,0 +1,10 @@ +namespace Wps.Client.Services +{ + public interface IXmlSerializer + { + + string Serialize(T obj); + T Deserialize(string xml); + + } +} diff --git a/src/Wps/Wps.Client/Services/XmlSerializationService.cs b/src/Wps/Wps.Client/Services/XmlSerializationService.cs new file mode 100644 index 0000000..fcae1de --- /dev/null +++ b/src/Wps/Wps.Client/Services/XmlSerializationService.cs @@ -0,0 +1,40 @@ +using System.IO; +using System.Text; +using System.Xml.Serialization; + +namespace Wps.Client.Services +{ + public class XmlSerializationService : IXmlSerializer + { + public string Serialize(T obj) + { + var serializer = new XmlSerializer(typeof(T)); + using (var writer = new CustomEncodingStringWriter(Encoding.UTF8)) + { + serializer.Serialize(writer, obj); + return writer.ToString(); + } + } + + public T Deserialize(string xml) + { + var serializer = new XmlSerializer(typeof(T)); + using (var reader = new StringReader(xml)) + { + var obj = (T) serializer.Deserialize(reader); + return obj; + } + } + + private class CustomEncodingStringWriter : StringWriter + { + public CustomEncodingStringWriter(Encoding encoding) + { + Encoding = encoding; + } + + public override Encoding Encoding { get; } + } + + } +} From 19df76aebf6e731bbdf6e4bc80300d405f02fda6 Mon Sep 17 00:00:00 2001 From: Mihai Stan Date: Tue, 28 May 2019 17:54:07 +0200 Subject: [PATCH 21/76] Add ProcessSummary model and deserialization tests --- .../ModelDeserializationTests.cs | 26 ++++++++++ src/Wps/Wps.Client/Models/ProcessSummary.cs | 48 +++++++++++++++++++ 2 files changed, 74 insertions(+) create mode 100644 src/Wps/Wps.Client/Models/ProcessSummary.cs diff --git a/src/Wps/Wps.Client.Tests/ModelDeserializationTests.cs b/src/Wps/Wps.Client.Tests/ModelDeserializationTests.cs index ac7df22..642d10a 100644 --- a/src/Wps/Wps.Client.Tests/ModelDeserializationTests.cs +++ b/src/Wps/Wps.Client.Tests/ModelDeserializationTests.cs @@ -282,5 +282,31 @@ public void DeserializeProcessOfferingCollection_ValidXmlGiven_ShouldPass() } } + [Fact] + public void DeserializeProcessSummary_ValidXmlGiven_ShouldPass() + { + const string xml = @" + Canonical components analysis (CCA) program for image processing. + i.cca + Test abstract + + WPS + geospatial + geoprocessing + + + "; + + var summary = _serializer.Deserialize(xml); + summary.Title.Should().Be("Canonical components analysis (CCA) program for image processing."); + summary.Identifier.Should().Be("i.cca"); + summary.Abstract.Should().Be("Test abstract"); + summary.ProcessVersion.Should().Be("1.0.0"); + summary.ProcessModel.Should().Be("test"); + summary.JobControlOptions.Should().Be("sync-execute async-execute"); + summary.OutputTransmission.Should().Be("value reference"); + summary.Keywords.Should().BeEquivalentTo("WPS", "geospatial", "geoprocessing"); + } + } } diff --git a/src/Wps/Wps.Client/Models/ProcessSummary.cs b/src/Wps/Wps.Client/Models/ProcessSummary.cs new file mode 100644 index 0000000..00caa95 --- /dev/null +++ b/src/Wps/Wps.Client/Models/ProcessSummary.cs @@ -0,0 +1,48 @@ +using System.Xml.Serialization; +using Wps.Client.Utils; + +namespace Wps.Client.Models +{ + [XmlRoot("ProcessSummary", Namespace = ModelNamespaces.Wps)] + public class ProcessSummary : IdentifiableObject + { + + /// + /// Type of the process description. + /// + [XmlAttribute("processModel", Namespace = ModelNamespaces.Wps)] + public string ProcessModel { get; set; } = "native"; + + /// + /// Job control options supported for this process. (i.e. sync-execute, async-execute) + /// + /// + /// At least one option is required. + /// + [XmlAttribute("jobControlOptions", Namespace = ModelNamespaces.Wps)] + public string JobControlOptions { get; set; } + + /// + /// Supported transmission modes for the outputs. (i.e. by value, by reference) + /// + /// + /// At least one mode is required. + /// + [XmlAttribute("outputTransmission", Namespace = ModelNamespaces.Wps)] + public string OutputTransmission { get; set; } + + /// + /// Keywords that characterize the process. + /// + [XmlArray("Keywords", Namespace = ModelNamespaces.Ows)] + [XmlArrayItem("Keyword", Namespace = ModelNamespaces.Ows)] + public string[] Keywords { get; set; } + + /// + /// Version of the process offered. + /// + [XmlAttribute("processVersion", Namespace = ModelNamespaces.Wps)] + public string ProcessVersion { get; set; } + + } +} From acb76ce8a149c433c250178a272ad61f720141b0 Mon Sep 17 00:00:00 2001 From: Mihai Stan Date: Tue, 28 May 2019 18:30:01 +0200 Subject: [PATCH 22/76] Add ServiceIdentification with deserialization tests --- .../ModelDeserializationTests.cs | 29 ++++++++++++++ .../Wps.Client/Models/DescriptiveObject.cs | 35 +++++++++++++++++ .../Models/ServiceIdentification.cs | 38 +++++++++++++++++++ 3 files changed, 102 insertions(+) create mode 100644 src/Wps/Wps.Client/Models/DescriptiveObject.cs create mode 100644 src/Wps/Wps.Client/Models/ServiceIdentification.cs diff --git a/src/Wps/Wps.Client.Tests/ModelDeserializationTests.cs b/src/Wps/Wps.Client.Tests/ModelDeserializationTests.cs index 642d10a..edc7ca0 100644 --- a/src/Wps/Wps.Client.Tests/ModelDeserializationTests.cs +++ b/src/Wps/Wps.Client.Tests/ModelDeserializationTests.cs @@ -308,5 +308,34 @@ public void DeserializeProcessSummary_ValidXmlGiven_ShouldPass() summary.Keywords.Should().BeEquivalentTo("WPS", "geospatial", "geoprocessing"); } + [Fact] + public void DeserializeServiceIdentification_ValidXmlGiven_ShouldPass() + { + const string xml = @" + 52°North WPS 4.0.0-beta.4-SNAPSHOT + Service based on the 52°North implementation of WPS 1.0.0 and 2.0.0 + + WPS + geospatial + geoprocessing + + WPS + 1.0.0 + 2.0.0 + NONE + NONE + "; + + var serviceIdentification = _serializer.Deserialize(xml); + serviceIdentification.Title.Should().Be("52°North WPS 4.0.0-beta.4-SNAPSHOT"); + serviceIdentification.Abstract.Should() + .Be("Service based on the 52°North implementation of WPS 1.0.0 and 2.0.0"); + serviceIdentification.Keywords.Should().BeEquivalentTo("WPS", "geospatial", "geoprocessing"); + serviceIdentification.Type.Should().Be("WPS"); + serviceIdentification.Versions.Should().BeEquivalentTo("1.0.0", "2.0.0"); + serviceIdentification.Fees.Should().Be("NONE"); + serviceIdentification.AccessConstraints.Should().Be("NONE"); + } + } } diff --git a/src/Wps/Wps.Client/Models/DescriptiveObject.cs b/src/Wps/Wps.Client/Models/DescriptiveObject.cs new file mode 100644 index 0000000..eb10a9d --- /dev/null +++ b/src/Wps/Wps.Client/Models/DescriptiveObject.cs @@ -0,0 +1,35 @@ +using System.Xml.Serialization; +using Wps.Client.Utils; + +namespace Wps.Client.Models +{ + /// + /// Object containing human-readable descriptive information. + /// + public abstract class DescriptiveObject + { + + /// + /// Human readable title for the object. (i.e. Input, Output, Process, etc.) + /// + /// + /// This property is required and shall not be null! + /// + [XmlElement("Title", Namespace = ModelNamespaces.Ows)] + public string Title { get; set; } + + /// + /// Human readable short description of the object. (i.e. Input, Output, Process, etc.) + /// + [XmlElement("Abstract", Namespace = ModelNamespaces.Ows)] + public string Abstract { get; set; } + + /// + /// Keywords that characterize the process. + /// + [XmlArray("Keywords", Namespace = ModelNamespaces.Ows)] + [XmlArrayItem("Keyword", Namespace = ModelNamespaces.Ows)] + public string[] Keywords { get; set; } + + } +} diff --git a/src/Wps/Wps.Client/Models/ServiceIdentification.cs b/src/Wps/Wps.Client/Models/ServiceIdentification.cs new file mode 100644 index 0000000..a6408fe --- /dev/null +++ b/src/Wps/Wps.Client/Models/ServiceIdentification.cs @@ -0,0 +1,38 @@ +using System.Xml.Serialization; +using Wps.Client.Utils; + +namespace Wps.Client.Models +{ + /// + /// General metadata for a specific server. + /// + [XmlRoot("ServiceIdentification", Namespace = ModelNamespaces.Ows)] + public class ServiceIdentification : DescriptiveObject + { + + /// + /// A service type name from a registry of services. + /// + [XmlElement("ServiceType", Namespace = ModelNamespaces.Ows)] + public string Type { get; set; } + + /// + /// Unordered list of one or more versions of this service type implemented by the server. + /// + [XmlElement("ServiceTypeVersion", Namespace = ModelNamespaces.Ows)] + public string Versions { get; set; } + + /// + /// Fees and terms for retrieving data from or otherwise using this server, including the monetary units as specified in ISO 4217. The reserved value NONE (case insensitive) shall be used to mean no fees or terms. + /// + [XmlElement("Fees", Namespace = ModelNamespaces.Ows)] + public string Fees { get; set; } + + /// + /// Access constraint applied to assure the protection of privacy or intellectual property, or any other restrictions on retrieving or using data from or otherwise using this server. The reserved value NONE (case insensitive) shall be used to mean no access constraints are imposed. + /// + [XmlElement("AccessConstraints", Namespace = ModelNamespaces.Ows)] + public string AccessConstraints { get; set; } + + } +} From f44f7caa2b0e583f089a26f8cb4c13319270f0f9 Mon Sep 17 00:00:00 2001 From: Mihai Stan Date: Wed, 29 May 2019 00:01:27 +0200 Subject: [PATCH 23/76] Add ServiceProvider with constituing models and unit tests --- .../ModelDeserializationTests.cs | 76 +++++++++++++++++++ src/Wps/Wps.Client/Models/Address.cs | 47 ++++++++++++ src/Wps/Wps.Client/Models/ContactInfo.cs | 40 ++++++++++ src/Wps/Wps.Client/Models/ProviderSite.cs | 17 +++++ src/Wps/Wps.Client/Models/ServiceContact.cs | 38 ++++++++++ src/Wps/Wps.Client/Models/ServiceProvider.cs | 29 +++++++ src/Wps/Wps.Client/Utils/ModelNamespaces.cs | 1 + 7 files changed, 248 insertions(+) create mode 100644 src/Wps/Wps.Client/Models/Address.cs create mode 100644 src/Wps/Wps.Client/Models/ContactInfo.cs create mode 100644 src/Wps/Wps.Client/Models/ProviderSite.cs create mode 100644 src/Wps/Wps.Client/Models/ServiceContact.cs create mode 100644 src/Wps/Wps.Client/Models/ServiceProvider.cs diff --git a/src/Wps/Wps.Client.Tests/ModelDeserializationTests.cs b/src/Wps/Wps.Client.Tests/ModelDeserializationTests.cs index edc7ca0..ed50b11 100644 --- a/src/Wps/Wps.Client.Tests/ModelDeserializationTests.cs +++ b/src/Wps/Wps.Client.Tests/ModelDeserializationTests.cs @@ -337,5 +337,81 @@ public void DeserializeServiceIdentification_ValidXmlGiven_ShouldPass() serviceIdentification.AccessConstraints.Should().Be("NONE"); } + [Fact] + public void DeserializeServiceProvider_ValidXmlGiven_ShouldPass() + { + const string xml = @" + CompanyName + + + + "; + + var serviceProvider = _serializer.Deserialize(xml); + serviceProvider.ProviderName.Should().Be("CompanyName"); + serviceProvider.ProviderSite.HyperlinkReference.Should().Be("http://sd.test.tdl"); + serviceProvider.ServiceContact.Should().NotBeNull(); + } + + [Fact] + public void DeserializeServiceContact_ValidXmlGiven_ShouldPass() + { + const string xml = @" + Test name + Test position + + Test role + "; + + var serviceContact = _serializer.Deserialize(xml); + serviceContact.IndividualName.Should().Be("Test name"); + serviceContact.PositionName.Should().Be("Test position"); + serviceContact.ContactInfo.Should().NotBeNull(); + serviceContact.Role.Should().Be("Test role"); + } + + [Fact] + public void DeserializeContactInfo_ValidXmlGiven_ShouldPass() + { + const string xml = @" + + test1 + test2 + + + 10am-8pm + None + "; + var contactInfo = _serializer.Deserialize(xml); + contactInfo.Address.Should().NotBeNull(); + contactInfo.ContactInstructions.Should().Be("None"); + contactInfo.Phone.Should().BeEquivalentTo("test1", "test2"); + contactInfo.HoursOfService.Should().Be("10am-8pm"); + } + + [Fact] + public void DeserializeAddress_ValidXmlGiven_ShouldPass() + { + const string xml = @" + Point 1 + Point 2 + Mars + None + NoZip + No country + test1@example.com + test2@example.com + test3@example.com + "; + + var address = _serializer.Deserialize
(xml); + address.AdministrativeArea.Should().Be("None"); + address.City.Should().Be("Mars"); + address.DeliveryPoints.Should().BeEquivalentTo("Point 1", "Point 2"); + address.Emails.Should().BeEquivalentTo("test1@example.com", "test2@example.com", "test3@example.com"); + address.PostalCode.Should().Be("NoZip"); + address.Country.Should().Be("No country"); + } + } } diff --git a/src/Wps/Wps.Client/Models/Address.cs b/src/Wps/Wps.Client/Models/Address.cs new file mode 100644 index 0000000..edfb31b --- /dev/null +++ b/src/Wps/Wps.Client/Models/Address.cs @@ -0,0 +1,47 @@ +using System.Xml.Serialization; +using Wps.Client.Utils; + +namespace Wps.Client.Models +{ + [XmlRoot("Address", Namespace = ModelNamespaces.Ows)] + public class Address + { + + /// + /// Address line for the location. + /// + [XmlElement("DeliveryPoint", Namespace = ModelNamespaces.Ows)] + public string[] DeliveryPoints { get; set; } + + /// + /// City of location + /// + [XmlElement("City", Namespace = ModelNamespaces.Ows)] + public string City { get; set; } + + /// + /// State or province of the location. + /// + [XmlElement("AdministrativeArea", Namespace = ModelNamespaces.Ows)] + public string AdministrativeArea { get; set; } + + /// + /// ZIP or other postal code. + /// + [XmlElement("PostalCode", Namespace = ModelNamespaces.Ows)] + public string PostalCode { get; set; } + + /// + /// Country of the physical address. + /// + [XmlElement("Country", Namespace = ModelNamespaces.Ows)] + public string Country { get; set; } + + /// + /// Address of the electronic mailbox of the responsible organization or individual. + /// + [XmlElement("ElectronicMailAddress", Namespace = ModelNamespaces.Ows)] + public string[] Emails { get; set; } + + } +} diff --git a/src/Wps/Wps.Client/Models/ContactInfo.cs b/src/Wps/Wps.Client/Models/ContactInfo.cs new file mode 100644 index 0000000..d822cf6 --- /dev/null +++ b/src/Wps/Wps.Client/Models/ContactInfo.cs @@ -0,0 +1,40 @@ +using System.Xml.Serialization; +using Wps.Client.Utils; + +namespace Wps.Client.Models +{ + /// + /// Address of the responsible party. + /// + [XmlRoot("ContactInfo", Namespace = ModelNamespaces.Ows)] + public class ContactInfo + { + + /// + /// Telephone numbers at which the organization or individual may be contacted. + /// + // TODO: Add 'Facsimile' numbers deserialization + [XmlArray("Phone", Namespace = ModelNamespaces.Ows)] + [XmlArrayItem("Voice", Namespace = ModelNamespaces.Ows)] + public string[] Phone { get; set; } + + /// + /// Physical and email address at which the organization or individual may be contacted. + /// + [XmlElement("Address", Namespace = ModelNamespaces.Ows, Type = typeof(Address))] + public Address Address { get; set; } + + /// + /// Time period when individuals can contact the organization or individual. + /// + [XmlElement("HoursOfService", Namespace = ModelNamespaces.Ows)] + public string HoursOfService { get; set; } + + /// + /// Supplemental instructions on how or when to contact the individual or organization. + /// + [XmlElement("ContactInstructions", Namespace = ModelNamespaces.Ows)] + public string ContactInstructions { get; set; } + + } +} diff --git a/src/Wps/Wps.Client/Models/ProviderSite.cs b/src/Wps/Wps.Client/Models/ProviderSite.cs new file mode 100644 index 0000000..5481bd7 --- /dev/null +++ b/src/Wps/Wps.Client/Models/ProviderSite.cs @@ -0,0 +1,17 @@ +using System.Xml.Serialization; +using Wps.Client.Utils; + +namespace Wps.Client.Models +{ + [XmlRoot("ProviderSite", Namespace = ModelNamespaces.Ows)] + public class ProviderSite + { + + /// + /// href of the provider website. + /// + [XmlAttribute("href", Namespace = ModelNamespaces.Xlink)] + public string HyperlinkReference { get; set; } + + } +} diff --git a/src/Wps/Wps.Client/Models/ServiceContact.cs b/src/Wps/Wps.Client/Models/ServiceContact.cs new file mode 100644 index 0000000..8510783 --- /dev/null +++ b/src/Wps/Wps.Client/Models/ServiceContact.cs @@ -0,0 +1,38 @@ +using System.Xml.Serialization; +using Wps.Client.Utils; + +namespace Wps.Client.Models +{ + /// + /// Information for contacting the service provider. + /// + [XmlRoot("ServiceContact", Namespace = ModelNamespaces.Ows)] + public class ServiceContact + { + + /// + /// Name of the responsible person. + /// + [XmlElement("IndividualName", Namespace = ModelNamespaces.Ows)] + public string IndividualName { get; set; } + + /// + /// Role or position of the responsible person. + /// + [XmlElement("PositionName", Namespace = ModelNamespaces.Ows)] + public string PositionName { get; set; } + + /// + /// Address of the responsible party. + /// + [XmlElement("ContactInfo", Namespace = ModelNamespaces.Ows)] + public ContactInfo ContactInfo { get; set; } + + /// + /// Function performed by the responsible party. + /// + [XmlElement("Role", Namespace = ModelNamespaces.Ows)] + public string Role { get; set; } + + } +} diff --git a/src/Wps/Wps.Client/Models/ServiceProvider.cs b/src/Wps/Wps.Client/Models/ServiceProvider.cs new file mode 100644 index 0000000..f795d33 --- /dev/null +++ b/src/Wps/Wps.Client/Models/ServiceProvider.cs @@ -0,0 +1,29 @@ +using System.Xml.Serialization; +using Wps.Client.Utils; + +namespace Wps.Client.Models +{ + [XmlRoot("ServiceProvider", Namespace = ModelNamespaces.Ows)] + public class ServiceProvider + { + + /// + /// Metadata about the organization that provides this specific service instance or server. + /// + [XmlElement("ProviderName", Namespace = ModelNamespaces.Ows)] + public string ProviderName { get; set; } + + /// + /// Reference the most relevant web site of the service provider. + /// + [XmlElement("ProviderSite", Namespace = ModelNamespaces.Ows)] + public ProviderSite ProviderSite { get; set; } + + /// + /// Information for contacting the service provider. + /// + [XmlElement("ServiceContact", Namespace = ModelNamespaces.Ows)] + public ServiceContact ServiceContact { get; set; } + + } +} diff --git a/src/Wps/Wps.Client/Utils/ModelNamespaces.cs b/src/Wps/Wps.Client/Utils/ModelNamespaces.cs index 3cfef81..edbc37c 100644 --- a/src/Wps/Wps.Client/Utils/ModelNamespaces.cs +++ b/src/Wps/Wps.Client/Utils/ModelNamespaces.cs @@ -5,6 +5,7 @@ public static class ModelNamespaces public const string Wps = "http://www.opengis.net/wps/2.0"; public const string Ows = "http://www.opengis.net/ows/2.0"; + public const string Xlink = "http://www.w3.org/1999/xlink"; } } From 9293f72cb1c3cefafcfd9db8c1b88827500bba0e Mon Sep 17 00:00:00 2001 From: Mihai Stan Date: Wed, 29 May 2019 18:54:46 +0200 Subject: [PATCH 24/76] Add OperationsMetadata model with submodels and deserialization tests --- .../ModelDeserializationTests.cs | 93 +++++++++++++++++++ .../Models/DistributedComputingPlatform.cs | 17 ++++ src/Wps/Wps.Client/Models/Operation.cs | 40 ++++++++ .../Wps.Client/Models/OperationConstraint.cs | 26 ++++++ .../Wps.Client/Models/OperationParameter.cs | 19 ++++ .../Wps.Client/Models/OperationsMetadata.cs | 34 +++++++ .../Models/PlatformConnectionPoint.cs | 33 +++++++ 7 files changed, 262 insertions(+) create mode 100644 src/Wps/Wps.Client/Models/DistributedComputingPlatform.cs create mode 100644 src/Wps/Wps.Client/Models/Operation.cs create mode 100644 src/Wps/Wps.Client/Models/OperationConstraint.cs create mode 100644 src/Wps/Wps.Client/Models/OperationParameter.cs create mode 100644 src/Wps/Wps.Client/Models/OperationsMetadata.cs create mode 100644 src/Wps/Wps.Client/Models/PlatformConnectionPoint.cs diff --git a/src/Wps/Wps.Client.Tests/ModelDeserializationTests.cs b/src/Wps/Wps.Client.Tests/ModelDeserializationTests.cs index ed50b11..6de0d2b 100644 --- a/src/Wps/Wps.Client.Tests/ModelDeserializationTests.cs +++ b/src/Wps/Wps.Client.Tests/ModelDeserializationTests.cs @@ -413,5 +413,98 @@ public void DeserializeAddress_ValidXmlGiven_ShouldPass() address.Country.Should().Be("No country"); } + [Fact] + public void DeserializeOperationParameter_ValidXmlGiven_ShouldPass() + { + var xml = @" + Value 1 + Value 2 + "; + + var operationParameter = _serializer.Deserialize(xml); + operationParameter.Name.Should().Be("test parameter"); + operationParameter.Values.Should().BeEquivalentTo("Value 1", "Value 2"); + } + + [Fact] + public void DeserializeOperationConstraint_ValidXmlGiven_ShouldPass() + { + var xml = @" + Value 1 + Value 2 + "; + + var operationParameter = _serializer.Deserialize(xml); + operationParameter.Name.Should().Be("test constraint"); + operationParameter.Values.Should().BeEquivalentTo("Value 1", "Value 2"); + } + + [Fact] + public void DeserializeOperationsMetadata_ValidXmlGiven_ShouldPass() + { + var xml = @" + + + + + + + + + + "; + + var opMetadata = _serializer.Deserialize(xml); + opMetadata.Constraints.Length.Should().Be(2); + opMetadata.Operations.Length.Should().Be(4); + opMetadata.Parameters.Length.Should().Be(3); + } + + [Fact] + public void DeserializeOperation_ValidXmlGiven_ShouldPass() + { + const string xml = @" + + + + + + + + + + "; + + var operation = _serializer.Deserialize(xml); + operation.Name.Should().Be("test operation"); + operation.DistributedComputingPlatforms.Length.Should().Be(4); + operation.Constraints.Length.Should().Be(2); + operation.Parameters.Length.Should().Be(3); + } + + [Fact] + public void DeserializeDistributedComputingPlatform_ValidXmlGiven_ShouldPass() + { + var xml = @" + + "; + + var dcp = _serializer.Deserialize(xml); + dcp.HttpConnectionPoint.Should().NotBeNull(); + } + + [Fact] + public void DeserializePlatformConnectionPoint_ValidXmlGiven_ShouldPass() + { + const string xml = @" + + + "; + + var pcp = _serializer.Deserialize(xml); + pcp.Get.Hyperlink.Should().Be("test hyperlink get"); + pcp.Post.Hyperlink.Should().Be("test hyperlink post"); + } + } } diff --git a/src/Wps/Wps.Client/Models/DistributedComputingPlatform.cs b/src/Wps/Wps.Client/Models/DistributedComputingPlatform.cs new file mode 100644 index 0000000..0ed7950 --- /dev/null +++ b/src/Wps/Wps.Client/Models/DistributedComputingPlatform.cs @@ -0,0 +1,17 @@ +using System.Xml.Serialization; +using Wps.Client.Utils; + +namespace Wps.Client.Models +{ + [XmlRoot("DCP", Namespace = ModelNamespaces.Ows)] + public class DistributedComputingPlatform + { + + /// + /// Connection point URLs for the HTTP DCP. + /// + [XmlElement("HTTP", Namespace = ModelNamespaces.Ows)] + public PlatformConnectionPoint HttpConnectionPoint { get; set; } + + } +} diff --git a/src/Wps/Wps.Client/Models/Operation.cs b/src/Wps/Wps.Client/Models/Operation.cs new file mode 100644 index 0000000..e3a4f05 --- /dev/null +++ b/src/Wps/Wps.Client/Models/Operation.cs @@ -0,0 +1,40 @@ +using System.Xml.Serialization; +using Wps.Client.Utils; + +namespace Wps.Client.Models +{ + /// + /// + /// + [XmlRoot("Operation", Namespace = ModelNamespaces.Ows)] + public class Operation + { + + /// + /// Name or identifier of this operation. + /// + [XmlAttribute("name", Namespace = ModelNamespaces.Ows)] + public string Name { get; set; } + + /// + /// Unordered list of Distributed Computing Platforms supported for this operation. + /// + [XmlElement("DCP", Namespace = ModelNamespaces.Ows)] + public DistributedComputingPlatform[] DistributedComputingPlatforms { get; set; } + + /// + /// Unordered list of parameter valid domains that each apply to one or more operations which this server interface implements. + /// + [XmlElement("Parameter", Namespace = ModelNamespaces.Ows)] + public OperationParameter[] Parameters { get; set; } + + /// + /// Unordered list of valid domain constraints on non-parameter quantities that each apply to this server. + /// + [XmlElement("Constraint", Namespace = ModelNamespaces.Ows)] + public OperationConstraint[] Constraints { get; set; } + + // TODO: Add ExtendedCapabilities? Vendor specific. To be seen. + + } +} diff --git a/src/Wps/Wps.Client/Models/OperationConstraint.cs b/src/Wps/Wps.Client/Models/OperationConstraint.cs new file mode 100644 index 0000000..aec9e88 --- /dev/null +++ b/src/Wps/Wps.Client/Models/OperationConstraint.cs @@ -0,0 +1,26 @@ +using System.Xml; +using System.Xml.Serialization; +using Wps.Client.Utils; + +namespace Wps.Client.Models +{ + [XmlRoot("Constraint", Namespace = ModelNamespaces.Ows)] + public class OperationConstraint + { + + /// + /// Name or identifier of this parameter or other quantity. + /// + [XmlAttribute("name", Namespace = ModelNamespaces.Ows)] + public string Name { get; set; } + + /// + /// Unordered list of all the valid values for this parameter or other quantity. + /// + [XmlElement("Value", Namespace = ModelNamespaces.Ows)] + public string[] Values { get; set; } + + // TODO: Add list of Metadata + + } +} diff --git a/src/Wps/Wps.Client/Models/OperationParameter.cs b/src/Wps/Wps.Client/Models/OperationParameter.cs new file mode 100644 index 0000000..ee5ffc8 --- /dev/null +++ b/src/Wps/Wps.Client/Models/OperationParameter.cs @@ -0,0 +1,19 @@ +using System.Xml.Serialization; +using Wps.Client.Utils; + +namespace Wps.Client.Models +{ + [XmlRoot("Parameter", Namespace = ModelNamespaces.Ows)] + public class OperationParameter + { + + [XmlAttribute("name", Namespace = ModelNamespaces.Ows)] + public string Name { get; set; } + + [XmlElement("Value", Namespace = ModelNamespaces.Ows)] + public string[] Values { get; set; } + + // TODO: Add list of Metadata + + } +} diff --git a/src/Wps/Wps.Client/Models/OperationsMetadata.cs b/src/Wps/Wps.Client/Models/OperationsMetadata.cs new file mode 100644 index 0000000..c452a33 --- /dev/null +++ b/src/Wps/Wps.Client/Models/OperationsMetadata.cs @@ -0,0 +1,34 @@ +using System.Xml.Serialization; +using Wps.Client.Utils; + +namespace Wps.Client.Models +{ + /// + /// Metadata about the operations and related abilities specified by this service and implemented by this server. + /// + [XmlRoot("OperationsMetadata", Namespace = ModelNamespaces.Ows)] + public class OperationsMetadata + { + + /// + /// Metadata for unordered list of all the operations that this server interface implements. + /// + [XmlElement("Operation", Namespace = ModelNamespaces.Ows)] + public Operation[] Operations { get; set; } + + /// + /// Optional unordered list of parameter valid domains that each apply to one or more operations which this server interface implements. + /// + [XmlElement("Parameter", Namespace = ModelNamespaces.Ows)] + public OperationParameter[] Parameters { get; set; } + + /// + /// Optional unordered list of valid domain constraints on non-parameter quantities that each apply to this server. + /// + [XmlElement("Constraint", Namespace = ModelNamespaces.Ows)] + public OperationConstraint[] Constraints { get; set; } + + // TODO: Add list of Metadata + + } +} diff --git a/src/Wps/Wps.Client/Models/PlatformConnectionPoint.cs b/src/Wps/Wps.Client/Models/PlatformConnectionPoint.cs new file mode 100644 index 0000000..c725e27 --- /dev/null +++ b/src/Wps/Wps.Client/Models/PlatformConnectionPoint.cs @@ -0,0 +1,33 @@ +using System.Xml.Serialization; +using Wps.Client.Utils; + +namespace Wps.Client.Models +{ + /// + /// Connection point URLs for the HTTP DCP. + /// + [XmlRoot("HTTP", Namespace = ModelNamespaces.Ows)] + public class PlatformConnectionPoint + { + + /// + /// Connection point URL prefix and any constraints for the HTTP "Get" request method for this operation request. + /// + [XmlElement("Get", Namespace = ModelNamespaces.Ows)] + public ConnectionPoint Get { get; set; } + + /// + /// Connection point URL and any constraints ofr the HTTP "Post" request method for this operation request. + /// + [XmlElement("Post", Namespace = ModelNamespaces.Ows)] + public ConnectionPoint Post { get; set; } + + } + + public class ConnectionPoint + { + [XmlAttribute("href", Namespace = ModelNamespaces.Xlink)] + public string Hyperlink { get; set; } + } + +} \ No newline at end of file From 7954835b59e00474b170f15af1ba18fab3d75f61 Mon Sep 17 00:00:00 2001 From: Mihai Stan Date: Fri, 31 May 2019 15:09:01 +0200 Subject: [PATCH 25/76] Add GetCapabilities response model with deserialization tests --- .../ModelDeserializationTests.cs | 29 ++++++++++ .../Responses/GetCapabilitiesResponse.cs | 58 +++++++++++++++++++ 2 files changed, 87 insertions(+) create mode 100644 src/Wps/Wps.Client/Models/Responses/GetCapabilitiesResponse.cs diff --git a/src/Wps/Wps.Client.Tests/ModelDeserializationTests.cs b/src/Wps/Wps.Client.Tests/ModelDeserializationTests.cs index 6de0d2b..162908e 100644 --- a/src/Wps/Wps.Client.Tests/ModelDeserializationTests.cs +++ b/src/Wps/Wps.Client.Tests/ModelDeserializationTests.cs @@ -2,6 +2,7 @@ using System.IO; using Wps.Client.Models; using Wps.Client.Models.Data; +using Wps.Client.Models.Responses; using Wps.Client.Services; using Xunit; @@ -506,5 +507,33 @@ public void DeserializePlatformConnectionPoint_ValidXmlGiven_ShouldPass() pcp.Post.Hyperlink.Should().Be("test hyperlink post"); } + [Fact] + public void DeserializeGetCapabilitiesResponse_ValidXmlGiven_ShouldPass() + { + const string xml = @" + + + + test + + + + + + + + + "; + + var response = _serializer.Deserialize(xml); + response.ServiceIdentification.Should().NotBeNull(); + response.ServiceProvider.Should().NotBeNull(); + response.OperationsMetadata.Should().NotBeNull(); + response.Languages.Should().NotBeEmpty(); + response.Service.Should().Be("WPS"); + response.Version.Should().Be("2.0.0"); + response.ProcessSummaries.Should().HaveCount(2); + } + } } diff --git a/src/Wps/Wps.Client/Models/Responses/GetCapabilitiesResponse.cs b/src/Wps/Wps.Client/Models/Responses/GetCapabilitiesResponse.cs new file mode 100644 index 0000000..bb28ecf --- /dev/null +++ b/src/Wps/Wps.Client/Models/Responses/GetCapabilitiesResponse.cs @@ -0,0 +1,58 @@ +using System.Xml.Serialization; +using Wps.Client.Utils; + +namespace Wps.Client.Models.Responses +{ + /// + /// The response received from the WPS server. + /// + [XmlRoot("Capabilities", Namespace = ModelNamespaces.Wps)] + public class GetCapabilitiesResponse + { + + /// + /// The type of service offered by the server. + /// + [XmlAttribute("service", Namespace = ModelNamespaces.Wps)] + public string Service { get; set; } + + /// + /// The version of the service. + /// + [XmlAttribute("version", Namespace = ModelNamespaces.Wps)] + public string Version { get; set; } + + /// + /// General metadata for a specific server. + /// + [XmlElement("ServiceIdentification", Namespace = ModelNamespaces.Ows)] + public ServiceIdentification ServiceIdentification { get; set; } + + /// + /// Information about the service provider. + /// + [XmlElement("ServiceProvider", Namespace = ModelNamespaces.Ows)] + public ServiceProvider ServiceProvider { get; set; } + + /// + /// Metadata about the operations and related abilities specified by this service and implemented by this server. + /// + [XmlElement("OperationsMetadata", Namespace = ModelNamespaces.Ows)] + public OperationsMetadata OperationsMetadata { get; set; } + + /// + /// The languages supported by this server. + /// + [XmlArray("Languages", Namespace = ModelNamespaces.Ows)] + [XmlArrayItem("Language", Namespace = ModelNamespaces.Ows, Type = typeof(string))] + public string[] Languages { get; set; } + + /// + /// The summary of the offered processes by the server. + /// + [XmlArray("Contents", Namespace = ModelNamespaces.Wps)] + [XmlArrayItem("ProcessSummary", Namespace = ModelNamespaces.Wps)] + public ProcessSummary[] ProcessSummaries { get; set; } + + } +} From 22360b7e3849948e24fa3efdc96f1c4fae176e8d Mon Sep 17 00:00:00 2001 From: Mihai Stan Date: Fri, 31 May 2019 15:14:31 +0200 Subject: [PATCH 26/76] Add GetCapabilities request with serialization test --- .../ModelSerializationTests.cs | 38 +++++++++++++++++++ .../Models/Requests/GetCapabilitiesRequest.cs | 17 +++++++++ 2 files changed, 55 insertions(+) create mode 100644 src/Wps/Wps.Client.Tests/ModelSerializationTests.cs create mode 100644 src/Wps/Wps.Client/Models/Requests/GetCapabilitiesRequest.cs diff --git a/src/Wps/Wps.Client.Tests/ModelSerializationTests.cs b/src/Wps/Wps.Client.Tests/ModelSerializationTests.cs new file mode 100644 index 0000000..fa64017 --- /dev/null +++ b/src/Wps/Wps.Client.Tests/ModelSerializationTests.cs @@ -0,0 +1,38 @@ +using FluentAssertions; +using System.Text.RegularExpressions; +using Wps.Client.Models.Requests; +using Wps.Client.Services; +using Xunit; + +namespace Wps.Client.Tests +{ + public class ModelSerializationTests : IClassFixture + { + + private readonly XmlSerializationService _serializer; + + public ModelSerializationTests(XmlSerializationService serializer) + { + _serializer = serializer; + } + + [Fact] + public void SerializeGetCapabilitiesRequest_ValidRequestGiven_ShouldPass() + { + const string expectedXml = @""; + + // Remove white spaces and new line characters. They do not change the actual (de)serialization of the XML. + var trimmedExpectedXml = Regex.Replace(expectedXml, @"\s+", string.Empty); + + var request = new GetCapabilitiesRequest() + { + Service = "WPS" + }; + + var resultXml = _serializer.Serialize(request); + var trimmedResult = Regex.Replace(resultXml, @"\s+", string.Empty); + trimmedResult.Should().Be(trimmedExpectedXml); + } + + } +} diff --git a/src/Wps/Wps.Client/Models/Requests/GetCapabilitiesRequest.cs b/src/Wps/Wps.Client/Models/Requests/GetCapabilitiesRequest.cs new file mode 100644 index 0000000..21531e5 --- /dev/null +++ b/src/Wps/Wps.Client/Models/Requests/GetCapabilitiesRequest.cs @@ -0,0 +1,17 @@ +using System; +using System.Xml.Serialization; +using Wps.Client.Utils; + +namespace Wps.Client.Models.Requests +{ + [Serializable] + [XmlRoot("GetCapabilities", Namespace = ModelNamespaces.Wps)] + public class GetCapabilitiesRequest + { + /// + /// The concerned service by the GetCapabilities request. + /// + [XmlAttribute("service", Namespace = ModelNamespaces.Wps)] + public string Service { get; set; } = "WPS"; + } +} \ No newline at end of file From ce390fdd563f243b7a7fd9ef8fd9d2a4dee464b9 Mon Sep 17 00:00:00 2001 From: Mihai Stan Date: Fri, 31 May 2019 16:12:52 +0200 Subject: [PATCH 27/76] Add XmlScehamInstance XML namespace --- src/Wps/Wps.Client/Utils/ModelNamespaces.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Wps/Wps.Client/Utils/ModelNamespaces.cs b/src/Wps/Wps.Client/Utils/ModelNamespaces.cs index edbc37c..29cbaf3 100644 --- a/src/Wps/Wps.Client/Utils/ModelNamespaces.cs +++ b/src/Wps/Wps.Client/Utils/ModelNamespaces.cs @@ -6,6 +6,7 @@ public static class ModelNamespaces public const string Wps = "http://www.opengis.net/wps/2.0"; public const string Ows = "http://www.opengis.net/ows/2.0"; public const string Xlink = "http://www.w3.org/1999/xlink"; + public const string XmlSchemaInstance = "http://www.w3.org/2001/XMLSchema-instance"; } } From 725e145a8adedea95261ed19942caa6d6209b762 Mon Sep 17 00:00:00 2001 From: Mihai Stan Date: Fri, 31 May 2019 16:13:08 +0200 Subject: [PATCH 28/76] Add explicit schema names in the XML serialization service --- src/Wps/Wps.Client/Services/XmlSerializationService.cs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/Wps/Wps.Client/Services/XmlSerializationService.cs b/src/Wps/Wps.Client/Services/XmlSerializationService.cs index fcae1de..0b2965c 100644 --- a/src/Wps/Wps.Client/Services/XmlSerializationService.cs +++ b/src/Wps/Wps.Client/Services/XmlSerializationService.cs @@ -1,6 +1,7 @@ using System.IO; using System.Text; using System.Xml.Serialization; +using Wps.Client.Utils; namespace Wps.Client.Services { @@ -9,9 +10,14 @@ public class XmlSerializationService : IXmlSerializer public string Serialize(T obj) { var serializer = new XmlSerializer(typeof(T)); + var namespaces = new XmlSerializerNamespaces(); + namespaces.Add("wps", ModelNamespaces.Wps); + namespaces.Add("ows", ModelNamespaces.Ows); + namespaces.Add("xli", ModelNamespaces.Xlink); + namespaces.Add("xsi", ModelNamespaces.XmlSchemaInstance); using (var writer = new CustomEncodingStringWriter(Encoding.UTF8)) { - serializer.Serialize(writer, obj); + serializer.Serialize(writer, obj, namespaces); return writer.ToString(); } } From 8dc2947e688bd7397911557ed1df836452749908 Mon Sep 17 00:00:00 2001 From: Mihai Stan Date: Fri, 31 May 2019 16:13:32 +0200 Subject: [PATCH 29/76] Add DescribeProcess request with serialization test --- .../ModelSerializationTests.cs | 26 ++++++++++++++++++- .../Models/Requests/DescribeProcessRequest.cs | 26 +++++++++++++++++++ .../Wps.Client/Models/Requests/RequestBase.cs | 21 +++++++++++++++ 3 files changed, 72 insertions(+), 1 deletion(-) create mode 100644 src/Wps/Wps.Client/Models/Requests/DescribeProcessRequest.cs create mode 100644 src/Wps/Wps.Client/Models/Requests/RequestBase.cs diff --git a/src/Wps/Wps.Client.Tests/ModelSerializationTests.cs b/src/Wps/Wps.Client.Tests/ModelSerializationTests.cs index fa64017..f99db01 100644 --- a/src/Wps/Wps.Client.Tests/ModelSerializationTests.cs +++ b/src/Wps/Wps.Client.Tests/ModelSerializationTests.cs @@ -34,5 +34,29 @@ public void SerializeGetCapabilitiesRequest_ValidRequestGiven_ShouldPass() trimmedResult.Should().Be(trimmedExpectedXml); } + [Fact] + public void SerializeDescribeProcessRequest_ValidRequestGiven_ShouldPass() + { + const string expectedXml = @" + + id1 + id2 + id3 + "; + + // Remove white spaces and new line characters. They do not change the actual (de)serialization of the XML. + var trimmedExpectedXml = Regex.Replace(expectedXml, @"\s+", string.Empty); + + var request = new DescribeProcessRequest() + { + Identifiers = new[] {"id1", "id2", "id3"}, + Language = "en-US", + }; + + var resultXml = _serializer.Serialize(request); + var trimmedResult = Regex.Replace(resultXml, @"\s+", string.Empty); + trimmedResult.Should().Be(trimmedExpectedXml); + } + } -} +} \ No newline at end of file diff --git a/src/Wps/Wps.Client/Models/Requests/DescribeProcessRequest.cs b/src/Wps/Wps.Client/Models/Requests/DescribeProcessRequest.cs new file mode 100644 index 0000000..9b422d1 --- /dev/null +++ b/src/Wps/Wps.Client/Models/Requests/DescribeProcessRequest.cs @@ -0,0 +1,26 @@ +using System.Xml.Serialization; +using Wps.Client.Utils; + +namespace Wps.Client.Models.Requests +{ + /// + /// Request used to describe a process. + /// + [XmlRoot("DescribeProcess", Namespace = ModelNamespaces.Wps)] + public class DescribeProcessRequest : RequestBase + { + + /// + /// The identifier of the process to be described. + /// + [XmlElement("Identifier", Namespace = ModelNamespaces.Ows)] + public string[] Identifiers { get; set; } + + /// + /// The language that should be used in the process description. + /// + [XmlAttribute("lang", Namespace = ModelNamespaces.Wps)] + public string Language { get; set; } + + } +} diff --git a/src/Wps/Wps.Client/Models/Requests/RequestBase.cs b/src/Wps/Wps.Client/Models/Requests/RequestBase.cs new file mode 100644 index 0000000..bfffb04 --- /dev/null +++ b/src/Wps/Wps.Client/Models/Requests/RequestBase.cs @@ -0,0 +1,21 @@ +using System.Xml.Serialization; + +namespace Wps.Client.Models.Requests +{ + public abstract class RequestBase + { + + /// + /// The type of the required service. + /// + [XmlAttribute("service")] + public string Service { get; set; } = "WPS"; + + /// + /// The version of the required service. + /// + [XmlAttribute("version")] + public string Version { get; set; } = "2.0.0"; + + } +} From 4636c58c98a2ca62a6fc21cf06142af114862890 Mon Sep 17 00:00:00 2001 From: Mihai Stan Date: Tue, 4 Jun 2019 16:43:56 +0200 Subject: [PATCH 30/76] Add StatusInfo model with deserialization test --- .../ModelDeserializationTests.cs | 25 +++++++++ src/Wps/Wps.Client/Models/StatusInfo.cs | 51 +++++++++++++++++++ 2 files changed, 76 insertions(+) create mode 100644 src/Wps/Wps.Client/Models/StatusInfo.cs diff --git a/src/Wps/Wps.Client.Tests/ModelDeserializationTests.cs b/src/Wps/Wps.Client.Tests/ModelDeserializationTests.cs index 162908e..3ce24bf 100644 --- a/src/Wps/Wps.Client.Tests/ModelDeserializationTests.cs +++ b/src/Wps/Wps.Client.Tests/ModelDeserializationTests.cs @@ -1,10 +1,12 @@ using FluentAssertions; +using System; using System.IO; using Wps.Client.Models; using Wps.Client.Models.Data; using Wps.Client.Models.Responses; using Wps.Client.Services; using Xunit; +using Process = Wps.Client.Models.Process; namespace Wps.Client.Tests { @@ -535,5 +537,28 @@ public void DeserializeGetCapabilitiesResponse_ValidXmlGiven_ShouldPass() response.ProcessSummaries.Should().HaveCount(2); } + [Fact] + public void DeserializeStatusInfo_ValidXmlGiven_ShouldPass() + { + const string xml = @" + + test-id + test-status + 2019-05-20T20:20:20Z + 2019-05-20T20:20:20Z + 2019-05-20T20:20:20Z + 45 +"; + var expectedDateTime = new DateTime(2019, 5, 20, 20, 20, 20); + + var response = _serializer.Deserialize(xml); + response.Status.Should().Be("test-status"); + response.JobId.Should().Be("test-id"); + response.EstimatedCompletion.Should().Be(expectedDateTime); + response.NextPollDateTime.Should().Be(expectedDateTime); + response.ExpirationDate.Should().Be(expectedDateTime); + response.CompletionRate.Should().Be(45); + } + } } diff --git a/src/Wps/Wps.Client/Models/StatusInfo.cs b/src/Wps/Wps.Client/Models/StatusInfo.cs new file mode 100644 index 0000000..b315243 --- /dev/null +++ b/src/Wps/Wps.Client/Models/StatusInfo.cs @@ -0,0 +1,51 @@ +using System; +using System.Xml.Serialization; +using Wps.Client.Utils; + +namespace Wps.Client.Models +{ + /// + /// Object used to provide identification and status information about a running job. + /// + [XmlRoot("StatusInfo", Namespace = ModelNamespaces.Wps)] + public class StatusInfo + { + + /// + /// Unambiguously identifier of a job within a WPS instance. + /// + [XmlElement("JobID", Namespace = ModelNamespaces.Wps)] + public string JobId { get; set; } + + /// + /// Well-known identifier describing the status of the job. + /// + [XmlElement("Status", Namespace = ModelNamespaces.Wps)] + public string Status { get; set; } + + /// + /// Date and time by which the job and its results will be no longer accessible. + /// + [XmlElement("ExpirationDate", Namespace = ModelNamespaces.Wps, Type = typeof(DateTime))] + public DateTime ExpirationDate { get; set; } + + /// + /// Date and time by which the processing job will be finished. + /// + [XmlElement("EstimatedCompletion", Namespace = ModelNamespaces.Wps, Type = typeof(DateTime))] + public DateTime EstimatedCompletion { get; set; } + + /// + /// Date and time for the next suggested status polling. + /// + [XmlElement("NextPoll", Namespace = ModelNamespaces.Wps, Type = typeof(DateTime))] + public DateTime NextPollDateTime { get; set; } + + /// + /// Percentage of process that has been completed. + /// + [XmlElement("PercentCompleted", Namespace = ModelNamespaces.Wps)] + public int CompletionRate { get; set; } + + } +} From dcf5c725ebe5236a41cf2f358144cd36cb425d5f Mon Sep 17 00:00:00 2001 From: Mihai Stan Date: Tue, 4 Jun 2019 22:03:29 +0200 Subject: [PATCH 31/76] Add GetRequest with serialization test --- .../ModelSerializationTests.cs | 24 ++++++++++++++++++- .../Models/Requests/GetStatusRequest.cs | 20 ++++++++++++++++ 2 files changed, 43 insertions(+), 1 deletion(-) create mode 100644 src/Wps/Wps.Client/Models/Requests/GetStatusRequest.cs diff --git a/src/Wps/Wps.Client.Tests/ModelSerializationTests.cs b/src/Wps/Wps.Client.Tests/ModelSerializationTests.cs index f99db01..4cb93be 100644 --- a/src/Wps/Wps.Client.Tests/ModelSerializationTests.cs +++ b/src/Wps/Wps.Client.Tests/ModelSerializationTests.cs @@ -1,5 +1,6 @@ using FluentAssertions; using System.Text.RegularExpressions; +using Wps.Client.Models; using Wps.Client.Models.Requests; using Wps.Client.Services; using Xunit; @@ -49,7 +50,7 @@ public void SerializeDescribeProcessRequest_ValidRequestGiven_ShouldPass() var request = new DescribeProcessRequest() { - Identifiers = new[] {"id1", "id2", "id3"}, + Identifiers = new[] { "id1", "id2", "id3" }, Language = "en-US", }; @@ -58,5 +59,26 @@ public void SerializeDescribeProcessRequest_ValidRequestGiven_ShouldPass() trimmedResult.Should().Be(trimmedExpectedXml); } + [Fact] + public void SerializeGetStatusRequest_ValidRequestGiven_ShouldPass() + { + const string expectedXml = @" + + testJobId + "; + + // Remove white spaces and new line characters. They do not change the actual (de)serialization of the XML. + var trimmedExpectedXml = Regex.Replace(expectedXml, @"\s+", string.Empty); + + var request = new GetStatusRequest + { + JobId = "testJobId" + }; + + var resultXml = _serializer.Serialize(request); + var trimmedResult = Regex.Replace(resultXml, @"\s+", string.Empty); + trimmedResult.Should().Be(trimmedExpectedXml); + } + } } \ No newline at end of file diff --git a/src/Wps/Wps.Client/Models/Requests/GetStatusRequest.cs b/src/Wps/Wps.Client/Models/Requests/GetStatusRequest.cs new file mode 100644 index 0000000..dae12e9 --- /dev/null +++ b/src/Wps/Wps.Client/Models/Requests/GetStatusRequest.cs @@ -0,0 +1,20 @@ +using System.Xml.Serialization; +using Wps.Client.Utils; + +namespace Wps.Client.Models.Requests +{ + /// + /// Request offering information about a job that has been executed asynchronously. + /// + [XmlRoot("GetStatus", Namespace = ModelNamespaces.Wps)] + public class GetStatusRequest : RequestBase + { + + /// + /// The ID of the job concerned by the status request. + /// + [XmlElement("JobID", Namespace = ModelNamespaces.Wps)] + public string JobId { get; set; } + + } +} From e4875e48f535a08edc3946e1e0220d5e7c04d1ed Mon Sep 17 00:00:00 2001 From: Mihai Stan Date: Tue, 4 Jun 2019 22:11:35 +0200 Subject: [PATCH 32/76] Add GetResult request with serialization test --- .../ModelSerializationTests.cs | 21 +++++++++++++++++++ .../Models/Requests/GetResultRequest.cs | 20 ++++++++++++++++++ 2 files changed, 41 insertions(+) create mode 100644 src/Wps/Wps.Client/Models/Requests/GetResultRequest.cs diff --git a/src/Wps/Wps.Client.Tests/ModelSerializationTests.cs b/src/Wps/Wps.Client.Tests/ModelSerializationTests.cs index 4cb93be..954d1e0 100644 --- a/src/Wps/Wps.Client.Tests/ModelSerializationTests.cs +++ b/src/Wps/Wps.Client.Tests/ModelSerializationTests.cs @@ -80,5 +80,26 @@ public void SerializeGetStatusRequest_ValidRequestGiven_ShouldPass() trimmedResult.Should().Be(trimmedExpectedXml); } + [Fact] + public void SerializeGetResultRequest_ValidRequestGiven_ShouldPass() + { + const string expectedXml = @" + + testJobId + "; + + // Remove white spaces and new line characters. They do not change the actual (de)serialization of the XML. + var trimmedExpectedXml = Regex.Replace(expectedXml, @"\s+", string.Empty); + + var request = new GetResultRequest() + { + JobId = "testJobId" + }; + + var resultXml = _serializer.Serialize(request); + var trimmedResult = Regex.Replace(resultXml, @"\s+", string.Empty); + trimmedResult.Should().Be(trimmedExpectedXml); + } + } } \ No newline at end of file diff --git a/src/Wps/Wps.Client/Models/Requests/GetResultRequest.cs b/src/Wps/Wps.Client/Models/Requests/GetResultRequest.cs new file mode 100644 index 0000000..72d60b6 --- /dev/null +++ b/src/Wps/Wps.Client/Models/Requests/GetResultRequest.cs @@ -0,0 +1,20 @@ +using System.Xml.Serialization; +using Wps.Client.Utils; + +namespace Wps.Client.Models.Requests +{ + /// + /// Operation allowing WPS clients to query the result of a finished processing job. + /// + [XmlRoot("GetResult", Namespace = ModelNamespaces.Wps)] + public class GetResultRequest : RequestBase + { + + /// + /// Job identifier. + /// + [XmlElement("JobID", Namespace = ModelNamespaces.Wps)] + public string JobId { get; set; } + + } +} From f74704f32ac0ac70693ca72be002c77330b9f951 Mon Sep 17 00:00:00 2001 From: Mihai Stan Date: Tue, 4 Jun 2019 22:38:45 +0200 Subject: [PATCH 33/76] Add Result model with submodels and deserialization tests --- .../ModelDeserializationTests.cs | 40 +++++++++++++++++++ src/Wps/Wps.Client/Models/Result.cs | 31 ++++++++++++++ src/Wps/Wps.Client/Models/ResultOutput.cs | 29 ++++++++++++++ 3 files changed, 100 insertions(+) create mode 100644 src/Wps/Wps.Client/Models/Result.cs create mode 100644 src/Wps/Wps.Client/Models/ResultOutput.cs diff --git a/src/Wps/Wps.Client.Tests/ModelDeserializationTests.cs b/src/Wps/Wps.Client.Tests/ModelDeserializationTests.cs index 3ce24bf..6b57843 100644 --- a/src/Wps/Wps.Client.Tests/ModelDeserializationTests.cs +++ b/src/Wps/Wps.Client.Tests/ModelDeserializationTests.cs @@ -560,5 +560,45 @@ public void DeserializeStatusInfo_ValidXmlGiven_ShouldPass() response.CompletionRate.Should().Be(45); } + [Fact] + public void DeserializeResultOutput_ValidXmlGiven_ShouldPass() + { + const string xml = @" + + + + + + "; + var expectedDateTime = new DateTime(2019, 5, 20, 20, 20, 20); + + var resultOutput = _serializer.Deserialize(xml); + resultOutput.Data.Should().NotBeNull(); + resultOutput.Id.Should().Be("result"); + resultOutput.Output.Should().NotBeNull(); + } + + [Fact] + public void DeserializeResult_ValidXmlGiven_ShouldPass() + { + const string xml = @" + + test-id + + + + + + + 2019-05-20T20:20:20Z +"; + var expectedExpirationDate = new DateTime(2019, 5, 20, 20, 20, 20); + + var result = _serializer.Deserialize(xml); + result.ExpirationDate.Should().Be(expectedExpirationDate); + result.JobId.Should().Be("test-id"); + result.Outputs.Should().HaveCount(1); + } + } } diff --git a/src/Wps/Wps.Client/Models/Result.cs b/src/Wps/Wps.Client/Models/Result.cs new file mode 100644 index 0000000..f8fa619 --- /dev/null +++ b/src/Wps/Wps.Client/Models/Result.cs @@ -0,0 +1,31 @@ +using System; +using System.Xml; +using System.Xml.Serialization; +using Wps.Client.Utils; + +namespace Wps.Client.Models +{ + [XmlRoot("Result", Namespace = ModelNamespaces.Wps)] + public class Result + { + + /// + /// Unambiguously identifier of a job within a WPS instance. + /// + [XmlElement("JobID", Namespace = ModelNamespaces.Wps)] + public string JobId { get; set; } + + /// + /// Date and time by which the results will be no longer accessible. + /// + [XmlElement("ExpirationDate", Namespace = ModelNamespaces.Wps, Type = typeof(DateTime))] + public DateTime ExpirationDate { get; set; } + + /// + /// Output item returned by a process execution. + /// + [XmlElement("Output", Namespace = ModelNamespaces.Wps)] + public ResultOutput[] Outputs { get; set; } + + } +} diff --git a/src/Wps/Wps.Client/Models/ResultOutput.cs b/src/Wps/Wps.Client/Models/ResultOutput.cs new file mode 100644 index 0000000..557bb49 --- /dev/null +++ b/src/Wps/Wps.Client/Models/ResultOutput.cs @@ -0,0 +1,29 @@ +using System.Xml.Serialization; +using Wps.Client.Utils; + +namespace Wps.Client.Models +{ + [XmlRoot("Output", Namespace = ModelNamespaces.Wps)] + public class ResultOutput + { + + /// + /// Unambiguous identifier or name of an output item. + /// + [XmlAttribute("id", Namespace = ModelNamespaces.Wps)] + public string Id { get; set; } + + /// + /// The data provided by this output item. + /// + [XmlElement("Data", Namespace = ModelNamespaces.Wps)] + public object Data { get; set; } + + /// + /// Nested output, child element. + /// + [XmlElement("Output", Namespace = ModelNamespaces.Wps)] + public ResultOutput Output { get; set; } + + } +} From 9aef6e5b500a0136132641c31f4ea7854e3615b6 Mon Sep 17 00:00:00 2001 From: Mihai Stan Date: Tue, 4 Jun 2019 22:38:59 +0200 Subject: [PATCH 34/76] Fix XML serialization service tests --- src/Wps/Wps.Client.Tests/XmlSerializationServiceTests.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Wps/Wps.Client.Tests/XmlSerializationServiceTests.cs b/src/Wps/Wps.Client.Tests/XmlSerializationServiceTests.cs index 4d9c18b..b944917 100644 --- a/src/Wps/Wps.Client.Tests/XmlSerializationServiceTests.cs +++ b/src/Wps/Wps.Client.Tests/XmlSerializationServiceTests.cs @@ -20,10 +20,10 @@ public XmlSerializationServiceTests(XmlSerializationService serializer) public void SerializeObject_ValidObjectGiven_ShouldPass() { const string expectedXml = @" - + Test 10 - "; + "; // Remove white spaces and new line characters. They do not change the actual (de)serialization of the XML. var trimmedExpectedXml = Regex.Replace(expectedXml, @"\s+", string.Empty); From f47d9a7327d9cccd3308b3356f6e521fbb43ec20 Mon Sep 17 00:00:00 2001 From: Mihai Stan Date: Wed, 5 Jun 2019 19:55:11 +0200 Subject: [PATCH 35/76] Add possibility to omit xml header on serialization --- src/Wps/Wps.Client/Services/IXmlSerializer.cs | 2 +- .../Services/XmlSerializationService.cs | 18 ++++++++++++++---- 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/src/Wps/Wps.Client/Services/IXmlSerializer.cs b/src/Wps/Wps.Client/Services/IXmlSerializer.cs index 1e8773a..d096ec9 100644 --- a/src/Wps/Wps.Client/Services/IXmlSerializer.cs +++ b/src/Wps/Wps.Client/Services/IXmlSerializer.cs @@ -3,7 +3,7 @@ public interface IXmlSerializer { - string Serialize(T obj); + string Serialize(object obj, bool omitHeaderDeclaration = false); T Deserialize(string xml); } diff --git a/src/Wps/Wps.Client/Services/XmlSerializationService.cs b/src/Wps/Wps.Client/Services/XmlSerializationService.cs index 0b2965c..fb77dde 100644 --- a/src/Wps/Wps.Client/Services/XmlSerializationService.cs +++ b/src/Wps/Wps.Client/Services/XmlSerializationService.cs @@ -1,5 +1,6 @@ using System.IO; using System.Text; +using System.Xml; using System.Xml.Serialization; using Wps.Client.Utils; @@ -7,18 +8,27 @@ namespace Wps.Client.Services { public class XmlSerializationService : IXmlSerializer { - public string Serialize(T obj) + public string Serialize(object obj, bool omitHeaderDeclaration = false) { - var serializer = new XmlSerializer(typeof(T)); + var serializer = new XmlSerializer(obj.GetType()); var namespaces = new XmlSerializerNamespaces(); namespaces.Add("wps", ModelNamespaces.Wps); namespaces.Add("ows", ModelNamespaces.Ows); namespaces.Add("xli", ModelNamespaces.Xlink); namespaces.Add("xsi", ModelNamespaces.XmlSchemaInstance); + + var settings = new XmlWriterSettings + { + OmitXmlDeclaration = omitHeaderDeclaration + }; + using (var writer = new CustomEncodingStringWriter(Encoding.UTF8)) { - serializer.Serialize(writer, obj, namespaces); - return writer.ToString(); + using (var xmlWriter = XmlWriter.Create(writer, settings)) + { + serializer.Serialize(xmlWriter, obj, namespaces); + return writer.ToString(); + } } } From 3b286ada5e9c0f58f92506129af6bf9aefb5b7bc Mon Sep 17 00:00:00 2001 From: Mihai Stan Date: Wed, 5 Jun 2019 23:15:58 +0200 Subject: [PATCH 36/76] Add Ranged value model with (de)serialization tests --- .../ModelDeserializationTests.cs | 17 +++ .../ModelSerializationTests.cs | 27 +++++ src/Wps/Wps.Client/Models/RangeClosure.cs | 12 ++ src/Wps/Wps.Client/Models/ValueRange.cs | 108 ++++++++++++++++++ 4 files changed, 164 insertions(+) create mode 100644 src/Wps/Wps.Client/Models/RangeClosure.cs create mode 100644 src/Wps/Wps.Client/Models/ValueRange.cs diff --git a/src/Wps/Wps.Client.Tests/ModelDeserializationTests.cs b/src/Wps/Wps.Client.Tests/ModelDeserializationTests.cs index 6b57843..eb71e59 100644 --- a/src/Wps/Wps.Client.Tests/ModelDeserializationTests.cs +++ b/src/Wps/Wps.Client.Tests/ModelDeserializationTests.cs @@ -600,5 +600,22 @@ public void DeserializeResult_ValidXmlGiven_ShouldPass() result.Outputs.Should().HaveCount(1); } + [Fact] + public void DeserializeValueRange_ValidXmlGiven_ShouldPass() + { + const string xml = @" + + 10 + 100 + 1000 +"; + + var result = _serializer.Deserialize(xml); + result.MinimumValue.Should().Be("10"); + result.MaximumValue.Should().Be("1000"); + result.Spacing.Should().Be("100"); + result.RangeClosure.Should().Be(RangeClosure.ClosedOpen); + } + } } diff --git a/src/Wps/Wps.Client.Tests/ModelSerializationTests.cs b/src/Wps/Wps.Client.Tests/ModelSerializationTests.cs index 954d1e0..7a9d35c 100644 --- a/src/Wps/Wps.Client.Tests/ModelSerializationTests.cs +++ b/src/Wps/Wps.Client.Tests/ModelSerializationTests.cs @@ -1,6 +1,7 @@ using FluentAssertions; using System.Text.RegularExpressions; using Wps.Client.Models; +using Wps.Client.Models.Execution; using Wps.Client.Models.Requests; using Wps.Client.Services; using Xunit; @@ -101,5 +102,31 @@ public void SerializeGetResultRequest_ValidRequestGiven_ShouldPass() trimmedResult.Should().Be(trimmedExpectedXml); } + [Fact] + public void SerializeValueRange_ValidRangeGiven_ShouldPass() + { + const string expectedXml = @" + + 10 + 100 + 10 + "; + + // Remove white spaces and new line characters. They do not change the actual (de)serialization of the XML. + var trimmedExpectedXml = Regex.Replace(expectedXml, @"\s+", string.Empty); + + var range = new ValueRange + { + MinimumValue = "10", + MaximumValue = "100", + RangeClosure = RangeClosure.ClosedOpen, + Spacing = "10" + }; + + var resultXml = _serializer.Serialize(range); + var trimmedResult = Regex.Replace(resultXml, @"\s+", string.Empty); + trimmedResult.Should().Be(trimmedExpectedXml); + } + } } \ No newline at end of file diff --git a/src/Wps/Wps.Client/Models/RangeClosure.cs b/src/Wps/Wps.Client/Models/RangeClosure.cs new file mode 100644 index 0000000..e5ae9ad --- /dev/null +++ b/src/Wps/Wps.Client/Models/RangeClosure.cs @@ -0,0 +1,12 @@ +namespace Wps.Client.Models +{ + public enum RangeClosure + { + + Closed, + Open, + OpenClosed, + ClosedOpen + + } +} diff --git a/src/Wps/Wps.Client/Models/ValueRange.cs b/src/Wps/Wps.Client/Models/ValueRange.cs new file mode 100644 index 0000000..0de48e0 --- /dev/null +++ b/src/Wps/Wps.Client/Models/ValueRange.cs @@ -0,0 +1,108 @@ +using System; +using System.Xml; +using System.Xml.Schema; +using System.Xml.Serialization; +using Wps.Client.Utils; + +namespace Wps.Client.Models +{ + [XmlRoot("Range", Namespace = ModelNamespaces.Ows)] + public class ValueRange : IXmlSerializable + { + + public RangeClosure RangeClosure { get; set; } + + public string MinimumValue { get; set; } + + public string MaximumValue { get; set; } + + public string Spacing { get; set; } + + // ------------------------------------------------------------------- + + public XmlSchema GetSchema() + { + return null; + } + + public void ReadXml(XmlReader reader) + { + RangeClosure = RangeClosureFromString(reader.GetAttribute("rangeClosure", ModelNamespaces.Ows)); + + while (reader.Read()) + { + if (reader.NodeType == XmlNodeType.Element && reader.NamespaceURI.Equals(ModelNamespaces.Ows)) + { + if (reader.LocalName.Equals("MinimumValue")) + { + MinimumValue = reader.ReadString(); + } + else if (reader.LocalName.Equals("MaximumValue")) + { + MaximumValue = reader.ReadString(); + } + else if (reader.LocalName.Equals("Spacing")) + { + Spacing = reader.ReadString(); + } + } + } + } + + public void WriteXml(XmlWriter writer) + { + writer.WriteAttributeString("ows", "rangeClosure", ModelNamespaces.Ows, RangeClosureToString(RangeClosure)); + writer.WriteElementString("ows", "MinimumValue", ModelNamespaces.Ows, MinimumValue); + writer.WriteElementString("ows", "MaximumValue", ModelNamespaces.Ows, MaximumValue); + writer.WriteElementString("ows", "Spacing", ModelNamespaces.Ows, Spacing); + } + + private RangeClosure RangeClosureFromString(string str) + { + if (str == null) + { + return RangeClosure.Closed; + } + + if (str.Equals("closed", StringComparison.InvariantCultureIgnoreCase)) + { + return RangeClosure.Closed; + } + + if (str.Equals("open", StringComparison.InvariantCultureIgnoreCase)) + { + return RangeClosure.Open; + } + + if (str.Equals("open-closed", StringComparison.InvariantCultureIgnoreCase)) + { + return RangeClosure.OpenClosed; + } + + if (str.Equals("closed-open", StringComparison.InvariantCultureIgnoreCase)) + { + return RangeClosure.ClosedOpen; + } + + return RangeClosure.Closed; // Value by default + } + + private string RangeClosureToString(RangeClosure rc) + { + switch (rc) + { + case RangeClosure.Closed: + return "closed"; + case RangeClosure.Open: + return "open"; + case RangeClosure.OpenClosed: + return "open-closed"; + case RangeClosure.ClosedOpen: + return "closed-open"; + default: + return "closed"; // Value by default + } + } + + } +} From b0d9b590cfaf0e5786453ecbf2410709a3ebb68a Mon Sep 17 00:00:00 2001 From: Mihai Stan Date: Thu, 6 Jun 2019 10:46:37 +0200 Subject: [PATCH 37/76] Use attributes to deserialize RangeClosure enumeration in ValueRange Remove the actual implementation of IXmlSerialization since it breaks the XML reader position for the parent element and use C# attributes that make the deserialization consistent with the potential classes that might contain ValueRange. --- .../ModelDeserializationTests.cs | 41 +++++++- .../ModelSerializationTests.cs | 19 ++-- src/Wps/Wps.Client/Models/RangeClosure.cs | 12 ++- src/Wps/Wps.Client/Models/ValueRange.cs | 96 ++----------------- 4 files changed, 62 insertions(+), 106 deletions(-) diff --git a/src/Wps/Wps.Client.Tests/ModelDeserializationTests.cs b/src/Wps/Wps.Client.Tests/ModelDeserializationTests.cs index eb71e59..d557a34 100644 --- a/src/Wps/Wps.Client.Tests/ModelDeserializationTests.cs +++ b/src/Wps/Wps.Client.Tests/ModelDeserializationTests.cs @@ -1,4 +1,4 @@ -using FluentAssertions; +using FluentAssertions; using System; using System.IO; using Wps.Client.Models; @@ -49,7 +49,7 @@ public void DeserializeDataType_ValidXmlGiven_ShouldPass() } [Fact] - public void DeserializeLiteralDataDomain_ValidXmlGiven_WithAllowedValues_ShouldPass() + public void DeserializeLiteralDataDomain_ValidXmlGiven_WithAllowedValues_WithValues_ShouldPass() { const string xml = @" @@ -78,6 +78,38 @@ public void DeserializeLiteralDataDomain_ValidXmlGiven_WithAllowedValues_ShouldP } } + [Fact] + public void DeserializeLiteralDataDomain_ValidXmlGiven_WithAllowedValues_WithRange_ShouldPass() + { + const string xml = @" + + + 1 + 1000 + 100 + + + + uncorrected + Meter + "; + + var domain = _serializer.Deserialize(xml); + domain.Should().NotBeNull(); + domain?.IsDefault.Should().BeTrue(); + domain?.DefaultValue.Should().Be("uncorrected"); + domain?.PossibleLiteralValues.GetType().Should().Be(typeof(AllowedValues)); + domain?.DataType?.Reference.Should().Be("string"); + domain?.UnitOfMeasure.Should().Be("Meter"); + if (domain?.PossibleLiteralValues is AllowedValues values) + { + values.Range.MinimumValue.Should().Be("1"); + values.Range.MaximumValue.Should().Be("1000"); + values.Range.Spacing.Should().Be("100"); + values.Range.RangeClosure.Should().Be(RangeClosure.Open); + } + } + [Fact] public void DeserializeLiteralDataDomain_ValidXmlGiven_WithAnyValue_ShouldPass() { @@ -600,11 +632,12 @@ public void DeserializeResult_ValidXmlGiven_ShouldPass() result.Outputs.Should().HaveCount(1); } +<<<<<<< HEAD [Fact] public void DeserializeValueRange_ValidXmlGiven_ShouldPass() { const string xml = @" - + 10 100 1000 @@ -617,5 +650,7 @@ public void DeserializeValueRange_ValidXmlGiven_ShouldPass() result.RangeClosure.Should().Be(RangeClosure.ClosedOpen); } +======= +>>>>>>> parent of 3b286ad... Add Ranged value model with (de)serialization tests } } diff --git a/src/Wps/Wps.Client.Tests/ModelSerializationTests.cs b/src/Wps/Wps.Client.Tests/ModelSerializationTests.cs index 7a9d35c..a19640d 100644 --- a/src/Wps/Wps.Client.Tests/ModelSerializationTests.cs +++ b/src/Wps/Wps.Client.Tests/ModelSerializationTests.cs @@ -1,7 +1,6 @@ -using FluentAssertions; +using FluentAssertions; using System.Text.RegularExpressions; using Wps.Client.Models; -using Wps.Client.Models.Execution; using Wps.Client.Models.Requests; using Wps.Client.Services; using Xunit; @@ -102,15 +101,17 @@ public void SerializeGetResultRequest_ValidRequestGiven_ShouldPass() trimmedResult.Should().Be(trimmedExpectedXml); } +<<<<<<< HEAD [Fact] public void SerializeValueRange_ValidRangeGiven_ShouldPass() { - const string expectedXml = @" - - 10 - 100 - 10 - "; + const string expectedXml = @" + + + 10 + 100 + 10 +"; // Remove white spaces and new line characters. They do not change the actual (de)serialization of the XML. var trimmedExpectedXml = Regex.Replace(expectedXml, @"\s+", string.Empty); @@ -128,5 +129,7 @@ public void SerializeValueRange_ValidRangeGiven_ShouldPass() trimmedResult.Should().Be(trimmedExpectedXml); } +======= +>>>>>>> parent of 3b286ad... Add Ranged value model with (de)serialization tests } } \ No newline at end of file diff --git a/src/Wps/Wps.Client/Models/RangeClosure.cs b/src/Wps/Wps.Client/Models/RangeClosure.cs index e5ae9ad..5ef62b7 100644 --- a/src/Wps/Wps.Client/Models/RangeClosure.cs +++ b/src/Wps/Wps.Client/Models/RangeClosure.cs @@ -1,12 +1,14 @@ -namespace Wps.Client.Models +using System.Xml.Serialization; + +namespace Wps.Client.Models { public enum RangeClosure { - Closed, - Open, - OpenClosed, - ClosedOpen + [XmlEnum("closed")] Closed, + [XmlEnum("open")] Open, + [XmlEnum("open-closed")] OpenClosed, + [XmlEnum("closed-open")] ClosedOpen } } diff --git a/src/Wps/Wps.Client/Models/ValueRange.cs b/src/Wps/Wps.Client/Models/ValueRange.cs index 0de48e0..b538b7c 100644 --- a/src/Wps/Wps.Client/Models/ValueRange.cs +++ b/src/Wps/Wps.Client/Models/ValueRange.cs @@ -1,108 +1,24 @@ -using System; -using System.Xml; -using System.Xml.Schema; +using System.Xml; using System.Xml.Serialization; using Wps.Client.Utils; namespace Wps.Client.Models { [XmlRoot("Range", Namespace = ModelNamespaces.Ows)] - public class ValueRange : IXmlSerializable + public class ValueRange { + [XmlAttribute("rangeClosure", Namespace = ModelNamespaces.Ows)] public RangeClosure RangeClosure { get; set; } + [XmlElement("MinimumValue", Namespace = ModelNamespaces.Ows)] public string MinimumValue { get; set; } + [XmlElement("MaximumValue", Namespace = ModelNamespaces.Ows)] public string MaximumValue { get; set; } + [XmlElement("Spacing", Namespace = ModelNamespaces.Ows)] public string Spacing { get; set; } - // ------------------------------------------------------------------- - - public XmlSchema GetSchema() - { - return null; - } - - public void ReadXml(XmlReader reader) - { - RangeClosure = RangeClosureFromString(reader.GetAttribute("rangeClosure", ModelNamespaces.Ows)); - - while (reader.Read()) - { - if (reader.NodeType == XmlNodeType.Element && reader.NamespaceURI.Equals(ModelNamespaces.Ows)) - { - if (reader.LocalName.Equals("MinimumValue")) - { - MinimumValue = reader.ReadString(); - } - else if (reader.LocalName.Equals("MaximumValue")) - { - MaximumValue = reader.ReadString(); - } - else if (reader.LocalName.Equals("Spacing")) - { - Spacing = reader.ReadString(); - } - } - } - } - - public void WriteXml(XmlWriter writer) - { - writer.WriteAttributeString("ows", "rangeClosure", ModelNamespaces.Ows, RangeClosureToString(RangeClosure)); - writer.WriteElementString("ows", "MinimumValue", ModelNamespaces.Ows, MinimumValue); - writer.WriteElementString("ows", "MaximumValue", ModelNamespaces.Ows, MaximumValue); - writer.WriteElementString("ows", "Spacing", ModelNamespaces.Ows, Spacing); - } - - private RangeClosure RangeClosureFromString(string str) - { - if (str == null) - { - return RangeClosure.Closed; - } - - if (str.Equals("closed", StringComparison.InvariantCultureIgnoreCase)) - { - return RangeClosure.Closed; - } - - if (str.Equals("open", StringComparison.InvariantCultureIgnoreCase)) - { - return RangeClosure.Open; - } - - if (str.Equals("open-closed", StringComparison.InvariantCultureIgnoreCase)) - { - return RangeClosure.OpenClosed; - } - - if (str.Equals("closed-open", StringComparison.InvariantCultureIgnoreCase)) - { - return RangeClosure.ClosedOpen; - } - - return RangeClosure.Closed; // Value by default - } - - private string RangeClosureToString(RangeClosure rc) - { - switch (rc) - { - case RangeClosure.Closed: - return "closed"; - case RangeClosure.Open: - return "open"; - case RangeClosure.OpenClosed: - return "open-closed"; - case RangeClosure.ClosedOpen: - return "closed-open"; - default: - return "closed"; // Value by default - } - } - } } From 3eefb416e4bbf6e6f8d8c30bb98d726fb011ee7e Mon Sep 17 00:00:00 2001 From: Mihai Stan Date: Thu, 6 Jun 2019 10:48:59 +0200 Subject: [PATCH 38/76] Add range to AllowedValues --- src/Wps/Wps.Client.Tests/ModelDeserializationTests.cs | 4 ---- src/Wps/Wps.Client.Tests/ModelSerializationTests.cs | 4 ---- src/Wps/Wps.Client/Models/Data/AllowedValues.cs | 3 +++ 3 files changed, 3 insertions(+), 8 deletions(-) diff --git a/src/Wps/Wps.Client.Tests/ModelDeserializationTests.cs b/src/Wps/Wps.Client.Tests/ModelDeserializationTests.cs index d557a34..7d90b92 100644 --- a/src/Wps/Wps.Client.Tests/ModelDeserializationTests.cs +++ b/src/Wps/Wps.Client.Tests/ModelDeserializationTests.cs @@ -632,7 +632,6 @@ public void DeserializeResult_ValidXmlGiven_ShouldPass() result.Outputs.Should().HaveCount(1); } -<<<<<<< HEAD [Fact] public void DeserializeValueRange_ValidXmlGiven_ShouldPass() { @@ -649,8 +648,5 @@ public void DeserializeValueRange_ValidXmlGiven_ShouldPass() result.Spacing.Should().Be("100"); result.RangeClosure.Should().Be(RangeClosure.ClosedOpen); } - -======= ->>>>>>> parent of 3b286ad... Add Ranged value model with (de)serialization tests } } diff --git a/src/Wps/Wps.Client.Tests/ModelSerializationTests.cs b/src/Wps/Wps.Client.Tests/ModelSerializationTests.cs index a19640d..715b6f9 100644 --- a/src/Wps/Wps.Client.Tests/ModelSerializationTests.cs +++ b/src/Wps/Wps.Client.Tests/ModelSerializationTests.cs @@ -101,7 +101,6 @@ public void SerializeGetResultRequest_ValidRequestGiven_ShouldPass() trimmedResult.Should().Be(trimmedExpectedXml); } -<<<<<<< HEAD [Fact] public void SerializeValueRange_ValidRangeGiven_ShouldPass() { @@ -128,8 +127,5 @@ public void SerializeValueRange_ValidRangeGiven_ShouldPass() var trimmedResult = Regex.Replace(resultXml, @"\s+", string.Empty); trimmedResult.Should().Be(trimmedExpectedXml); } - -======= ->>>>>>> parent of 3b286ad... Add Ranged value model with (de)serialization tests } } \ No newline at end of file diff --git a/src/Wps/Wps.Client/Models/Data/AllowedValues.cs b/src/Wps/Wps.Client/Models/Data/AllowedValues.cs index 042a2fe..33c7f06 100644 --- a/src/Wps/Wps.Client/Models/Data/AllowedValues.cs +++ b/src/Wps/Wps.Client/Models/Data/AllowedValues.cs @@ -10,5 +10,8 @@ public class AllowedValues : LiteralValue [XmlElement("Value", Namespace = ModelNamespaces.Ows)] public string[] Values { get; set; } + [XmlElement("Range", Namespace = ModelNamespaces.Ows)] + public ValueRange Range { get; set; } + } } From 5b84d9bb06fa28354687bcf52f033d9ff1b1651f Mon Sep 17 00:00:00 2001 From: Mihai Stan Date: Thu, 6 Jun 2019 12:50:49 +0200 Subject: [PATCH 39/76] Add DataInput model for the execution request --- .../ModelSerializationTests.cs | 31 +++++++++++- .../Wps.Client/Models/Execution/DataInput.cs | 47 +++++++++++++++++++ .../Models/Execution/ResourceReference.cs | 17 +++++++ 3 files changed, 94 insertions(+), 1 deletion(-) create mode 100644 src/Wps/Wps.Client/Models/Execution/DataInput.cs create mode 100644 src/Wps/Wps.Client/Models/Execution/ResourceReference.cs diff --git a/src/Wps/Wps.Client.Tests/ModelSerializationTests.cs b/src/Wps/Wps.Client.Tests/ModelSerializationTests.cs index 715b6f9..992ce8b 100644 --- a/src/Wps/Wps.Client.Tests/ModelSerializationTests.cs +++ b/src/Wps/Wps.Client.Tests/ModelSerializationTests.cs @@ -1,6 +1,7 @@ -using FluentAssertions; +using FluentAssertions; using System.Text.RegularExpressions; using Wps.Client.Models; +using Wps.Client.Models.Execution; using Wps.Client.Models.Requests; using Wps.Client.Services; using Xunit; @@ -127,5 +128,33 @@ public void SerializeValueRange_ValidRangeGiven_ShouldPass() var trimmedResult = Regex.Replace(resultXml, @"\s+", string.Empty); trimmedResult.Should().Be(trimmedExpectedXml); } + + [Fact] + public void SerializeDataInput_ValidInputGiven_ShouldPass() + { + const string expectedXml = @"105"; + + // Remove white spaces and new line characters. They do not change the actual (de)serialization of the XML. + var trimmedExpectedXml = Regex.Replace(expectedXml, @"\s+", string.Empty); + + var dataInput = new DataInput + { + Identifier = "test-id", + Reference = new ResourceReference + { + Href = "test", + Schema = "test-schema" + }, + Data = new LiteralDataValue + { + Value = 105 + } + }; + + var resultXml = _serializer.Serialize(dataInput); + var trimmedResult = Regex.Replace(resultXml, @"\s+", string.Empty); + trimmedResult.Should().Be(trimmedExpectedXml); + } + } } \ No newline at end of file diff --git a/src/Wps/Wps.Client/Models/Execution/DataInput.cs b/src/Wps/Wps.Client/Models/Execution/DataInput.cs new file mode 100644 index 0000000..b13d93b --- /dev/null +++ b/src/Wps/Wps.Client/Models/Execution/DataInput.cs @@ -0,0 +1,47 @@ +using System.Xml; +using System.Xml.Schema; +using System.Xml.Serialization; +using Wps.Client.Services; +using Wps.Client.Utils; + +namespace Wps.Client.Models.Execution +{ + [XmlRoot("Input", Namespace = ModelNamespaces.Wps)] + public class DataInput : IXmlSerializable + { + + public string Identifier { get; set; } + public object Data { get; set; } + public ResourceReference Reference { get; set; } + public DataInput[] Inputs { get; set; } + + public XmlSchema GetSchema() + { + return null; + } + + public void ReadXml(XmlReader reader) + { + throw new System.NotImplementedException(); + } + + public void WriteXml(XmlWriter writer) + { + var xmlSerializer = new XmlSerializationService(); + + writer.WriteAttributeString("id", Identifier); + + if (Data != null) + { + writer.WriteStartElement("wps", "Data", ModelNamespaces.Wps); + writer.WriteRaw(xmlSerializer.Serialize(Data, true)); + writer.WriteEndElement(); + } + + if (Reference != null) + { + writer.WriteRaw(xmlSerializer.Serialize(Reference, true)); + } + } + } +} diff --git a/src/Wps/Wps.Client/Models/Execution/ResourceReference.cs b/src/Wps/Wps.Client/Models/Execution/ResourceReference.cs new file mode 100644 index 0000000..ce14504 --- /dev/null +++ b/src/Wps/Wps.Client/Models/Execution/ResourceReference.cs @@ -0,0 +1,17 @@ +using System.Xml.Serialization; +using Wps.Client.Utils; + +namespace Wps.Client.Models.Execution +{ + [XmlRoot("Reference", Namespace = ModelNamespaces.Wps)] + public class ResourceReference + { + + [XmlAttribute("href", Namespace = ModelNamespaces.Xlink)] + public string Href { get; set; } + + [XmlAttribute("schema")] + public string Schema { get; set; } + + } +} From a60015fc26fd33b6b58311c4cba33ea2226d72a4 Mon Sep 17 00:00:00 2001 From: Mihai Stan Date: Thu, 6 Jun 2019 12:51:48 +0200 Subject: [PATCH 40/76] Add DataOutput for the execution request --- .../ModelSerializationTests.cs | 19 ++++++++++++++++++ .../Wps.Client/Models/Execution/DataOutput.cs | 20 +++++++++++++++++++ src/Wps/Wps.Client/Models/TransmissionMode.cs | 13 ++++++++++++ 3 files changed, 52 insertions(+) create mode 100644 src/Wps/Wps.Client/Models/Execution/DataOutput.cs create mode 100644 src/Wps/Wps.Client/Models/TransmissionMode.cs diff --git a/src/Wps/Wps.Client.Tests/ModelSerializationTests.cs b/src/Wps/Wps.Client.Tests/ModelSerializationTests.cs index 992ce8b..b717d83 100644 --- a/src/Wps/Wps.Client.Tests/ModelSerializationTests.cs +++ b/src/Wps/Wps.Client.Tests/ModelSerializationTests.cs @@ -156,5 +156,24 @@ public void SerializeDataInput_ValidInputGiven_ShouldPass() trimmedResult.Should().Be(trimmedExpectedXml); } + [Fact] + public void SerializeDataOutput_ValidOutputGiven_ShouldPass() + { + const string expectedXml = @""; + + // Remove white spaces and new line characters. They do not change the actual (de)serialization of the XML. + var trimmedExpectedXml = Regex.Replace(expectedXml, @"\s+", string.Empty); + + var dataInput = new DataOutput() + { + Identifier = "test-id", + Transmission = TransmissionMode.Value + }; + + var resultXml = _serializer.Serialize(dataInput); + var trimmedResult = Regex.Replace(resultXml, @"\s+", string.Empty); + trimmedResult.Should().Be(trimmedExpectedXml); + } + } } \ No newline at end of file diff --git a/src/Wps/Wps.Client/Models/Execution/DataOutput.cs b/src/Wps/Wps.Client/Models/Execution/DataOutput.cs new file mode 100644 index 0000000..d0c7a2b --- /dev/null +++ b/src/Wps/Wps.Client/Models/Execution/DataOutput.cs @@ -0,0 +1,20 @@ +using System.Xml.Serialization; +using Wps.Client.Utils; + +namespace Wps.Client.Models.Execution +{ + [XmlRoot("Output", Namespace = ModelNamespaces.Wps)] + public class DataOutput + { + + [XmlAttribute("transmission")] + public TransmissionMode Transmission { get; set; } + + [XmlAttribute("id")] + public string Identifier { get; set; } + + [XmlElement("Output", Namespace = ModelNamespaces.Wps)] + public DataOutput[] Outputs { get; set; } + + } +} diff --git a/src/Wps/Wps.Client/Models/TransmissionMode.cs b/src/Wps/Wps.Client/Models/TransmissionMode.cs new file mode 100644 index 0000000..2e12c6f --- /dev/null +++ b/src/Wps/Wps.Client/Models/TransmissionMode.cs @@ -0,0 +1,13 @@ +using System.Xml.Serialization; + +namespace Wps.Client.Models +{ + public enum TransmissionMode + { + + [XmlEnum("value")] Value, + [XmlEnum("reference")] Reference, + [XmlEnum("value reference")] ValueReference, + + } +} From 5ddb0d9a5508a568397d76208899ba85f19006e3 Mon Sep 17 00:00:00 2001 From: Mihai Stan Date: Thu, 6 Jun 2019 13:13:19 +0200 Subject: [PATCH 41/76] Serialize nested inputs in DataInput --- src/Wps/Wps.Client/Models/Execution/DataInput.cs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/Wps/Wps.Client/Models/Execution/DataInput.cs b/src/Wps/Wps.Client/Models/Execution/DataInput.cs index b13d93b..af18978 100644 --- a/src/Wps/Wps.Client/Models/Execution/DataInput.cs +++ b/src/Wps/Wps.Client/Models/Execution/DataInput.cs @@ -42,6 +42,14 @@ public void WriteXml(XmlWriter writer) { writer.WriteRaw(xmlSerializer.Serialize(Reference, true)); } + + if (Inputs != null) + { + foreach (var input in Inputs) + { + writer.WriteRaw(xmlSerializer.Serialize(input, true)); + } + } } } } From 5a331dbc2010cc4134bc4a8de182b08d093ae47d Mon Sep 17 00:00:00 2001 From: Mihai Stan Date: Thu, 6 Jun 2019 13:13:43 +0200 Subject: [PATCH 42/76] Add LiteralDataValue for execution request --- .../Models/Execution/LiteralDataValue.cs | 30 +++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 src/Wps/Wps.Client/Models/Execution/LiteralDataValue.cs diff --git a/src/Wps/Wps.Client/Models/Execution/LiteralDataValue.cs b/src/Wps/Wps.Client/Models/Execution/LiteralDataValue.cs new file mode 100644 index 0000000..15c1949 --- /dev/null +++ b/src/Wps/Wps.Client/Models/Execution/LiteralDataValue.cs @@ -0,0 +1,30 @@ +using System; +using System.Xml; +using System.Xml.Schema; +using System.Xml.Serialization; +using Wps.Client.Utils; + +namespace Wps.Client.Models.Execution +{ + [XmlRoot("LiteralValue", Namespace = ModelNamespaces.Wps)] + public class LiteralDataValue : IXmlSerializable + { + + public object Value { get; set; } + + public XmlSchema GetSchema() + { + return null; + } + + public void ReadXml(XmlReader reader) + { + throw new NotImplementedException(); + } + + public void WriteXml(XmlWriter writer) + { + writer.WriteRaw(Value.ToString()); + } + } +} From cb9f42cbc3b9e30824800c8b50b0d118bf50ac5b Mon Sep 17 00:00:00 2001 From: Mihai Stan Date: Thu, 6 Jun 2019 13:14:25 +0200 Subject: [PATCH 43/76] Add Execution request model with serialization test --- .../ModelSerializationTests.cs | 48 +++++++++++++++++++ .../Models/Execution/ExecutionMode.cs | 11 +++++ .../Models/Execution/ResponseType.cs | 12 +++++ .../Models/Requests/ExecuteRequest.cs | 27 +++++++++++ 4 files changed, 98 insertions(+) create mode 100644 src/Wps/Wps.Client/Models/Execution/ExecutionMode.cs create mode 100644 src/Wps/Wps.Client/Models/Execution/ResponseType.cs create mode 100644 src/Wps/Wps.Client/Models/Requests/ExecuteRequest.cs diff --git a/src/Wps/Wps.Client.Tests/ModelSerializationTests.cs b/src/Wps/Wps.Client.Tests/ModelSerializationTests.cs index b717d83..b141050 100644 --- a/src/Wps/Wps.Client.Tests/ModelSerializationTests.cs +++ b/src/Wps/Wps.Client.Tests/ModelSerializationTests.cs @@ -175,5 +175,53 @@ public void SerializeDataOutput_ValidOutputGiven_ShouldPass() trimmedResult.Should().Be(trimmedExpectedXml); } + [Fact] + public void SerializeExecuteRequest_ValidRequestGiven_ShouldPass() + { + const string expectedXml = @"org.n52.wps.server.algorithm.SimpleBufferAlgorithm0.05"; + + // Remove white spaces and new line characters. They do not change the actual (de)serialization of the XML. + var trimmedExpectedXml = Regex.Replace(expectedXml, @"\s+", string.Empty); + + var executeRequest = new ExecuteRequest + { + Identifier = "org.n52.wps.server.algorithm.SimpleBufferAlgorithm", + ExecutionMode = ExecutionMode.Synchronous, + ResponseType = ResponseType.Document, + Inputs = new[] + { + new DataInput + { + Identifier = "data", + Reference = new ResourceReference + { + Href = "http://geoprocessing.demo.52north.org:8080/geoserver/wfs?SERVICE=WFS&VERSION=1.0.0&REQUEST=GetFeature&TYPENAME=topp:tasmania_roads&SRS=EPSG:4326&OUTPUTFORMAT=GML3", + Schema = "http://schemas.opengis.net/gml/3.1.1/base/feature.xsd" + } + }, + new DataInput + { + Identifier = "width", + Data = new LiteralDataValue + { + Value = 0.05f + } + } + }, + Outputs = new [] + { + new DataOutput + { + Identifier = "result", + Transmission = TransmissionMode.Value + } + } + }; + + var resultXml = _serializer.Serialize(executeRequest); + var trimmedResult = Regex.Replace(resultXml, @"\s+", string.Empty); + trimmedResult.Should().Be(trimmedExpectedXml); + } + } } \ No newline at end of file diff --git a/src/Wps/Wps.Client/Models/Execution/ExecutionMode.cs b/src/Wps/Wps.Client/Models/Execution/ExecutionMode.cs new file mode 100644 index 0000000..8bae052 --- /dev/null +++ b/src/Wps/Wps.Client/Models/Execution/ExecutionMode.cs @@ -0,0 +1,11 @@ +using System.Xml.Serialization; + +namespace Wps.Client.Models.Execution +{ + public enum ExecutionMode + { + [XmlEnum("sync")] Synchronous, + [XmlEnum("async")] Asynchronous, + [XmlEnum("auto")] Auto + } +} diff --git a/src/Wps/Wps.Client/Models/Execution/ResponseType.cs b/src/Wps/Wps.Client/Models/Execution/ResponseType.cs new file mode 100644 index 0000000..690b95f --- /dev/null +++ b/src/Wps/Wps.Client/Models/Execution/ResponseType.cs @@ -0,0 +1,12 @@ +using System.Xml.Serialization; + +namespace Wps.Client.Models.Execution +{ + public enum ResponseType + { + + [XmlEnum("raw")] Raw, + [XmlEnum("document")] Document + + } +} diff --git a/src/Wps/Wps.Client/Models/Requests/ExecuteRequest.cs b/src/Wps/Wps.Client/Models/Requests/ExecuteRequest.cs new file mode 100644 index 0000000..79fc992 --- /dev/null +++ b/src/Wps/Wps.Client/Models/Requests/ExecuteRequest.cs @@ -0,0 +1,27 @@ +using System.Xml.Serialization; +using Wps.Client.Models.Execution; +using Wps.Client.Utils; + +namespace Wps.Client.Models.Requests +{ + [XmlRoot("Execute", Namespace = ModelNamespaces.Wps)] + public class ExecuteRequest : RequestBase + { + + [XmlAttribute("mode", Namespace = ModelNamespaces.Wps)] + public ExecutionMode ExecutionMode { get; set; } + + [XmlAttribute("response", Namespace = ModelNamespaces.Wps)] + public ResponseType ResponseType { get; set; } + + [XmlElement("Identifier", Namespace = ModelNamespaces.Ows)] + public string Identifier { get; set; } + + [XmlElement("Input", Namespace = ModelNamespaces.Wps)] + public DataInput[] Inputs { get; set; } + + [XmlElement("Output", Namespace = ModelNamespaces.Wps)] + public DataOutput[] Outputs { get; set; } + + } +} From 51c40f6fad391f70b45812096f472cdc2e4986b4 Mon Sep 17 00:00:00 2001 From: Mihai Stan Date: Fri, 7 Jun 2019 12:26:11 +0200 Subject: [PATCH 44/76] Fix GetCapabilitiesRequest serialization test It was outdated and the expected XML didn't contain all the namespaces needed. --- src/Wps/Wps.Client.Tests/ModelSerializationTests.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Wps/Wps.Client.Tests/ModelSerializationTests.cs b/src/Wps/Wps.Client.Tests/ModelSerializationTests.cs index b141050..6ffa98d 100644 --- a/src/Wps/Wps.Client.Tests/ModelSerializationTests.cs +++ b/src/Wps/Wps.Client.Tests/ModelSerializationTests.cs @@ -1,4 +1,4 @@ -using FluentAssertions; +using FluentAssertions; using System.Text.RegularExpressions; using Wps.Client.Models; using Wps.Client.Models.Execution; @@ -21,7 +21,7 @@ public ModelSerializationTests(XmlSerializationService serializer) [Fact] public void SerializeGetCapabilitiesRequest_ValidRequestGiven_ShouldPass() { - const string expectedXml = @""; + const string expectedXml = @""; // Remove white spaces and new line characters. They do not change the actual (de)serialization of the XML. var trimmedExpectedXml = Regex.Replace(expectedXml, @"\s+", string.Empty); From a4a7bb473fd18e861e59a3fa8dbbcc227f4c2131 Mon Sep 17 00:00:00 2001 From: Mihai Stan Date: Fri, 7 Jun 2019 12:39:53 +0200 Subject: [PATCH 45/76] Add BoundingBoxData model with (de)serializaiton tests --- .../ModelDeserializationTests.cs | 33 ++++++++++++++++++ .../ModelSerializationTests.cs | 32 +++++++++++++++-- src/Wps/Wps.Client/Models/BoundingBoxData.cs | 17 ++++++++++ .../Models/CoordinateReferenceSystem.cs | 34 +++++++++++++++++++ 4 files changed, 114 insertions(+), 2 deletions(-) create mode 100644 src/Wps/Wps.Client/Models/BoundingBoxData.cs create mode 100644 src/Wps/Wps.Client/Models/CoordinateReferenceSystem.cs diff --git a/src/Wps/Wps.Client.Tests/ModelDeserializationTests.cs b/src/Wps/Wps.Client.Tests/ModelDeserializationTests.cs index 7d90b92..b614732 100644 --- a/src/Wps/Wps.Client.Tests/ModelDeserializationTests.cs +++ b/src/Wps/Wps.Client.Tests/ModelDeserializationTests.cs @@ -648,5 +648,38 @@ public void DeserializeValueRange_ValidXmlGiven_ShouldPass() result.Spacing.Should().Be("100"); result.RangeClosure.Should().Be(RangeClosure.ClosedOpen); } + + [Fact] + public void DeserializeCoordinateReferenceSystem_ValidXmlGiven_ShouldPass() + { + const string xml = @" +Test reference system"; + + var crs = _serializer.Deserialize(xml); + crs.IsDefault.Should().BeTrue(); + crs.Uri.Should().Be("Test reference system"); + } + + [Fact] + public void DeserializeBoundingBoxData_ValidXmlGiven_ShouldPass() + { + const string xml = @" + + + + test-uri-1 + test-uri-1 + test-uri-1 +"; + + var boundingBoxData = _serializer.Deserialize(xml); + boundingBoxData.SupportedCrs.Should().HaveCount(3); + boundingBoxData.Formats.Should().HaveCount(2); + } + } } diff --git a/src/Wps/Wps.Client.Tests/ModelSerializationTests.cs b/src/Wps/Wps.Client.Tests/ModelSerializationTests.cs index 6ffa98d..b8f9957 100644 --- a/src/Wps/Wps.Client.Tests/ModelSerializationTests.cs +++ b/src/Wps/Wps.Client.Tests/ModelSerializationTests.cs @@ -1,4 +1,4 @@ -using FluentAssertions; +using FluentAssertions; using System.Text.RegularExpressions; using Wps.Client.Models; using Wps.Client.Models.Execution; @@ -208,7 +208,7 @@ public void SerializeExecuteRequest_ValidRequestGiven_ShouldPass() } } }, - Outputs = new [] + Outputs = new[] { new DataOutput { @@ -223,5 +223,33 @@ public void SerializeExecuteRequest_ValidRequestGiven_ShouldPass() trimmedResult.Should().Be(trimmedExpectedXml); } + [Fact] + public void SerializeBoundingBoxData_ValidObjectsGiven_ShouldPass() + { + const string expectedXml = @"test-uri-1test-uri-1test-uri-1"; + + // Remove white spaces and new line characters. They do not change the actual (de)serialization of the XML. + var trimmedExpectedXml = Regex.Replace(expectedXml, @"\s+", string.Empty); + + var boundingBoxData = new BoundingBoxData + { + Formats = new[] + { + new Format { MimeType = "test" }, + new Format { MimeType = "test" }, + }, + SupportedCrs = new[] + { + new CoordinateReferenceSystem{ IsDefault = true, Uri = "test-uri-1" }, + new CoordinateReferenceSystem{ IsDefault = true, Uri = "test-uri-1" }, + new CoordinateReferenceSystem{ IsDefault = true, Uri = "test-uri-1" }, + } + }; + + var resultXml = _serializer.Serialize(boundingBoxData); + var trimmedResult = Regex.Replace(resultXml, @"\s+", string.Empty); + trimmedResult.Should().Be(trimmedExpectedXml); + } + } } \ No newline at end of file diff --git a/src/Wps/Wps.Client/Models/BoundingBoxData.cs b/src/Wps/Wps.Client/Models/BoundingBoxData.cs new file mode 100644 index 0000000..de7d8d7 --- /dev/null +++ b/src/Wps/Wps.Client/Models/BoundingBoxData.cs @@ -0,0 +1,17 @@ +using System.Xml.Serialization; +using Wps.Client.Utils; + +namespace Wps.Client.Models +{ + [XmlRoot("BoundingBoxData", Namespace = ModelNamespaces.Wps)] + public class BoundingBoxData : Data.Data + { + + /// + /// List of the supported Coordinate Reference Systems. + /// + [XmlElement("SupportedCRS", Namespace = ModelNamespaces.Wps)] + public CoordinateReferenceSystem[] SupportedCrs { get; set; } + + } +} diff --git a/src/Wps/Wps.Client/Models/CoordinateReferenceSystem.cs b/src/Wps/Wps.Client/Models/CoordinateReferenceSystem.cs new file mode 100644 index 0000000..8aeae2e --- /dev/null +++ b/src/Wps/Wps.Client/Models/CoordinateReferenceSystem.cs @@ -0,0 +1,34 @@ +using System.Xml; +using System.Xml.Schema; +using System.Xml.Serialization; +using Wps.Client.Utils; + +namespace Wps.Client.Models +{ + [XmlRoot("CRS", Namespace = ModelNamespaces.Wps)] + public class CoordinateReferenceSystem : IXmlSerializable + { + + public string Uri { get; set; } + + public bool IsDefault { get; set; } + + public XmlSchema GetSchema() + { + return null; + } + + // This might actually break the XML deserializer of other elements using C# attributes. Careful when reading! Should read the subtree. + public void ReadXml(XmlReader baseReader) + { + IsDefault = bool.TryParse(baseReader.GetAttribute("default"), out var isDefault) && isDefault; + Uri = baseReader.ReadElementContentAsString(); + } + + public void WriteXml(XmlWriter writer) + { + writer.WriteAttributeString("default", IsDefault.ToString().ToLower()); + writer.WriteRaw(Uri); + } + } +} From 22e4acaea99384d004035c31181743e21c45bcd0 Mon Sep 17 00:00:00 2001 From: Mihai Stan Date: Fri, 7 Jun 2019 12:59:16 +0200 Subject: [PATCH 46/76] Add BoundingBoxValue for the execution request --- .../ModelSerializationTests.cs | 26 ++++++++ .../Models/Execution/BoundingBoxValue.cs | 64 +++++++++++++++++++ 2 files changed, 90 insertions(+) create mode 100644 src/Wps/Wps.Client/Models/Execution/BoundingBoxValue.cs diff --git a/src/Wps/Wps.Client.Tests/ModelSerializationTests.cs b/src/Wps/Wps.Client.Tests/ModelSerializationTests.cs index b8f9957..fa7d789 100644 --- a/src/Wps/Wps.Client.Tests/ModelSerializationTests.cs +++ b/src/Wps/Wps.Client.Tests/ModelSerializationTests.cs @@ -251,5 +251,31 @@ public void SerializeBoundingBoxData_ValidObjectsGiven_ShouldPass() trimmedResult.Should().Be(trimmedExpectedXml); } + [Fact] + public void SerializeBoundingBoxValue_ValidObjectsGiven_ShouldPass() + { + const string expectedXml = @" + + + 1.1 2.2 3.3 + 3.1 2.2 1.3 +"; + + // Remove white spaces and new line characters. They do not change the actual (de)serialization of the XML. + var trimmedExpectedXml = Regex.Replace(expectedXml, @"\s+", string.Empty); + + var boundingBoxData = new BoundingBoxValue + { + CrsUri = "crs-uri", + DimensionCount = 3, + LowerCornerPoints = new[] { 1.1, 2.2, 3.3 }, + UpperCornerPoints = new[] { 3.1, 2.2, 1.3 }, + }; + + var resultXml = _serializer.Serialize(boundingBoxData); + var trimmedResult = Regex.Replace(resultXml, @"\s+", string.Empty); + trimmedResult.Should().Be(trimmedExpectedXml); + } + } } \ No newline at end of file diff --git a/src/Wps/Wps.Client/Models/Execution/BoundingBoxValue.cs b/src/Wps/Wps.Client/Models/Execution/BoundingBoxValue.cs new file mode 100644 index 0000000..0396901 --- /dev/null +++ b/src/Wps/Wps.Client/Models/Execution/BoundingBoxValue.cs @@ -0,0 +1,64 @@ +using System; +using System.Xml; +using System.Xml.Schema; +using System.Xml.Serialization; +using Wps.Client.Utils; + +namespace Wps.Client.Models.Execution +{ + [XmlRoot("BoundingBox", Namespace = ModelNamespaces.Ows)] + public class BoundingBoxValue : IXmlSerializable + { + + public double[] LowerCornerPoints { get; set; } + public double[] UpperCornerPoints { get; set; } + public string CrsUri { get; set; } + + /// + /// Number of dimensions in this bounding box. + /// + public int DimensionCount { get; set; } + + public XmlSchema GetSchema() + { + return null; + } + + public void ReadXml(XmlReader reader) + { + /* + * TODO: Add manual deserialization for this object. Careful, the XmlReader has a confusing cursor system that once shifted (even by 1!) will break the serialization for the rest of the objects and will give you a completely wrong XML for the containing paren too! + */ + throw new System.NotImplementedException(); + } + + public void WriteXml(XmlWriter writer) + { + if (CrsUri != null) + { + writer.WriteAttributeString("crs", ModelNamespaces.Ows, CrsUri); + } + + if (DimensionCount >= 0) + { + writer.WriteAttributeString("dimensions", ModelNamespaces.Ows, DimensionCount.ToString()); + } + else + { + throw new InvalidOperationException("The dimension count inside a bounding box cannot be negative."); + } + + if (LowerCornerPoints != null) + { + var content = string.Join(" ", LowerCornerPoints); + writer.WriteElementString("ows", "LowerCorner", ModelNamespaces.Ows, content); + } + + if (UpperCornerPoints != null) + { + var content = string.Join(" ", UpperCornerPoints); + writer.WriteElementString("ows", "UpperCorner", ModelNamespaces.Ows, content); + } + } + } +} From 75a5910e46b9279f9664933d83feaae17fb56a2c Mon Sep 17 00:00:00 2001 From: Mihai Stan Date: Fri, 7 Jun 2019 13:04:35 +0200 Subject: [PATCH 47/76] Add encoding attributes to DataOutput model --- .../ModelSerializationTests.cs | 7 ++-- .../Wps.Client/Models/Execution/DataOutput.cs | 36 +++++++++++++++++++ 2 files changed, 41 insertions(+), 2 deletions(-) diff --git a/src/Wps/Wps.Client.Tests/ModelSerializationTests.cs b/src/Wps/Wps.Client.Tests/ModelSerializationTests.cs index fa7d789..4d07576 100644 --- a/src/Wps/Wps.Client.Tests/ModelSerializationTests.cs +++ b/src/Wps/Wps.Client.Tests/ModelSerializationTests.cs @@ -159,7 +159,7 @@ public void SerializeDataInput_ValidInputGiven_ShouldPass() [Fact] public void SerializeDataOutput_ValidOutputGiven_ShouldPass() { - const string expectedXml = @""; + const string expectedXml = @""; // Remove white spaces and new line characters. They do not change the actual (de)serialization of the XML. var trimmedExpectedXml = Regex.Replace(expectedXml, @"\s+", string.Empty); @@ -167,7 +167,10 @@ public void SerializeDataOutput_ValidOutputGiven_ShouldPass() var dataInput = new DataOutput() { Identifier = "test-id", - Transmission = TransmissionMode.Value + Transmission = TransmissionMode.Value, + Encoding = "test-encoding", + MimeType = "test-mimetype", + Schema = "test-schema" }; var resultXml = _serializer.Serialize(dataInput); diff --git a/src/Wps/Wps.Client/Models/Execution/DataOutput.cs b/src/Wps/Wps.Client/Models/Execution/DataOutput.cs index d0c7a2b..2682bc0 100644 --- a/src/Wps/Wps.Client/Models/Execution/DataOutput.cs +++ b/src/Wps/Wps.Client/Models/Execution/DataOutput.cs @@ -7,12 +7,48 @@ namespace Wps.Client.Models.Execution public class DataOutput { + /// + /// The media type of the data. + /// + /// + /// It is mandatory to precise the media type. + /// + [XmlAttribute("mimeType")] + public string MimeType { get; set; } + + /// + /// The encoding procedure or character set of the data. (e.g. raw or base64) + /// + /// + /// It is mandatory to precise the encoding. + /// + [XmlAttribute("encoding")] + public string Encoding { get; set; } + + /// + /// The identification of the data schema. + /// + /// + /// It is mandatory to precise the schema. + /// + [XmlAttribute("schema")] + public string Schema { get; set; } + + /// + /// Code that indicates the desired data transmission mode for this output. + /// [XmlAttribute("transmission")] public TransmissionMode Transmission { get; set; } + /// + /// Identifier of a particular output, as defined in the process description. + /// [XmlAttribute("id")] public string Identifier { get; set; } + /// + /// Nested outputs of this data output. + /// [XmlElement("Output", Namespace = ModelNamespaces.Wps)] public DataOutput[] Outputs { get; set; } From 5421ae94838d7a9693931d3a2b298d4a5521f830 Mon Sep 17 00:00:00 2001 From: Mihai Stan Date: Fri, 7 Jun 2019 13:04:56 +0200 Subject: [PATCH 48/76] Add BoundingBoxData as possible value for inputs and outputs --- src/Wps/Wps.Client/Models/Input.cs | 3 ++- src/Wps/Wps.Client/Models/Output.cs | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/Wps/Wps.Client/Models/Input.cs b/src/Wps/Wps.Client/Models/Input.cs index f109d35..906bd8a 100644 --- a/src/Wps/Wps.Client/Models/Input.cs +++ b/src/Wps/Wps.Client/Models/Input.cs @@ -34,7 +34,8 @@ public class Input : IdentifiableObject /// The data that will be present in this input. ///
[XmlElement("LiteralData", Type = typeof(LiteralData), Namespace = ModelNamespaces.Wps), - XmlElement("ComplexData", Type = typeof(ComplexData), Namespace = ModelNamespaces.Wps)] + XmlElement("ComplexData", Type = typeof(ComplexData), Namespace = ModelNamespaces.Wps), + XmlElement("BoundingBoxData", Type = typeof(BoundingBoxData), Namespace = ModelNamespaces.Wps)] public Data.Data Data { get; set; } /// diff --git a/src/Wps/Wps.Client/Models/Output.cs b/src/Wps/Wps.Client/Models/Output.cs index 8a3a4bb..8e3f0f9 100644 --- a/src/Wps/Wps.Client/Models/Output.cs +++ b/src/Wps/Wps.Client/Models/Output.cs @@ -14,7 +14,8 @@ public class Output : IdentifiableObject /// The data that will be present in this output. /// [XmlElement("LiteralData", Type = typeof(LiteralData), Namespace = ModelNamespaces.Wps), - XmlElement("ComplexData", Type = typeof(ComplexData), Namespace = ModelNamespaces.Wps)] + XmlElement("ComplexData", Type = typeof(ComplexData), Namespace = ModelNamespaces.Wps), + XmlElement("BoundingBoxData", Type = typeof(BoundingBoxData), Namespace = ModelNamespaces.Wps)] public Data.Data Data { get; set; } /// From e9669f67ef72d5dfa93ac631fd795d478e252deb Mon Sep 17 00:00:00 2001 From: Mihai Stan Date: Fri, 7 Jun 2019 13:23:47 +0200 Subject: [PATCH 49/76] Use TransmissionMode type instead of raw strings --- src/Wps/Wps.Client.Tests/ModelDeserializationTests.cs | 4 ++-- src/Wps/Wps.Client/Models/ProcessOffering.cs | 5 ++--- src/Wps/Wps.Client/Models/ProcessSummary.cs | 2 +- 3 files changed, 5 insertions(+), 6 deletions(-) diff --git a/src/Wps/Wps.Client.Tests/ModelDeserializationTests.cs b/src/Wps/Wps.Client.Tests/ModelDeserializationTests.cs index b614732..a51bf0c 100644 --- a/src/Wps/Wps.Client.Tests/ModelDeserializationTests.cs +++ b/src/Wps/Wps.Client.Tests/ModelDeserializationTests.cs @@ -289,7 +289,7 @@ public void DeserializeProcessOffering_ValidXmlGiven_ShouldPass() var processOffering = _serializer.Deserialize(xml); processOffering?.ProcessVersion.Should().Be("1.0.0"); processOffering?.JobControlOptions.Should().Be("sync-execute async-execute"); - processOffering?.OutputTransmission.Should().Be("value reference"); + processOffering?.OutputTransmission.Should().Be(TransmissionMode.ValueReference); processOffering?.ProcessModel.Should().Be("test"); processOffering?.Process.Should().NotBeNull(); processOffering?.Process.GetType().Should().Be(typeof(Process)); @@ -339,7 +339,7 @@ public void DeserializeProcessSummary_ValidXmlGiven_ShouldPass() summary.ProcessVersion.Should().Be("1.0.0"); summary.ProcessModel.Should().Be("test"); summary.JobControlOptions.Should().Be("sync-execute async-execute"); - summary.OutputTransmission.Should().Be("value reference"); + summary.OutputTransmission.Should().Be(TransmissionMode.ValueReference); summary.Keywords.Should().BeEquivalentTo("WPS", "geospatial", "geoprocessing"); } diff --git a/src/Wps/Wps.Client/Models/ProcessOffering.cs b/src/Wps/Wps.Client/Models/ProcessOffering.cs index 1c47c83..0c992e5 100644 --- a/src/Wps/Wps.Client/Models/ProcessOffering.cs +++ b/src/Wps/Wps.Client/Models/ProcessOffering.cs @@ -1,5 +1,4 @@ -using System.Xml.Schema; -using System.Xml.Serialization; +using System.Xml.Serialization; using Wps.Client.Utils; namespace Wps.Client.Models @@ -30,7 +29,7 @@ public class ProcessOffering /// At least one mode is required. /// [XmlAttribute("outputTransmission", Namespace = ModelNamespaces.Wps)] - public string OutputTransmission { get; set; } + public TransmissionMode OutputTransmission { get; set; } /// /// Release version of the process. diff --git a/src/Wps/Wps.Client/Models/ProcessSummary.cs b/src/Wps/Wps.Client/Models/ProcessSummary.cs index 00caa95..763d9bd 100644 --- a/src/Wps/Wps.Client/Models/ProcessSummary.cs +++ b/src/Wps/Wps.Client/Models/ProcessSummary.cs @@ -29,7 +29,7 @@ public class ProcessSummary : IdentifiableObject /// At least one mode is required. /// [XmlAttribute("outputTransmission", Namespace = ModelNamespaces.Wps)] - public string OutputTransmission { get; set; } + public TransmissionMode OutputTransmission { get; set; } /// /// Keywords that characterize the process. From a51ed89c3d4542d51db86f5c0bcca8333e05d458 Mon Sep 17 00:00:00 2001 From: Mihai Stan Date: Fri, 7 Jun 2019 13:59:50 +0200 Subject: [PATCH 50/76] Move the OWS specific models into their own namespace --- src/Wps/Wps.Client.Tests/ModelDeserializationTests.cs | 1 + src/Wps/Wps.Client.Tests/ModelSerializationTests.cs | 1 + src/Wps/Wps.Client/Models/Data/AllowedValues.cs | 1 + src/Wps/Wps.Client/Models/{ => Ows}/Address.cs | 2 +- src/Wps/Wps.Client/Models/{ => Ows}/ContactInfo.cs | 2 +- .../Models/{ => Ows}/DistributedComputingPlatform.cs | 2 +- src/Wps/Wps.Client/Models/{ => Ows}/Operation.cs | 2 +- src/Wps/Wps.Client/Models/{ => Ows}/OperationConstraint.cs | 5 ++--- src/Wps/Wps.Client/Models/{ => Ows}/OperationParameter.cs | 2 +- src/Wps/Wps.Client/Models/{ => Ows}/OperationsMetadata.cs | 2 +- .../Wps.Client/Models/{ => Ows}/PlatformConnectionPoint.cs | 2 +- src/Wps/Wps.Client/Models/{ => Ows}/ProviderSite.cs | 2 +- src/Wps/Wps.Client/Models/{ => Ows}/ServiceContact.cs | 2 +- src/Wps/Wps.Client/Models/{ => Ows}/ServiceIdentification.cs | 2 +- src/Wps/Wps.Client/Models/{ => Ows}/ServiceProvider.cs | 2 +- src/Wps/Wps.Client/Models/{ => Ows}/ValueRange.cs | 5 ++--- .../Wps.Client/Models/Responses/GetCapabilitiesResponse.cs | 1 + 17 files changed, 19 insertions(+), 17 deletions(-) rename src/Wps/Wps.Client/Models/{ => Ows}/Address.cs (97%) rename src/Wps/Wps.Client/Models/{ => Ows}/ContactInfo.cs (97%) rename src/Wps/Wps.Client/Models/{ => Ows}/DistributedComputingPlatform.cs (92%) rename src/Wps/Wps.Client/Models/{ => Ows}/Operation.cs (97%) rename src/Wps/Wps.Client/Models/{ => Ows}/OperationConstraint.cs (88%) rename src/Wps/Wps.Client/Models/{ => Ows}/OperationParameter.cs (93%) rename src/Wps/Wps.Client/Models/{ => Ows}/OperationsMetadata.cs (97%) rename src/Wps/Wps.Client/Models/{ => Ows}/PlatformConnectionPoint.cs (96%) rename src/Wps/Wps.Client/Models/{ => Ows}/ProviderSite.cs (92%) rename src/Wps/Wps.Client/Models/{ => Ows}/ServiceContact.cs (97%) rename src/Wps/Wps.Client/Models/{ => Ows}/ServiceIdentification.cs (98%) rename src/Wps/Wps.Client/Models/{ => Ows}/ServiceProvider.cs (96%) rename src/Wps/Wps.Client/Models/{ => Ows}/ValueRange.cs (88%) diff --git a/src/Wps/Wps.Client.Tests/ModelDeserializationTests.cs b/src/Wps/Wps.Client.Tests/ModelDeserializationTests.cs index a51bf0c..aa481ff 100644 --- a/src/Wps/Wps.Client.Tests/ModelDeserializationTests.cs +++ b/src/Wps/Wps.Client.Tests/ModelDeserializationTests.cs @@ -3,6 +3,7 @@ using System.IO; using Wps.Client.Models; using Wps.Client.Models.Data; +using Wps.Client.Models.Ows; using Wps.Client.Models.Responses; using Wps.Client.Services; using Xunit; diff --git a/src/Wps/Wps.Client.Tests/ModelSerializationTests.cs b/src/Wps/Wps.Client.Tests/ModelSerializationTests.cs index 4d07576..9375709 100644 --- a/src/Wps/Wps.Client.Tests/ModelSerializationTests.cs +++ b/src/Wps/Wps.Client.Tests/ModelSerializationTests.cs @@ -2,6 +2,7 @@ using System.Text.RegularExpressions; using Wps.Client.Models; using Wps.Client.Models.Execution; +using Wps.Client.Models.Ows; using Wps.Client.Models.Requests; using Wps.Client.Services; using Xunit; diff --git a/src/Wps/Wps.Client/Models/Data/AllowedValues.cs b/src/Wps/Wps.Client/Models/Data/AllowedValues.cs index 33c7f06..b27b54c 100644 --- a/src/Wps/Wps.Client/Models/Data/AllowedValues.cs +++ b/src/Wps/Wps.Client/Models/Data/AllowedValues.cs @@ -1,4 +1,5 @@ using System.Xml.Serialization; +using Wps.Client.Models.Ows; using Wps.Client.Utils; namespace Wps.Client.Models.Data diff --git a/src/Wps/Wps.Client/Models/Address.cs b/src/Wps/Wps.Client/Models/Ows/Address.cs similarity index 97% rename from src/Wps/Wps.Client/Models/Address.cs rename to src/Wps/Wps.Client/Models/Ows/Address.cs index edfb31b..d2bad96 100644 --- a/src/Wps/Wps.Client/Models/Address.cs +++ b/src/Wps/Wps.Client/Models/Ows/Address.cs @@ -1,7 +1,7 @@ using System.Xml.Serialization; using Wps.Client.Utils; -namespace Wps.Client.Models +namespace Wps.Client.Models.Ows { [XmlRoot("Address", Namespace = ModelNamespaces.Ows)] public class Address diff --git a/src/Wps/Wps.Client/Models/ContactInfo.cs b/src/Wps/Wps.Client/Models/Ows/ContactInfo.cs similarity index 97% rename from src/Wps/Wps.Client/Models/ContactInfo.cs rename to src/Wps/Wps.Client/Models/Ows/ContactInfo.cs index d822cf6..7e7e69b 100644 --- a/src/Wps/Wps.Client/Models/ContactInfo.cs +++ b/src/Wps/Wps.Client/Models/Ows/ContactInfo.cs @@ -1,7 +1,7 @@ using System.Xml.Serialization; using Wps.Client.Utils; -namespace Wps.Client.Models +namespace Wps.Client.Models.Ows { /// /// Address of the responsible party. diff --git a/src/Wps/Wps.Client/Models/DistributedComputingPlatform.cs b/src/Wps/Wps.Client/Models/Ows/DistributedComputingPlatform.cs similarity index 92% rename from src/Wps/Wps.Client/Models/DistributedComputingPlatform.cs rename to src/Wps/Wps.Client/Models/Ows/DistributedComputingPlatform.cs index 0ed7950..111e58b 100644 --- a/src/Wps/Wps.Client/Models/DistributedComputingPlatform.cs +++ b/src/Wps/Wps.Client/Models/Ows/DistributedComputingPlatform.cs @@ -1,7 +1,7 @@ using System.Xml.Serialization; using Wps.Client.Utils; -namespace Wps.Client.Models +namespace Wps.Client.Models.Ows { [XmlRoot("DCP", Namespace = ModelNamespaces.Ows)] public class DistributedComputingPlatform diff --git a/src/Wps/Wps.Client/Models/Operation.cs b/src/Wps/Wps.Client/Models/Ows/Operation.cs similarity index 97% rename from src/Wps/Wps.Client/Models/Operation.cs rename to src/Wps/Wps.Client/Models/Ows/Operation.cs index e3a4f05..5d86777 100644 --- a/src/Wps/Wps.Client/Models/Operation.cs +++ b/src/Wps/Wps.Client/Models/Ows/Operation.cs @@ -1,7 +1,7 @@ using System.Xml.Serialization; using Wps.Client.Utils; -namespace Wps.Client.Models +namespace Wps.Client.Models.Ows { /// /// diff --git a/src/Wps/Wps.Client/Models/OperationConstraint.cs b/src/Wps/Wps.Client/Models/Ows/OperationConstraint.cs similarity index 88% rename from src/Wps/Wps.Client/Models/OperationConstraint.cs rename to src/Wps/Wps.Client/Models/Ows/OperationConstraint.cs index aec9e88..89af4fb 100644 --- a/src/Wps/Wps.Client/Models/OperationConstraint.cs +++ b/src/Wps/Wps.Client/Models/Ows/OperationConstraint.cs @@ -1,8 +1,7 @@ -using System.Xml; -using System.Xml.Serialization; +using System.Xml.Serialization; using Wps.Client.Utils; -namespace Wps.Client.Models +namespace Wps.Client.Models.Ows { [XmlRoot("Constraint", Namespace = ModelNamespaces.Ows)] public class OperationConstraint diff --git a/src/Wps/Wps.Client/Models/OperationParameter.cs b/src/Wps/Wps.Client/Models/Ows/OperationParameter.cs similarity index 93% rename from src/Wps/Wps.Client/Models/OperationParameter.cs rename to src/Wps/Wps.Client/Models/Ows/OperationParameter.cs index ee5ffc8..4774c3f 100644 --- a/src/Wps/Wps.Client/Models/OperationParameter.cs +++ b/src/Wps/Wps.Client/Models/Ows/OperationParameter.cs @@ -1,7 +1,7 @@ using System.Xml.Serialization; using Wps.Client.Utils; -namespace Wps.Client.Models +namespace Wps.Client.Models.Ows { [XmlRoot("Parameter", Namespace = ModelNamespaces.Ows)] public class OperationParameter diff --git a/src/Wps/Wps.Client/Models/OperationsMetadata.cs b/src/Wps/Wps.Client/Models/Ows/OperationsMetadata.cs similarity index 97% rename from src/Wps/Wps.Client/Models/OperationsMetadata.cs rename to src/Wps/Wps.Client/Models/Ows/OperationsMetadata.cs index c452a33..cf69c35 100644 --- a/src/Wps/Wps.Client/Models/OperationsMetadata.cs +++ b/src/Wps/Wps.Client/Models/Ows/OperationsMetadata.cs @@ -1,7 +1,7 @@ using System.Xml.Serialization; using Wps.Client.Utils; -namespace Wps.Client.Models +namespace Wps.Client.Models.Ows { /// /// Metadata about the operations and related abilities specified by this service and implemented by this server. diff --git a/src/Wps/Wps.Client/Models/PlatformConnectionPoint.cs b/src/Wps/Wps.Client/Models/Ows/PlatformConnectionPoint.cs similarity index 96% rename from src/Wps/Wps.Client/Models/PlatformConnectionPoint.cs rename to src/Wps/Wps.Client/Models/Ows/PlatformConnectionPoint.cs index c725e27..207e1d0 100644 --- a/src/Wps/Wps.Client/Models/PlatformConnectionPoint.cs +++ b/src/Wps/Wps.Client/Models/Ows/PlatformConnectionPoint.cs @@ -1,7 +1,7 @@ using System.Xml.Serialization; using Wps.Client.Utils; -namespace Wps.Client.Models +namespace Wps.Client.Models.Ows { /// /// Connection point URLs for the HTTP DCP. diff --git a/src/Wps/Wps.Client/Models/ProviderSite.cs b/src/Wps/Wps.Client/Models/Ows/ProviderSite.cs similarity index 92% rename from src/Wps/Wps.Client/Models/ProviderSite.cs rename to src/Wps/Wps.Client/Models/Ows/ProviderSite.cs index 5481bd7..81913df 100644 --- a/src/Wps/Wps.Client/Models/ProviderSite.cs +++ b/src/Wps/Wps.Client/Models/Ows/ProviderSite.cs @@ -1,7 +1,7 @@ using System.Xml.Serialization; using Wps.Client.Utils; -namespace Wps.Client.Models +namespace Wps.Client.Models.Ows { [XmlRoot("ProviderSite", Namespace = ModelNamespaces.Ows)] public class ProviderSite diff --git a/src/Wps/Wps.Client/Models/ServiceContact.cs b/src/Wps/Wps.Client/Models/Ows/ServiceContact.cs similarity index 97% rename from src/Wps/Wps.Client/Models/ServiceContact.cs rename to src/Wps/Wps.Client/Models/Ows/ServiceContact.cs index 8510783..49428cb 100644 --- a/src/Wps/Wps.Client/Models/ServiceContact.cs +++ b/src/Wps/Wps.Client/Models/Ows/ServiceContact.cs @@ -1,7 +1,7 @@ using System.Xml.Serialization; using Wps.Client.Utils; -namespace Wps.Client.Models +namespace Wps.Client.Models.Ows { /// /// Information for contacting the service provider. diff --git a/src/Wps/Wps.Client/Models/ServiceIdentification.cs b/src/Wps/Wps.Client/Models/Ows/ServiceIdentification.cs similarity index 98% rename from src/Wps/Wps.Client/Models/ServiceIdentification.cs rename to src/Wps/Wps.Client/Models/Ows/ServiceIdentification.cs index a6408fe..493c047 100644 --- a/src/Wps/Wps.Client/Models/ServiceIdentification.cs +++ b/src/Wps/Wps.Client/Models/Ows/ServiceIdentification.cs @@ -1,7 +1,7 @@ using System.Xml.Serialization; using Wps.Client.Utils; -namespace Wps.Client.Models +namespace Wps.Client.Models.Ows { /// /// General metadata for a specific server. diff --git a/src/Wps/Wps.Client/Models/ServiceProvider.cs b/src/Wps/Wps.Client/Models/Ows/ServiceProvider.cs similarity index 96% rename from src/Wps/Wps.Client/Models/ServiceProvider.cs rename to src/Wps/Wps.Client/Models/Ows/ServiceProvider.cs index f795d33..5e3c9ef 100644 --- a/src/Wps/Wps.Client/Models/ServiceProvider.cs +++ b/src/Wps/Wps.Client/Models/Ows/ServiceProvider.cs @@ -1,7 +1,7 @@ using System.Xml.Serialization; using Wps.Client.Utils; -namespace Wps.Client.Models +namespace Wps.Client.Models.Ows { [XmlRoot("ServiceProvider", Namespace = ModelNamespaces.Ows)] public class ServiceProvider diff --git a/src/Wps/Wps.Client/Models/ValueRange.cs b/src/Wps/Wps.Client/Models/Ows/ValueRange.cs similarity index 88% rename from src/Wps/Wps.Client/Models/ValueRange.cs rename to src/Wps/Wps.Client/Models/Ows/ValueRange.cs index b538b7c..704816b 100644 --- a/src/Wps/Wps.Client/Models/ValueRange.cs +++ b/src/Wps/Wps.Client/Models/Ows/ValueRange.cs @@ -1,8 +1,7 @@ -using System.Xml; -using System.Xml.Serialization; +using System.Xml.Serialization; using Wps.Client.Utils; -namespace Wps.Client.Models +namespace Wps.Client.Models.Ows { [XmlRoot("Range", Namespace = ModelNamespaces.Ows)] public class ValueRange diff --git a/src/Wps/Wps.Client/Models/Responses/GetCapabilitiesResponse.cs b/src/Wps/Wps.Client/Models/Responses/GetCapabilitiesResponse.cs index bb28ecf..e1889a6 100644 --- a/src/Wps/Wps.Client/Models/Responses/GetCapabilitiesResponse.cs +++ b/src/Wps/Wps.Client/Models/Responses/GetCapabilitiesResponse.cs @@ -1,4 +1,5 @@ using System.Xml.Serialization; +using Wps.Client.Models.Ows; using Wps.Client.Utils; namespace Wps.Client.Models.Responses From bc6598162fb98d55e537b8c9e6e25151ab87bf47 Mon Sep 17 00:00:00 2001 From: Mihai Stan Date: Mon, 10 Jun 2019 17:53:07 +0200 Subject: [PATCH 51/76] Add string helper for formattable array concatenation --- src/Wps/Wps.Client.Tests/StringHelperTests.cs | 59 +++++++++++++++++++ src/Wps/Wps.Client/Utils/StringHelper.cs | 33 +++++++++++ 2 files changed, 92 insertions(+) create mode 100644 src/Wps/Wps.Client.Tests/StringHelperTests.cs create mode 100644 src/Wps/Wps.Client/Utils/StringHelper.cs diff --git a/src/Wps/Wps.Client.Tests/StringHelperTests.cs b/src/Wps/Wps.Client.Tests/StringHelperTests.cs new file mode 100644 index 0000000..d4b6230 --- /dev/null +++ b/src/Wps/Wps.Client.Tests/StringHelperTests.cs @@ -0,0 +1,59 @@ +using FluentAssertions; +using System.Globalization; +using System.Threading; +using Wps.Client.Utils; +using Xunit; + +namespace Wps.Client.Tests +{ + public class StringHelperTests + { + + [Fact] + public void CustomFormatJoin_EmptyFormatGiven_With_1_SpaceSeparator_With_5_Doubles_With_InvariantCulture_ShouldGenerateInvariantString() + { + const string expectedResult = "1.1 2.2 3.3 4.4 5.5"; + + var elements = new [] { 1.1, 2.2, 3.3, 4.4, 5.5 }; + var result = StringHelper.CustomFormatJoin(string.Empty, CultureInfo.InvariantCulture, " ", elements); + + result.Should().Be(expectedResult); + } + + [Fact] + public void CustomFormatJoin_NoFormatGiven_With_1_SpaceSeparator_With_5_Doubles_With_InvariantCulture_ShouldGenerateInvariantString() + { + const string expectedResult = "1.1 2.2 3.3 4.4 5.5"; + + var elements = new[] { 1.1, 2.2, 3.3, 4.4, 5.5 }; + var result = StringHelper.CustomFormatJoin(CultureInfo.InvariantCulture, " ", elements); + + result.Should().Be(expectedResult); + } + + [Fact] + public void CustomFormatJoin_NoFormatGiven_With_1_SpaceSeparator_With_5_Doubles_With_GermanCulture_ShouldGenerateCultureVariantString() + { + const string expectedResult = "1,1 2,2 3,3 4,4 5,5"; + + var elements = new[] { 1.1, 2.2, 3.3, 4.4, 5.5 }; + var result = StringHelper.CustomFormatJoin(CultureInfo.CreateSpecificCulture("de-DE"), " ", elements); + + result.Should().Be(expectedResult); + } + + [Fact] + public void CustomFormatJoin_NoFormatGiven_With_1_SpaceSeparator_With_5_Doubles_With_GermanCultureThreadOnThread_ShouldGenerateInvariantString() + { + Thread.CurrentThread.CurrentCulture = CultureInfo.CreateSpecificCulture("de-DE"); + + const string expectedResult = "1.1 2.2 3.3 4.4 5.5"; + + var elements = new[] { 1.1, 2.2, 3.3, 4.4, 5.5 }; + var result = StringHelper.CustomFormatJoin(CultureInfo.InvariantCulture, " ", elements); + + result.Should().Be(expectedResult); + } + + } +} diff --git a/src/Wps/Wps.Client/Utils/StringHelper.cs b/src/Wps/Wps.Client/Utils/StringHelper.cs new file mode 100644 index 0000000..eb5e43c --- /dev/null +++ b/src/Wps/Wps.Client/Utils/StringHelper.cs @@ -0,0 +1,33 @@ +using System; +using System.Linq; +using System.Text; + +namespace Wps.Client.Utils +{ + public static class StringHelper + { + + public static string CustomFormatJoin(string format, IFormatProvider formatProvider, string separator, params T[] elements) where T : IFormattable + { + var stringBuilder = new StringBuilder(); + if (elements.Length > 2) + { + foreach (var el in elements.Take(elements.Length - 1)) + { + stringBuilder.Append(el.ToString(format, formatProvider)); + stringBuilder.Append(separator); + } + } + + stringBuilder.Append(elements.Last().ToString(format, formatProvider)); + + return stringBuilder.ToString(); + } + + public static string CustomFormatJoin(IFormatProvider formatProvider, string separator, params T[] elements) where T : IFormattable + { + return CustomFormatJoin(string.Empty, formatProvider, separator, elements); + } + + } +} From 4da40fcdf1a3f4b77c0a2f0dbb477beac6ff869c Mon Sep 17 00:00:00 2001 From: Mihai Stan Date: Mon, 10 Jun 2019 17:53:52 +0200 Subject: [PATCH 52/76] Use invariant culture for BoundingBox corner values conversion --- .../ModelSerializationTests.cs | 22 ++++++++++--------- .../Models/Execution/BoundingBoxValue.cs | 5 +++-- 2 files changed, 15 insertions(+), 12 deletions(-) diff --git a/src/Wps/Wps.Client.Tests/ModelSerializationTests.cs b/src/Wps/Wps.Client.Tests/ModelSerializationTests.cs index 9375709..6d074a9 100644 --- a/src/Wps/Wps.Client.Tests/ModelSerializationTests.cs +++ b/src/Wps/Wps.Client.Tests/ModelSerializationTests.cs @@ -1,5 +1,7 @@ using FluentAssertions; +using System.Globalization; using System.Text.RegularExpressions; +using System.Threading; using Wps.Client.Models; using Wps.Client.Models.Execution; using Wps.Client.Models.Ows; @@ -24,7 +26,7 @@ public void SerializeGetCapabilitiesRequest_ValidRequestGiven_ShouldPass() { const string expectedXml = @""; - // Remove white spaces and new line characters. They do not change the actual (de)serialization of the XML. + // Remove white spaces and new line characters for XML comparison. var trimmedExpectedXml = Regex.Replace(expectedXml, @"\s+", string.Empty); var request = new GetCapabilitiesRequest() @@ -47,7 +49,7 @@ public void SerializeDescribeProcessRequest_ValidRequestGiven_ShouldPass() id3 "; - // Remove white spaces and new line characters. They do not change the actual (de)serialization of the XML. + // Remove white spaces and new line characters for XML comparison. var trimmedExpectedXml = Regex.Replace(expectedXml, @"\s+", string.Empty); var request = new DescribeProcessRequest() @@ -69,7 +71,7 @@ public void SerializeGetStatusRequest_ValidRequestGiven_ShouldPass() testJobId "; - // Remove white spaces and new line characters. They do not change the actual (de)serialization of the XML. + // Remove white spaces and new line characters for XML comparison. var trimmedExpectedXml = Regex.Replace(expectedXml, @"\s+", string.Empty); var request = new GetStatusRequest @@ -90,7 +92,7 @@ public void SerializeGetResultRequest_ValidRequestGiven_ShouldPass() testJobId "; - // Remove white spaces and new line characters. They do not change the actual (de)serialization of the XML. + // Remove white spaces and new line characters for XML comparison. var trimmedExpectedXml = Regex.Replace(expectedXml, @"\s+", string.Empty); var request = new GetResultRequest() @@ -114,7 +116,7 @@ public void SerializeValueRange_ValidRangeGiven_ShouldPass() 10 "; - // Remove white spaces and new line characters. They do not change the actual (de)serialization of the XML. + // Remove white spaces and new line characters for XML comparison. var trimmedExpectedXml = Regex.Replace(expectedXml, @"\s+", string.Empty); var range = new ValueRange @@ -135,7 +137,7 @@ public void SerializeDataInput_ValidInputGiven_ShouldPass() { const string expectedXml = @"105"; - // Remove white spaces and new line characters. They do not change the actual (de)serialization of the XML. + // Remove white spaces and new line characters for XML comparison. var trimmedExpectedXml = Regex.Replace(expectedXml, @"\s+", string.Empty); var dataInput = new DataInput @@ -162,7 +164,7 @@ public void SerializeDataOutput_ValidOutputGiven_ShouldPass() { const string expectedXml = @""; - // Remove white spaces and new line characters. They do not change the actual (de)serialization of the XML. + // Remove white spaces and new line characters for XML comparison. var trimmedExpectedXml = Regex.Replace(expectedXml, @"\s+", string.Empty); var dataInput = new DataOutput() @@ -184,7 +186,7 @@ public void SerializeExecuteRequest_ValidRequestGiven_ShouldPass() { const string expectedXml = @"org.n52.wps.server.algorithm.SimpleBufferAlgorithm0.05"; - // Remove white spaces and new line characters. They do not change the actual (de)serialization of the XML. + // Remove white spaces and new line characters for XML comparison. var trimmedExpectedXml = Regex.Replace(expectedXml, @"\s+", string.Empty); var executeRequest = new ExecuteRequest @@ -232,7 +234,7 @@ public void SerializeBoundingBoxData_ValidObjectsGiven_ShouldPass() { const string expectedXml = @"test-uri-1test-uri-1test-uri-1"; - // Remove white spaces and new line characters. They do not change the actual (de)serialization of the XML. + // Remove white spaces and new line characters for XML comparison. var trimmedExpectedXml = Regex.Replace(expectedXml, @"\s+", string.Empty); var boundingBoxData = new BoundingBoxData @@ -265,7 +267,7 @@ public void SerializeBoundingBoxValue_ValidObjectsGiven_ShouldPass() 3.1 2.2 1.3 "; - // Remove white spaces and new line characters. They do not change the actual (de)serialization of the XML. + // Remove white spaces and new line characters for XML comparison. var trimmedExpectedXml = Regex.Replace(expectedXml, @"\s+", string.Empty); var boundingBoxData = new BoundingBoxValue diff --git a/src/Wps/Wps.Client/Models/Execution/BoundingBoxValue.cs b/src/Wps/Wps.Client/Models/Execution/BoundingBoxValue.cs index 0396901..cc1e352 100644 --- a/src/Wps/Wps.Client/Models/Execution/BoundingBoxValue.cs +++ b/src/Wps/Wps.Client/Models/Execution/BoundingBoxValue.cs @@ -1,4 +1,5 @@ using System; +using System.Globalization; using System.Xml; using System.Xml.Schema; using System.Xml.Serialization; @@ -50,13 +51,13 @@ public void WriteXml(XmlWriter writer) if (LowerCornerPoints != null) { - var content = string.Join(" ", LowerCornerPoints); + var content = StringHelper.CustomFormatJoin(CultureInfo.InvariantCulture, " ", LowerCornerPoints); writer.WriteElementString("ows", "LowerCorner", ModelNamespaces.Ows, content); } if (UpperCornerPoints != null) { - var content = string.Join(" ", UpperCornerPoints); + var content = StringHelper.CustomFormatJoin(CultureInfo.InvariantCulture, " ", UpperCornerPoints); writer.WriteElementString("ows", "UpperCorner", ModelNamespaces.Ows, content); } } From 41359745e88286a08424f1bdd89424274e1bd9e2 Mon Sep 17 00:00:00 2001 From: Mihai Stan Date: Mon, 10 Jun 2019 19:20:55 +0200 Subject: [PATCH 53/76] Use enumeration type for the job status instead of raw string --- .../Wps.Client.Tests/ModelDeserializationTests.cs | 4 ++-- src/Wps/Wps.Client/Models/JobStatus.cs | 14 ++++++++++++++ src/Wps/Wps.Client/Models/StatusInfo.cs | 4 ++-- 3 files changed, 18 insertions(+), 4 deletions(-) create mode 100644 src/Wps/Wps.Client/Models/JobStatus.cs diff --git a/src/Wps/Wps.Client.Tests/ModelDeserializationTests.cs b/src/Wps/Wps.Client.Tests/ModelDeserializationTests.cs index aa481ff..8183dbf 100644 --- a/src/Wps/Wps.Client.Tests/ModelDeserializationTests.cs +++ b/src/Wps/Wps.Client.Tests/ModelDeserializationTests.cs @@ -576,7 +576,7 @@ public void DeserializeStatusInfo_ValidXmlGiven_ShouldPass() const string xml = @" test-id - test-status + Failed 2019-05-20T20:20:20Z 2019-05-20T20:20:20Z 2019-05-20T20:20:20Z @@ -585,7 +585,7 @@ public void DeserializeStatusInfo_ValidXmlGiven_ShouldPass() var expectedDateTime = new DateTime(2019, 5, 20, 20, 20, 20); var response = _serializer.Deserialize(xml); - response.Status.Should().Be("test-status"); + response.Status.Should().Be(JobStatus.Failed); response.JobId.Should().Be("test-id"); response.EstimatedCompletion.Should().Be(expectedDateTime); response.NextPollDateTime.Should().Be(expectedDateTime); diff --git a/src/Wps/Wps.Client/Models/JobStatus.cs b/src/Wps/Wps.Client/Models/JobStatus.cs new file mode 100644 index 0000000..6a9839a --- /dev/null +++ b/src/Wps/Wps.Client/Models/JobStatus.cs @@ -0,0 +1,14 @@ +using System.Xml.Serialization; + +namespace Wps.Client.Models +{ + public enum JobStatus + { + + [XmlEnum("Succeeded")] Succeeded, + [XmlEnum("Failed")] Failed, + [XmlEnum("Accepted")] Accepted, + [XmlEnum("Running")] Running + + } +} diff --git a/src/Wps/Wps.Client/Models/StatusInfo.cs b/src/Wps/Wps.Client/Models/StatusInfo.cs index b315243..f3b5aba 100644 --- a/src/Wps/Wps.Client/Models/StatusInfo.cs +++ b/src/Wps/Wps.Client/Models/StatusInfo.cs @@ -20,8 +20,8 @@ public class StatusInfo /// /// Well-known identifier describing the status of the job. /// - [XmlElement("Status", Namespace = ModelNamespaces.Wps)] - public string Status { get; set; } + [XmlElement("Status", Namespace = ModelNamespaces.Wps, Type = typeof(JobStatus))] + public JobStatus Status { get; set; } /// /// Date and time by which the job and its results will be no longer accessible. From da32ee4aa0e68bf5b4412e8be564be49ada1f70f Mon Sep 17 00:00:00 2001 From: Mihai Stan Date: Tue, 11 Jun 2019 15:07:14 +0200 Subject: [PATCH 54/76] Deserialize result output data to generic type --- .../ModelDeserializationTests.cs | 28 +++++----- src/Wps/Wps.Client/Models/Result.cs | 4 +- src/Wps/Wps.Client/Models/ResultOutput.cs | 51 +++++++++++++++++-- 3 files changed, 65 insertions(+), 18 deletions(-) diff --git a/src/Wps/Wps.Client.Tests/ModelDeserializationTests.cs b/src/Wps/Wps.Client.Tests/ModelDeserializationTests.cs index 8183dbf..fde7786 100644 --- a/src/Wps/Wps.Client.Tests/ModelDeserializationTests.cs +++ b/src/Wps/Wps.Client.Tests/ModelDeserializationTests.cs @@ -597,18 +597,23 @@ public void DeserializeStatusInfo_ValidXmlGiven_ShouldPass() public void DeserializeResultOutput_ValidXmlGiven_ShouldPass() { const string xml = @" - - - + + 123 - + + + 321 + + "; var expectedDateTime = new DateTime(2019, 5, 20, 20, 20, 20); - var resultOutput = _serializer.Deserialize(xml); - resultOutput.Data.Should().NotBeNull(); + var resultOutput = _serializer.Deserialize>(xml); + resultOutput.Data.Should().Be(123); resultOutput.Id.Should().Be("result"); - resultOutput.Output.Should().NotBeNull(); + resultOutput.Output.Id.Should().Be("test"); + resultOutput.Output.Data.Should().Be(321); + resultOutput.Output.Output.Should().BeNull(); } [Fact] @@ -616,18 +621,17 @@ public void DeserializeResult_ValidXmlGiven_ShouldPass() { const string xml = @" - test-id - - - + + 123 + test-id 2019-05-20T20:20:20Z "; var expectedExpirationDate = new DateTime(2019, 5, 20, 20, 20, 20); - var result = _serializer.Deserialize(xml); + var result = _serializer.Deserialize>(xml); result.ExpirationDate.Should().Be(expectedExpirationDate); result.JobId.Should().Be("test-id"); result.Outputs.Should().HaveCount(1); diff --git a/src/Wps/Wps.Client/Models/Result.cs b/src/Wps/Wps.Client/Models/Result.cs index f8fa619..d9779fd 100644 --- a/src/Wps/Wps.Client/Models/Result.cs +++ b/src/Wps/Wps.Client/Models/Result.cs @@ -6,7 +6,7 @@ namespace Wps.Client.Models { [XmlRoot("Result", Namespace = ModelNamespaces.Wps)] - public class Result + public class Result { /// @@ -25,7 +25,7 @@ public class Result /// Output item returned by a process execution. /// [XmlElement("Output", Namespace = ModelNamespaces.Wps)] - public ResultOutput[] Outputs { get; set; } + public ResultOutput[] Outputs { get; set; } } } diff --git a/src/Wps/Wps.Client/Models/ResultOutput.cs b/src/Wps/Wps.Client/Models/ResultOutput.cs index 557bb49..959d0e4 100644 --- a/src/Wps/Wps.Client/Models/ResultOutput.cs +++ b/src/Wps/Wps.Client/Models/ResultOutput.cs @@ -1,10 +1,13 @@ -using System.Xml.Serialization; +using System.Xml; +using System.Xml.Schema; +using System.Xml.Serialization; +using Wps.Client.Services; using Wps.Client.Utils; namespace Wps.Client.Models { [XmlRoot("Output", Namespace = ModelNamespaces.Wps)] - public class ResultOutput + public class ResultOutput : IXmlSerializable { /// @@ -17,13 +20,53 @@ public class ResultOutput /// The data provided by this output item. /// [XmlElement("Data", Namespace = ModelNamespaces.Wps)] - public object Data { get; set; } + public TData Data { get; set; } /// /// Nested output, child element. /// [XmlElement("Output", Namespace = ModelNamespaces.Wps)] - public ResultOutput Output { get; set; } + public ResultOutput Output { get; set; } + public XmlSchema GetSchema() + { + return null; + } + + public void ReadXml(XmlReader reader) + { + var serializer = new XmlSerializationService(); + Id = reader.GetAttribute("id"); + + var subtreeReader = reader.ReadSubtree(); + subtreeReader.MoveToContent(); + while(subtreeReader.Read()) + { + if (subtreeReader.NodeType == XmlNodeType.Element) + { + if (subtreeReader.LocalName.Equals("Data")) + { + var content = subtreeReader.ReadInnerXml(); + Data = serializer.Deserialize(content); + } + + if (subtreeReader.LocalName.Equals("Output")) + { + if(subtreeReader.Depth == 1) + { + var content = subtreeReader.ReadOuterXml(); + Output = serializer.Deserialize>(content); + } + } + } + } + + reader.Skip(); + } + + public void WriteXml(XmlWriter writer) + { + throw new System.NotImplementedException(); + } } } From f768eeffb6632cdcd6a59326e66ab420899abc24 Mon Sep 17 00:00:00 2001 From: Mihai Stan Date: Tue, 11 Jun 2019 16:16:52 +0200 Subject: [PATCH 55/76] Use invariant culture in LiteralDataValue for formattable objects --- .../Wps.Client/Models/Execution/LiteralDataValue.cs | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/Wps/Wps.Client/Models/Execution/LiteralDataValue.cs b/src/Wps/Wps.Client/Models/Execution/LiteralDataValue.cs index 15c1949..090bc08 100644 --- a/src/Wps/Wps.Client/Models/Execution/LiteralDataValue.cs +++ b/src/Wps/Wps.Client/Models/Execution/LiteralDataValue.cs @@ -1,4 +1,5 @@ using System; +using System.Globalization; using System.Xml; using System.Xml.Schema; using System.Xml.Serialization; @@ -24,7 +25,14 @@ public void ReadXml(XmlReader reader) public void WriteXml(XmlWriter writer) { - writer.WriteRaw(Value.ToString()); + if (Value is IFormattable formattableValue) + { + writer.WriteRaw(formattableValue.ToString(string.Empty, CultureInfo.InvariantCulture)); + } + else + { + writer.WriteRaw(Value.ToString()); + } } } } From 67ae1daaf488740043cb17df3fdf66cd35f54eb4 Mon Sep 17 00:00:00 2001 From: Mihai Stan Date: Tue, 11 Jun 2019 17:33:41 +0200 Subject: [PATCH 56/76] Implement OWS exception model with deserialization tests --- .../ModelDeserializationTests.cs | 34 +++++++++++ .../Wps.Client/Models/Ows/ExceptionReport.cs | 20 +++++++ src/Wps/Wps.Client/Models/Ows/OwsException.cs | 56 +++++++++++++++++++ 3 files changed, 110 insertions(+) create mode 100644 src/Wps/Wps.Client/Models/Ows/ExceptionReport.cs create mode 100644 src/Wps/Wps.Client/Models/Ows/OwsException.cs diff --git a/src/Wps/Wps.Client.Tests/ModelDeserializationTests.cs b/src/Wps/Wps.Client.Tests/ModelDeserializationTests.cs index fde7786..50ea28a 100644 --- a/src/Wps/Wps.Client.Tests/ModelDeserializationTests.cs +++ b/src/Wps/Wps.Client.Tests/ModelDeserializationTests.cs @@ -686,5 +686,39 @@ public void DeserializeBoundingBoxData_ValidXmlGiven_ShouldPass() boundingBoxData.Formats.Should().HaveCount(2); } + + + [Fact] + public void DeserializeOwsExceptionReport_ValidXmlGiven_ShouldPass() + { + const string xml = @" + + + + +"; + + var report = _serializer.Deserialize(xml); + report.Exceptions.Should().HaveCount(3); + report.Language.Should().Be("en-US"); + report.Version.Should().Be("2.0.0"); + } + + [Fact] + public void DeserializeOwsException_ValidXmlGiven_ShouldPass() + { + const string xml = @" + + There went something wrong with parsing the POST data: XML document structures must start and end within the same entity. +"; + + var exception = _serializer.Deserialize(xml); + exception.Code.Should().Be("NoApplicableCode"); + exception.Locator.Should().Be("test locator"); + exception.Message.Should() + .Be( + "There went something wrong with parsing the POST data: XML document structures must start and end within the same entity."); + } + } } diff --git a/src/Wps/Wps.Client/Models/Ows/ExceptionReport.cs b/src/Wps/Wps.Client/Models/Ows/ExceptionReport.cs new file mode 100644 index 0000000..8d24c79 --- /dev/null +++ b/src/Wps/Wps.Client/Models/Ows/ExceptionReport.cs @@ -0,0 +1,20 @@ +using System.Xml.Serialization; +using Wps.Client.Utils; + +namespace Wps.Client.Models.Ows +{ + [XmlRoot("ExceptionReport", Namespace = ModelNamespaces.Ows)] + public class ExceptionReport + { + + [XmlAttribute("version", Namespace = ModelNamespaces.Ows)] + public string Version { get; set; } + + [XmlAttribute("lang", Namespace = ModelNamespaces.Ows)] + public string Language { get; set; } + + [XmlElement("Exception", Namespace = ModelNamespaces.Ows)] + public OwsException[] Exceptions { get; set; } + + } +} diff --git a/src/Wps/Wps.Client/Models/Ows/OwsException.cs b/src/Wps/Wps.Client/Models/Ows/OwsException.cs new file mode 100644 index 0000000..35d559e --- /dev/null +++ b/src/Wps/Wps.Client/Models/Ows/OwsException.cs @@ -0,0 +1,56 @@ +using System; +using System.Xml; +using System.Xml.Schema; +using System.Xml.Serialization; +using Wps.Client.Services; +using Wps.Client.Utils; + +namespace Wps.Client.Models.Ows +{ + [XmlRoot("Exception", Namespace = ModelNamespaces.Ows)] + public class OwsException : Exception, IXmlSerializable + { + + //[XmlAttribute("exceptionCode", Namespace = ModelNamespaces.Ows)] + public string Code { get; set; } + + //[XmlAttribute("locator", Namespace = ModelNamespaces.Ows)] + public string Locator { get; set; } + + private string _message; + + public override string Message => _message; + + public XmlSchema GetSchema() + { + return null; + } + + public void ReadXml(XmlReader reader) + { + var serializer = new XmlSerializationService(); + Code = reader.GetAttribute("exceptionCode"); + Locator = reader.GetAttribute("locator"); + + var subtreeReader = reader.ReadSubtree(); + subtreeReader.MoveToContent(); + while (subtreeReader.Read()) + { + if (subtreeReader.NodeType == XmlNodeType.Element) + { + if (subtreeReader.LocalName.Equals("ExceptionText")) + { + _message = subtreeReader.ReadElementContentAsString(); + } + } + } + + reader.Skip(); + } + + public void WriteXml(XmlWriter writer) + { + throw new NotImplementedException(); + } + } +} From da91c3a2a79a994f95b2a5cddba56d9c4dddd37b Mon Sep 17 00:00:00 2001 From: Mihai Stan Date: Wed, 12 Jun 2019 13:51:39 +0200 Subject: [PATCH 57/76] Make literal data value as string Previously it was an object type and it was serialized to a string depending on culture. It wasn't coherent with the WPS standard. --- .../ModelSerializationTests.cs | 26 ++++---- src/Wps/Wps.Client.Tests/StringHelperTests.cs | 59 ------------------- .../Models/Execution/BoundingBoxValue.cs | 5 +- .../Models/Execution/LiteralDataValue.cs | 20 ++----- src/Wps/Wps.Client/Utils/StringHelper.cs | 33 ----------- 5 files changed, 22 insertions(+), 121 deletions(-) delete mode 100644 src/Wps/Wps.Client.Tests/StringHelperTests.cs delete mode 100644 src/Wps/Wps.Client/Utils/StringHelper.cs diff --git a/src/Wps/Wps.Client.Tests/ModelSerializationTests.cs b/src/Wps/Wps.Client.Tests/ModelSerializationTests.cs index 6d074a9..630f70e 100644 --- a/src/Wps/Wps.Client.Tests/ModelSerializationTests.cs +++ b/src/Wps/Wps.Client.Tests/ModelSerializationTests.cs @@ -150,7 +150,7 @@ public void SerializeDataInput_ValidInputGiven_ShouldPass() }, Data = new LiteralDataValue { - Value = 105 + Value = 105.ToString(CultureInfo.InvariantCulture) } }; @@ -210,7 +210,7 @@ public void SerializeExecuteRequest_ValidRequestGiven_ShouldPass() Identifier = "width", Data = new LiteralDataValue { - Value = 0.05f + Value = 0.05f.ToString(CultureInfo.InvariantCulture) } } }, @@ -257,14 +257,16 @@ public void SerializeBoundingBoxData_ValidObjectsGiven_ShouldPass() trimmedResult.Should().Be(trimmedExpectedXml); } - [Fact] - public void SerializeBoundingBoxValue_ValidObjectsGiven_ShouldPass() + [Theory] + [InlineData("crs-uri", 3, new[] { 1.1, 2.2, 3.3 }, new[] { 3.1, 2.2, 1.3 })] + public void SerializeBoundingBoxValue_ValidObjectsGiven_ShouldPass(string crsUri, int dimensionCount, double[] lCornerPoints, double[] rCornerPoints) { - const string expectedXml = @" - + Thread.CurrentThread.CurrentCulture = CultureInfo.CreateSpecificCulture("de-DE"); + + var expectedXml = $@" - 1.1 2.2 3.3 - 3.1 2.2 1.3 + {string.Join(" ", lCornerPoints)} + {string.Join(" ", rCornerPoints)} "; // Remove white spaces and new line characters for XML comparison. @@ -272,10 +274,10 @@ public void SerializeBoundingBoxValue_ValidObjectsGiven_ShouldPass() var boundingBoxData = new BoundingBoxValue { - CrsUri = "crs-uri", - DimensionCount = 3, - LowerCornerPoints = new[] { 1.1, 2.2, 3.3 }, - UpperCornerPoints = new[] { 3.1, 2.2, 1.3 }, + CrsUri = crsUri, + DimensionCount = dimensionCount, + LowerCornerPoints = lCornerPoints, + UpperCornerPoints = rCornerPoints, }; var resultXml = _serializer.Serialize(boundingBoxData); diff --git a/src/Wps/Wps.Client.Tests/StringHelperTests.cs b/src/Wps/Wps.Client.Tests/StringHelperTests.cs deleted file mode 100644 index d4b6230..0000000 --- a/src/Wps/Wps.Client.Tests/StringHelperTests.cs +++ /dev/null @@ -1,59 +0,0 @@ -using FluentAssertions; -using System.Globalization; -using System.Threading; -using Wps.Client.Utils; -using Xunit; - -namespace Wps.Client.Tests -{ - public class StringHelperTests - { - - [Fact] - public void CustomFormatJoin_EmptyFormatGiven_With_1_SpaceSeparator_With_5_Doubles_With_InvariantCulture_ShouldGenerateInvariantString() - { - const string expectedResult = "1.1 2.2 3.3 4.4 5.5"; - - var elements = new [] { 1.1, 2.2, 3.3, 4.4, 5.5 }; - var result = StringHelper.CustomFormatJoin(string.Empty, CultureInfo.InvariantCulture, " ", elements); - - result.Should().Be(expectedResult); - } - - [Fact] - public void CustomFormatJoin_NoFormatGiven_With_1_SpaceSeparator_With_5_Doubles_With_InvariantCulture_ShouldGenerateInvariantString() - { - const string expectedResult = "1.1 2.2 3.3 4.4 5.5"; - - var elements = new[] { 1.1, 2.2, 3.3, 4.4, 5.5 }; - var result = StringHelper.CustomFormatJoin(CultureInfo.InvariantCulture, " ", elements); - - result.Should().Be(expectedResult); - } - - [Fact] - public void CustomFormatJoin_NoFormatGiven_With_1_SpaceSeparator_With_5_Doubles_With_GermanCulture_ShouldGenerateCultureVariantString() - { - const string expectedResult = "1,1 2,2 3,3 4,4 5,5"; - - var elements = new[] { 1.1, 2.2, 3.3, 4.4, 5.5 }; - var result = StringHelper.CustomFormatJoin(CultureInfo.CreateSpecificCulture("de-DE"), " ", elements); - - result.Should().Be(expectedResult); - } - - [Fact] - public void CustomFormatJoin_NoFormatGiven_With_1_SpaceSeparator_With_5_Doubles_With_GermanCultureThreadOnThread_ShouldGenerateInvariantString() - { - Thread.CurrentThread.CurrentCulture = CultureInfo.CreateSpecificCulture("de-DE"); - - const string expectedResult = "1.1 2.2 3.3 4.4 5.5"; - - var elements = new[] { 1.1, 2.2, 3.3, 4.4, 5.5 }; - var result = StringHelper.CustomFormatJoin(CultureInfo.InvariantCulture, " ", elements); - - result.Should().Be(expectedResult); - } - - } -} diff --git a/src/Wps/Wps.Client/Models/Execution/BoundingBoxValue.cs b/src/Wps/Wps.Client/Models/Execution/BoundingBoxValue.cs index cc1e352..0396901 100644 --- a/src/Wps/Wps.Client/Models/Execution/BoundingBoxValue.cs +++ b/src/Wps/Wps.Client/Models/Execution/BoundingBoxValue.cs @@ -1,5 +1,4 @@ using System; -using System.Globalization; using System.Xml; using System.Xml.Schema; using System.Xml.Serialization; @@ -51,13 +50,13 @@ public void WriteXml(XmlWriter writer) if (LowerCornerPoints != null) { - var content = StringHelper.CustomFormatJoin(CultureInfo.InvariantCulture, " ", LowerCornerPoints); + var content = string.Join(" ", LowerCornerPoints); writer.WriteElementString("ows", "LowerCorner", ModelNamespaces.Ows, content); } if (UpperCornerPoints != null) { - var content = StringHelper.CustomFormatJoin(CultureInfo.InvariantCulture, " ", UpperCornerPoints); + var content = string.Join(" ", UpperCornerPoints); writer.WriteElementString("ows", "UpperCorner", ModelNamespaces.Ows, content); } } diff --git a/src/Wps/Wps.Client/Models/Execution/LiteralDataValue.cs b/src/Wps/Wps.Client/Models/Execution/LiteralDataValue.cs index 090bc08..531b4f1 100644 --- a/src/Wps/Wps.Client/Models/Execution/LiteralDataValue.cs +++ b/src/Wps/Wps.Client/Models/Execution/LiteralDataValue.cs @@ -1,6 +1,4 @@ -using System; -using System.Globalization; -using System.Xml; +using System.Xml; using System.Xml.Schema; using System.Xml.Serialization; using Wps.Client.Utils; @@ -11,28 +9,22 @@ namespace Wps.Client.Models.Execution public class LiteralDataValue : IXmlSerializable { - public object Value { get; set; } + public string Value { get; set; } public XmlSchema GetSchema() { return null; } - public void ReadXml(XmlReader reader) + public virtual void ReadXml(XmlReader reader) { - throw new NotImplementedException(); + Value = reader.ReadElementContentAsString(); } public void WriteXml(XmlWriter writer) { - if (Value is IFormattable formattableValue) - { - writer.WriteRaw(formattableValue.ToString(string.Empty, CultureInfo.InvariantCulture)); - } - else - { - writer.WriteRaw(Value.ToString()); - } + writer.WriteRaw(Value); } } + } diff --git a/src/Wps/Wps.Client/Utils/StringHelper.cs b/src/Wps/Wps.Client/Utils/StringHelper.cs deleted file mode 100644 index eb5e43c..0000000 --- a/src/Wps/Wps.Client/Utils/StringHelper.cs +++ /dev/null @@ -1,33 +0,0 @@ -using System; -using System.Linq; -using System.Text; - -namespace Wps.Client.Utils -{ - public static class StringHelper - { - - public static string CustomFormatJoin(string format, IFormatProvider formatProvider, string separator, params T[] elements) where T : IFormattable - { - var stringBuilder = new StringBuilder(); - if (elements.Length > 2) - { - foreach (var el in elements.Take(elements.Length - 1)) - { - stringBuilder.Append(el.ToString(format, formatProvider)); - stringBuilder.Append(separator); - } - } - - stringBuilder.Append(elements.Last().ToString(format, formatProvider)); - - return stringBuilder.ToString(); - } - - public static string CustomFormatJoin(IFormatProvider formatProvider, string separator, params T[] elements) where T : IFormattable - { - return CustomFormatJoin(string.Empty, formatProvider, separator, elements); - } - - } -} From a6a6814e94da1dae34af02ebf1a5dfb40a625632 Mon Sep 17 00:00:00 2001 From: Mihai Stan Date: Wed, 12 Jun 2019 16:26:41 +0200 Subject: [PATCH 58/76] Make the GetCapabilities request inheirt RequesTBase --- .../Wps.Client/Models/Requests/GetCapabilitiesRequest.cs | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/src/Wps/Wps.Client/Models/Requests/GetCapabilitiesRequest.cs b/src/Wps/Wps.Client/Models/Requests/GetCapabilitiesRequest.cs index 21531e5..264c3d9 100644 --- a/src/Wps/Wps.Client/Models/Requests/GetCapabilitiesRequest.cs +++ b/src/Wps/Wps.Client/Models/Requests/GetCapabilitiesRequest.cs @@ -6,12 +6,8 @@ namespace Wps.Client.Models.Requests { [Serializable] [XmlRoot("GetCapabilities", Namespace = ModelNamespaces.Wps)] - public class GetCapabilitiesRequest + public class GetCapabilitiesRequest : RequestBase { - /// - /// The concerned service by the GetCapabilities request. - /// - [XmlAttribute("service", Namespace = ModelNamespaces.Wps)] - public string Service { get; set; } = "WPS"; + } } \ No newline at end of file From a05489cdc34a725a21490ae26721821c5d3b050f Mon Sep 17 00:00:00 2001 From: Mihai Stan Date: Wed, 12 Jun 2019 16:29:21 +0200 Subject: [PATCH 59/76] Add embedded resource attribute to load files during tests --- .../EmbeddedResourceDataAttribute.cs | 46 +++++++++++++++++++ 1 file changed, 46 insertions(+) create mode 100644 src/Wps/Wps.Client.Tests/EmbeddedResourceDataAttribute.cs diff --git a/src/Wps/Wps.Client.Tests/EmbeddedResourceDataAttribute.cs b/src/Wps/Wps.Client.Tests/EmbeddedResourceDataAttribute.cs new file mode 100644 index 0000000..53cbd46 --- /dev/null +++ b/src/Wps/Wps.Client.Tests/EmbeddedResourceDataAttribute.cs @@ -0,0 +1,46 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Reflection; +using Xunit.Sdk; + +namespace Wps.Client.Tests +{ + /// Source: https://www.patriksvensson.se/2017/11/using-embedded-resources-in-xunit-tests + public sealed class EmbeddedResourceDataAttribute : DataAttribute + { + private readonly string[] _args; + + public EmbeddedResourceDataAttribute(params string[] args) + { + _args = args; + } + + public override IEnumerable GetData(MethodInfo testMethod) + { + var result = new object[_args.Length]; + for (var index = 0; index < _args.Length; index++) + { + result[index] = ReadManifestData(_args[index]); + } + return new[] { result }; + } + + public static string ReadManifestData(string resourceName) + { + var assembly = typeof(EmbeddedResourceDataAttribute).GetTypeInfo().Assembly; + resourceName = resourceName.Replace("/", "."); + using (var stream = assembly.GetManifestResourceStream(resourceName)) + { + if (stream == null) + { + throw new InvalidOperationException("Could not load manifest resource stream."); + } + using (var reader = new StreamReader(stream)) + { + return reader.ReadToEnd(); + } + } + } + } +} From 99cc0ba15fbe6d597f9e0acfc4b7c0eb5e1d074b Mon Sep 17 00:00:00 2001 From: Mihai Stan Date: Wed, 12 Jun 2019 16:45:51 +0200 Subject: [PATCH 60/76] Add embedded resource attribute to load files during tests --- .../Resources/ExceptionReport.xml | 63 + .../Resources/GetCapabilities.xml | 1185 +++++++++++++++++ .../Resources/ProcessOfferings.xml | 244 ++++ .../Wps.Client.Tests/Wps.Client.Tests.csproj | 12 +- 4 files changed, 1502 insertions(+), 2 deletions(-) create mode 100644 src/Wps/Wps.Client.Tests/Resources/ExceptionReport.xml create mode 100644 src/Wps/Wps.Client.Tests/Resources/GetCapabilities.xml create mode 100644 src/Wps/Wps.Client.Tests/Resources/ProcessOfferings.xml diff --git a/src/Wps/Wps.Client.Tests/Resources/ExceptionReport.xml b/src/Wps/Wps.Client.Tests/Resources/ExceptionReport.xml new file mode 100644 index 0000000..ccb3bd7 --- /dev/null +++ b/src/Wps/Wps.Client.Tests/Resources/ExceptionReport.xml @@ -0,0 +1,63 @@ + + + + Algorithm does not exist: alsfgdgsdfsfgdl + + + + org.n52.wps.server.ExceptionReport: Algorithm does not exist: alsfgdgsdfsfgdl + at org.n52.wps.server.request.DescribeProcessRequestV200.call(DescribeProcessRequestV200.java:170) + at org.n52.wps.server.handler.RequestHandler.handle(RequestHandler.java:428) + at org.n52.wps.server.WebProcessingService.doPost(WebProcessingService.java:311) + at sun.reflect.GeneratedMethodAccessor676.invoke(Unknown Source) + at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source) + at java.lang.reflect.Method.invoke(Unknown Source) + at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:221) + at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:137) + at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:110) + at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandleMethod(RequestMappingHandlerAdapter.java:776) + at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:705) + at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:85) + at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:959) + at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:893) + at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:966) + at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:868) + at javax.servlet.http.HttpServlet.service(HttpServlet.java:661) + at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:842) + at javax.servlet.http.HttpServlet.service(HttpServlet.java:742) + at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:231) + at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) + at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52) + at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) + at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) + at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:207) + at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:176) + at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:344) + at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:261) + at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) + at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) + at com.thetransactioncompany.cors.CORSFilter.doFilter(CORSFilter.java:169) + at com.thetransactioncompany.cors.CORSFilter.doFilter(CORSFilter.java:232) + at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) + at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) + at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:198) + at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:96) + at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:493) + at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:140) + at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:81) + at org.apache.catalina.valves.AbstractAccessLogValve.invoke(AbstractAccessLogValve.java:650) + at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:87) + at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:342) + at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:800) + at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:66) + at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:806) + at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1498) + at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49) + at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source) + at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source) + at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) + at java.lang.Thread.run(Unknown Source) + + + + \ No newline at end of file diff --git a/src/Wps/Wps.Client.Tests/Resources/GetCapabilities.xml b/src/Wps/Wps.Client.Tests/Resources/GetCapabilities.xml new file mode 100644 index 0000000..26d11d3 --- /dev/null +++ b/src/Wps/Wps.Client.Tests/Resources/GetCapabilities.xml @@ -0,0 +1,1185 @@ + + + + 52°North WPS 4.0.0-beta.4-SNAPSHOT + Service based on the 52°North implementation of WPS 1.0.0 and 2.0.0 + + WPS + geospatial + geoprocessing + + WPS + 1.0.0 + 2.0.0 + NONE + NONE + + + 52°North GmbH + + + Benjamin Pross + + + Martin-Luther-King-Weg 24 + Muenster + NW + 48155 + Germany + b.pross @52north.org + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + en-US + + + + R Annotation Validation + org.n52.wps.server.algorithm.r.AnnotationValidation + + + + org.n52.wps.server.algorithm.JTSConvexHullAlgorithm + org.n52.wps.server.algorithm.JTSConvexHullAlgorithm + + + + org.n52.wps.server.algorithm.SimpleBufferAlgorithm + org.n52.wps.server.algorithm.SimpleBufferAlgorithm + + + + org.n52.wps.server.algorithm.convexhull.ConvexHullAlgorithm + org.n52.wps.server.algorithm.convexhull.ConvexHullAlgorithm + + + + org.n52.wps.server.algorithm.intersection.IntersectionAlgorithm + org.n52.wps.server.algorithm.intersection.IntersectionAlgorithm + + + + org.n52.wps.server.algorithm.test.DummyTestClass + org.n52.wps.server.algorithm.test.DummyTestClass + + + + Echo process + org.n52.wps.server.algorithm.test.EchoProcess + + + + org.n52.wps.server.algorithm.test.LongRunningDummyTestClass + org.n52.wps.server.algorithm.test.LongRunningDummyTestClass + + + + for testing multiple binary inputs by reference + org.n52.wps.server.algorithm.test.MultiReferenceBinaryInputAlgorithm + + + + for testing multiple inputs by reference + org.n52.wps.server.algorithm.test.MultiReferenceInputAlgorithm + + + + org.n52.wps.server.algorithm.test.MultipleComplexInAndOutputsDummyTestClass + org.n52.wps.server.algorithm.test.MultipleComplexInAndOutputsDummyTestClass + + + + 6S - Second Simulation of Satellite Signal in the Solar Spectrum. + i.atcorr + + + + Computes biomass growth, precursor of crop yield calculation. + i.biomass + + + + Canonical components analysis (CCA) program for image processing. + i.cca + + + + The resulting signature file is used as input for i.maxlik, to generate an unsupervised image classification. + i.cluster + + + + Actual evapotranspiration for diurnal period (Bastiaanssen, 1995). + i.eb.eta + + + + Computes evaporative fraction (Bastiaanssen, 1995) and root zone soil moisture (Makin, Molden and Bastiaanssen, 2001). + i.eb.evapfr + + + + Soil heat flux approximation (Bastiaanssen, 1995). + i.eb.soilheatflux + + + + Computes emissivity from NDVI, generic method for sparse land. + i.emissivity + + + + Fast Fourier Transform (FFT) for image processing. + i.fft + + + + Generates statistics for i.maxlik from raster map. + i.gensig + + + + Generates statistics for i.smap from raster map. + i.gensigset + + + + Transforms raster maps from HIS (Hue-Intensity-Saturation) color space to RGB (Red-Green-Blue) color space. + i.his.rgb + + + + Inverse Fast Fourier Transform (IFFT) for image processing. + i.ifft + + + + Performs Landsat TM/ETM+ Automatic Cloud Cover Assessment (ACCA). + i.landsat.acca + + + + Calculates top-of-atmosphere radiance or reflectance and temperature for Landsat MSS/TM/ETM+/OLI + i.landsat.toar + + + + Classification is based on the spectral signature information generated by either i.cluster, i.class, or i.gensig. + i.maxlik + + + + Extracts quality control parameters from Modis QC layers. + i.modis.qc + + + + Principal components analysis (PCA) for image processing. + i.pca + + + + Rectifies an image by computing a coordinate transformation for each pixel in the image based on the control points. + i.rectify + + + + Transforms raster maps from RGB (Red-Green-Blue) color space to HIS (Hue-Intensity-Saturation) color space. + i.rgb.his + + + + Performs contextual image classification using sequential maximum a posteriori (SMAP) estimation. + i.smap + + + + Targets an imagery group to a GRASS location and mapset. + i.target + + + + Uses red and nir bands mostly, and some indices require additional bands. + i.vi + + + + Zero-crossing "edge detection" raster function for image processing. + i.zc + + + + It assumes a cartesian coordinate system + m.cogo + + + + Measures the lengths and areas of features. + m.measure + + + + Generates watershed subbasins raster map. + r.basins.fill + + + + Compares bit patterns with a raster map. + r.bitpattern + + + + Takes vector stream data, transforms it to raster and subtracts depth from the output DEM. + r.carve + + + + Manages category values and labels associated with user-specified raster map layers. + r.category + + + + Creates a raster map containing concentric rings around a given point. + r.circle + + + + Recategorizes data in a raster map by grouping cells that form physically discrete areas into unique categories. + r.clump + + + + Tabulates the mutual occurrence (coincidence) of categories for two raster map layers. + r.coin + + + + Compresses and decompresses raster maps. + r.compress + + + + Produces a vector map of specified contours from a raster map. + r.contour + + + + Creates a raster map showing the cumulative cost of moving between different geographic locations on an input raster map whose cell category values represent cost. + r.cost + + + + Outputs a covariance/correlation matrix for user-specified raster map layer(s). + r.covar + + + + Creates a cross product of the category values from multiple raster map layers. + r.cross + + + + Prints terse list of category values found in a raster map layer. + r.describe + + + + Locates the closest points between objects in two raster maps. + r.distance + + + + Traces a flow through an elevation model or cost surface on a raster map. + r.drain + + + + Filters and generates a depressionless elevation map and a flow direction map from a given elevation raster map. + r.fill.dir + + + + Computes flowlines, flowpath lengths, and flowaccumulation (contributing areas) from a elevation raster map. + r.flow + + + + Generates a raster map containing distances to nearest raster features. + r.grow.distance + + + + Numerical calculation program for transient, confined and unconfined groundwater flow in two dimensions. + r.gwflow + + + + Generates red, green and blue raster map layers combining hue, intensity and saturation (HIS) values from user-specified input raster map layers. + r.his + + + + Computes horizon angle height from a digital elevation model. The module has two different modes of operation: 1. Computes the entire horizon around a single point whose coordinates are given with the 'coord' option. The horizon height (in radians). 2. Computes one or more raster maps of the horizon height in a single direction. The input for this is the angle (in degrees), which is measured counterclockwise with east=0, north=90 etc. The output is the horizon height in radians. + r.horizon + + + + Calculates error matrix and kappa parameter for accuracy assessment of classification result. + r.kappa + + + + Fills lake at given point to given level. + r.lake + + + + Calculates contrast weighted edge density index on a raster map + r.li.cwed + + + + Calculates dominance's diversity index on a raster map + r.li.dominance + + + + Calculates edge density index on a raster map, using a 4 neighbour algorithm + r.li.edgedensity + + + + Calculates mean pixel attribute index on a raster map + r.li.mpa + + + + Calculates mean patch size index on a raster map, using a 4 neighbour algorithm + r.li.mps + + + + Calculates coefficient of variation of patch area on a raster map + r.li.padcv + + + + Calculates range of patch area size on a raster map + r.li.padrange + + + + Calculates standard deviation of patch area a raster map + r.li.padsd + + + + Calculates patch density index on a raster map, using a 4 neighbour algorithm + r.li.patchdensity + + + + Calculates patch number index on a raster map, using a 4 neighbour algorithm. + r.li.patchnum + + + + Calculates richness index on a raster map + r.li.richness + + + + Calculates Shannon's diversity index on a raster map + r.li.shannon + + + + Calculates shape index on a raster map + r.li.shape + + + + Calculates Simpson's diversity index on a raster map + r.li.simpson + + + + Line-of-sight raster analysis program. + r.los + + + + Raster map calculator. + r.mapcalc + + + + Performs raster map matrix filter. + r.mfilter + + + + Finds the mode of values in a cover map within areas assigned the same category value in a user-specified base map. + r.mode + + + + Makes each cell category value a function of the category values assigned to the cells around it, and stores new cell values in an output raster map layer. + r.neighbors + + + + Manages NULL-values of given raster map. + r.null + + + + Uses a multi-scale approach by taking fitting quadratic parameters to any size window (via least squares). + r.param.scale + + + + Creates a composite raster map layer by using known category values from one (or more) map layer(s) to fill in areas of "no data" in another map layer. + r.patch + + + + Outputs the raster map layer values lying on user-defined line(s). + r.profile + + + + Re-projects a raster map from given location to the current location. + r.proj + + + + Produces the quantization file for a floating-point map. + r.quant + + + + Compute quantiles using two passes. + r.quantile + + + + Creates a raster map layer and vector point map containing randomly located points. + r.random + + + + Generates random cell values with spatial dependence. + r.random.cells + + + + Generates random surface(s) with spatial dependence. + r.random.surface + + + + Creates a new raster map whose category values are based upon a reclassification of the categories in an existing raster map. + r.reclass + + + + Recodes categorical raster maps. + r.recode + + + + Sets the boundary definitions for a raster map. + r.region + + + + Calculates linear regression from two raster maps: y = a + b*x. + r.regression.line + + + + Reports statistics for raster maps. + r.report + + + + Performs bilinear or bicubic spline interpolation with Tykhonov regularization. + r.resamp.bspline + + + + Resamples raster map layers using an analytic kernel. + r.resamp.filter + + + + Resamples raster map to a finer grid using interpolation. + r.resamp.interp + + + + Reinterpolates and optionally computes topographic analysis from input raster map to a new raster map (possibly with different resolution) using regularized spline with tension and smoothing. + r.resamp.rst + + + + Resamples raster map layers to a coarser grid using aggregation. + r.resamp.stats + + + + GRASS raster map layer data resampling capability. + r.resample + + + + Rescales the range of category values in a raster map layer. + r.rescale + + + + Rescales histogram equalized the range of category values in a raster map layer. + r.rescale.eq + + + + Generates three, or four raster map layers showing the base (perpendicular) rate of spread (ROS), the maximum (forward) ROS, the direction of the maximum ROS, and optionally the maximum potential spotting distance for fire spread simulation. + r.ros + + + + Makes each output cell value a function of the values assigned to the corresponding cells in the input raster map layers. + r.series + + + + Sediment transport and erosion/deposition simulation using path sampling method (SIMWE). + r.sim.sediment + + + + Overland flow hydrologic simulation using path sampling method (SIMWE). + r.sim.water + + + + Aspect is calculated counterclockwise from east. + r.slope.aspect + + + + Numerical calculation program for transient, confined and unconfined solute transport in two dimensions + r.solute.transport + + + + Generates a raster map of the cumulative time of spread, given raster maps containing the rates of spread (ROS), the ROS directions and the spread origins. It optionally produces raster maps to contain backlink UTM coordinates for tracing spread paths. Usable for fire spread simulations. + r.spread + + + + Recursively traces the least cost path backwards to cells from which the cumulative cost was determined. + r.spreadpath + + + + Calculates category or object oriented statistics. + r.statistics + + + + Generates area statistics for raster map. + r.stats + + + + Computes direct (beam), diffuse and reflected solar irradiation raster maps for given day, latitude, surface and atmospheric conditions. Solar parameters (e.g. sunrise, sunset times, declination, extraterrestrial irradiance, daylight length) are saved in the map history file. Alternatively, a local time can be specified to compute solar incidence angle and/or irradiance raster maps. The shadowing effect of the topography is optionally incorporated. + r.sun + + + + Either exact sun position (A) is specified, or date/time to calculate the sun position (B) by r.sunmask itself. + r.sunmask + + + + Allows creation and/or modification of raster map layer support files. + r.support + + + + Update raster map statistics + r.support.stats + + + + Prints estimation of surface area for raster map. + r.surf.area + + + + Generates surface raster map from rasterized contours. + r.surf.contour + + + + Creates a fractal surface of a given fractal dimension. + r.surf.fractal + + + + Mean and standard deviation of gaussian deviates can be expressed by the user. + r.surf.gauss + + + + Surface interpolation utility for raster map. + r.surf.idw + + + + Surface generation program. + r.surf.idw2 + + + + Produces a raster surface map of uniform random deviates with defined range. + r.surf.random + + + + Flow computation for massive grids (float version). + r.terraflow + + + + Generate images with textural features from a raster map. + r.texture + + + + Thins non-zero cells that denote linear features in a raster map layer. + r.thin + + + + Print/add/remove a timestamp for a raster map. + r.timestamp + + + + Converts 2D raster map slices to one 3D raster volume map. + r.to.rast3 + + + + Creates a 3D volume map based on 2D elevation and value raster maps. + r.to.rast3elev + + + + Converts a raster map into a vector map. + r.to.vect + + + + Creates a topographic index raster map from an elevation raster map. + r.topidx + + + + Simulates TOPMODEL which is a physically based hydrologic model. + r.topmodel + + + + Outputs raster map layer values lying along user defined transect line(s). + r.transect + + + + Calculates univariate statistics from the non-null cells of a raster map. + r.univar + + + + Computes USLE Soil Erodibility Factor (K). + r.uslek + + + + Computes USLE R factor, Rainfall erosivity index. + r.usler + + + + Optionally produces a GRASS vector points map containing the calculated centroids of these clumps. + r.volume + + + + Computes anisotropic cumulative cost of moving between different geographic locations on an input elevation raster map whose cell category values represent elevation combined with an input raster map layer whose cell values represent friction cost. + r.walk + + + + Creates watershed basins from a drainage direction map. + r.water.outlet + + + + Calculates hydrological parameters and RUSLE factors. + r.watershed + + + + Numerical calculation program for transient, confined groundwater flow in three dimensions. + r3.gwflow + + + + Outputs basic information about a user-specified 3D raster map layer. + r3.info + + + + Raster map calculator. + r3.mapcalc + + + + Establishes the current working 3D raster mask. + r3.mask + + + + Explicitly create the 3D NULL-value bitmap file. + r3.null + + + + Generates volume statistics for 3D raster maps. + r3.stats + + + + Print/add/remove a timestamp for a 3D raster map + r3.timestamp + + + + Converts 3D raster maps to 2D raster maps + r3.to.rast + + + + Calculates univariate statistics from the non-null 3d cells of a raster3d map. + r3.univar + + + + Creates a buffer around vector features of given type. + v.buffer + + + + Optionaly also checks for topological errors. + v.build + + + + Builds polylines from lines or boundaries. + v.build.polylines + + + + Attaches, deletes or reports vector categories to map geometry. + v.category + + + + Classifies attribute data, e.g. for thematic mapping + v.class + + + + Imports older versions of GRASS vector maps. + v.convert + + + + Prints/sets DB connection for a vector map to attribute table. + v.db.connect + + + + Prints vector map attributes. + v.db.select + + + + Creates a Delaunay triangulation from an input vector map containing points or centroids. + v.delaunay + + + + Finds the nearest element in vector map 'to' for elements in vector map 'from'. + v.distance + + + + Converts 2D vector features to 3D by sampling of elevation raster map. + v.drape + + + + Edits a vector map, allows adding, deleting and modifying selected vector features. + v.edit + + + + Selects vector features from an existing vector map and creates a new vector map containing only the selected features. + v.extract + + + + Optionally the height can be derived from sampling of elevation raster map. + v.extrude + + + + Performs vector based generalization. + v.generalize + + + + Produces a 2D/3D convex hull for a given vector map. + v.hull + + + + Randomly partition points into test/train sets. + v.kcv + + + + Density is computed using a moving kernel. Optionally generates a vector density map on a vector network. + v.kernel + + + + Creates paint labels for a vector map from attached attributes. + v.label + + + + Correction of the v.lidar.growing output. It is the last of the three algorithms for LIDAR filtering. + v.lidar.correction + + + + Detects the object's edges from a LIDAR data set. + v.lidar.edgedetection + + + + Building contour determination and Region Growing algorithm for determining the building inside + v.lidar.growing + + + + Creates a linear reference system. + v.lrs.create + + + + Creates stationing from input lines, and linear reference system. + v.lrs.label + + + + Creates points/segments from input lines, linear reference system and positions read from stdin or a file. + v.lrs.segment + + + + Finds line id and real km+offset for given points in vector map using linear reference system. + v.lrs.where + + + + Creates a vector map of a user-defined grid. + v.mkgrid + + + + Makes each cell value a function of the attribute values assigned to the vector points or centroids around it, and stores new cell values in an output raster map. + v.neighbors + + + + Performs network maintenance. + v.net + + + + center node must be opened (costs >= 0). Costs of center node are used in calculation + v.net.alloc + + + + Computes the shortest path between all pairs of nodes in the network. + v.net.allpairs + + + + Computes bridges and articulation points in the network. + v.net.bridge + + + + Computes degree, centrality, betweeness, closeness and eigenvector centrality measures in the network. + v.net.centrality + + + + Computes strongly and weakly connected components in the network. + v.net.components + + + + Computes vertex connectivity between two sets of nodes in the network. + v.net.connectivity + + + + Finds the shortest paths from each 'from' point to the nearest 'to' feature and various information about this relation are uploaded to the attribute table. + v.net.distance + + + + Computes the maximum flow between two sets of nodes in the network. + v.net.flow + + + + Splits net to bands between cost isolines (direction from centre). Centre node must be opened (costs >= 0). Costs of centre node are used in calculation. + v.net.iso + + + + Finds shortest path on vector network. + v.net.path + + + + Note that TSP is NP-hard, heuristic algorithm is used by this module and created cycle may be sub optimal + v.net.salesman + + + + Computes minimum spanning tree for the network. + v.net.spanningtree + + + + Note that 'Minimum Steiner Tree' problem is NP-hard and heuristic algorithm is used in this module so the result may be sub optimal. + v.net.steiner + + + + Finds shortest path using timetables. + v.net.timetable + + + + Performs visibility graph construction. + v.net.visibility + + + + Tests for normality for vector points. + v.normal + + + + Removes outliers from vector point data. + v.outlier + + + + Overlays two vector maps. + v.overlay + + + + Creates parallel line to input vector lines. + v.parallel + + + + Creates a new vector map by combining other vector maps. + v.patch + + + + Random location perturbations of vector points. + v.perturb + + + + Re-projects a vector map from one location to the current location. + v.proj + + + + Indices for quadrat counts of sites lists. + v.qcount + + + + Generates random 2D/3D vector points. + v.random + + + + Changes vector category values for an existing vector map according to results of SQL queries or a value in attribute table column. + v.reclass + + + + Samples a raster map at vector point locations. + v.sample + + + + Creates points/segments from input vector lines and positions. + v.segment + + + + Selects features from vector map (A) by features from other vector map (B). + v.select + + + + Splits vector lines to shorter segments. + v.split + + + + Updates vector map metadata. + v.support + + + + Performs bicubic or bilinear spline interpolation with Tykhonov regularization. + v.surf.bspline + + + + Provides surface interpolation from vector point data by Inverse Distance Squared Weighting. + v.surf.idw + + + + Spatial approximation and topographic analysis from given point or isoline data in vector format to floating point raster format using regularized spline with tension. + v.surf.rst + + + + Performs transformation of 2D vector features to 3D. + v.to.3d + + + + Populates attribute values from vector features. + v.to.db + + + + Creates points along input lines in new vector map with 2 layers. + v.to.points + + + + Converts (rasterize) a vector map into a raster map. + v.to.rast + + + + Converts a vector map (only points) into a 3D raster map. + v.to.rast3 + + + + Performs an affine transformation (shift, scale and rotate) on vector map. + v.transform + + + + Changes type of vector features. + v.type + + + + Variance and standard deviation is calculated only for points if specified. + v.univar + + + + Interpolates point data to a 3D raster map using regularized spline with tension (RST) algorithm. + v.vol.rst + + + + Creates a Voronoi diagram in current region from an input vector map containing points or centroids. + v.voronoi + + + + \ No newline at end of file diff --git a/src/Wps/Wps.Client.Tests/Resources/ProcessOfferings.xml b/src/Wps/Wps.Client.Tests/Resources/ProcessOfferings.xml new file mode 100644 index 0000000..821837f --- /dev/null +++ b/src/Wps/Wps.Client.Tests/Resources/ProcessOfferings.xml @@ -0,0 +1,244 @@ + + + + + Generates statistics for i.smap from raster map. + http://grass.osgeo.org/grass70/manuals/html70_user/i.gensigset.html + i.gensigset + + Ground truth training map + trainingmap + + + + + + + + + + + + + + + + + + + + + + Name of input imagery group + group + + + + + + + + + + + Name of input imagery subgroup + subgroup + + + + + + + + + + + Name for output file containing result signatures + signaturefile + + + + + + + + + + + Maximum number of sub-signatures in any class + maxsig + + + + + + + 5 + + + + + Resolution of the mapset in north-south direction in meters or degrees + This parameter defines the north-south resolution of the mapset in meter or degrees, which should be used to process the input and output raster data. To enable this setting, you need to specify north-south and east-west resolution. + grass_resolution_ns + + + + + + + + + + + Resolution of the mapset in east-west direction in meters or degrees + This parameter defines the east-west resolution of the mapset in meters or degrees, which should be used to process the input and output raster data. To enable this setting, you need to specify north-south and east-west resolution. + grass_resolution_ew + + + + + + + + + + + Band to select for processing (default is all bands) + This parameter defines band number of the input raster files which should be processed. As default all bands are processed and used as single and multiple inputs for raster modules. + grass_band_number + + + + + + + + + + + Module output on stdout + The output of the module written to stdout + stdout + + + + + + + + + + Generates statistics for i.maxlik from raster map. + http://grass.osgeo.org/grass70/manuals/html70_user/i.gensig.html + i.gensig + + Ground truth training map + trainingmap + + + + + + + + + + + + + + + + + + + + + + Name of input imagery group + group + + + + + + + + + + + Name of input imagery subgroup + subgroup + + + + + + + + + + + Name for output file containing result signatures + signaturefile + + + + + + + + + + + Resolution of the mapset in north-south direction in meters or degrees + This parameter defines the north-south resolution of the mapset in meter or degrees, which should be used to process the input and output raster data. To enable this setting, you need to specify north-south and east-west resolution. + grass_resolution_ns + + + + + + + + + + + Resolution of the mapset in east-west direction in meters or degrees + This parameter defines the east-west resolution of the mapset in meters or degrees, which should be used to process the input and output raster data. To enable this setting, you need to specify north-south and east-west resolution. + grass_resolution_ew + + + + + + + + + + + Band to select for processing (default is all bands) + This parameter defines band number of the input raster files which should be processed. As default all bands are processed and used as single and multiple inputs for raster modules. + grass_band_number + + + + + + + + + + + Module output on stdout + The output of the module written to stdout + stdout + + + + + + + + \ No newline at end of file diff --git a/src/Wps/Wps.Client.Tests/Wps.Client.Tests.csproj b/src/Wps/Wps.Client.Tests/Wps.Client.Tests.csproj index 8385f1e..3caa3f7 100644 --- a/src/Wps/Wps.Client.Tests/Wps.Client.Tests.csproj +++ b/src/Wps/Wps.Client.Tests/Wps.Client.Tests.csproj @@ -7,8 +7,16 @@ - - + + + + + + + + + + all From 0613dfc8c9bea2b0c64c4b39077f2c0972c58711 Mon Sep 17 00:00:00 2001 From: Mihai Stan Date: Wed, 12 Jun 2019 16:46:12 +0200 Subject: [PATCH 61/76] Update tests project packages --- src/Wps/Wps.Client.Tests/Wps.Client.Tests.csproj | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/Wps/Wps.Client.Tests/Wps.Client.Tests.csproj b/src/Wps/Wps.Client.Tests/Wps.Client.Tests.csproj index 3caa3f7..cc60c93 100644 --- a/src/Wps/Wps.Client.Tests/Wps.Client.Tests.csproj +++ b/src/Wps/Wps.Client.Tests/Wps.Client.Tests.csproj @@ -17,6 +17,10 @@ + + + + all From 81f409b808c355af6ba0027298bc73dee2499f72 Mon Sep 17 00:00:00 2001 From: Mihai Stan Date: Wed, 12 Jun 2019 16:46:30 +0200 Subject: [PATCH 62/76] Add Moq package to the tests project --- src/Wps/Wps.Client.Tests/Wps.Client.Tests.csproj | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Wps/Wps.Client.Tests/Wps.Client.Tests.csproj b/src/Wps/Wps.Client.Tests/Wps.Client.Tests.csproj index cc60c93..a3e44f2 100644 --- a/src/Wps/Wps.Client.Tests/Wps.Client.Tests.csproj +++ b/src/Wps/Wps.Client.Tests/Wps.Client.Tests.csproj @@ -21,6 +21,7 @@ + all From 92544e6d49c6d81386789003ed131bd5d27980bf Mon Sep 17 00:00:00 2001 From: Mihai Stan Date: Wed, 12 Jun 2019 17:00:07 +0200 Subject: [PATCH 63/76] Add synchronous WPS client interface --- src/Wps/Wps.Client/Services/IWpsClient.cs | 69 +++++++++++++++++++++++ 1 file changed, 69 insertions(+) create mode 100644 src/Wps/Wps.Client/Services/IWpsClient.cs diff --git a/src/Wps/Wps.Client/Services/IWpsClient.cs b/src/Wps/Wps.Client/Services/IWpsClient.cs new file mode 100644 index 0000000..2ea06f7 --- /dev/null +++ b/src/Wps/Wps.Client/Services/IWpsClient.cs @@ -0,0 +1,69 @@ +using System.Threading.Tasks; +using Wps.Client.Models; +using Wps.Client.Models.Requests; +using Wps.Client.Models.Responses; + +namespace Wps.Client.Services +{ + public interface IWpsClient + { + + /// + /// Get the capabilities of the WPS server. + /// + /// The address pointing to the WPS server. + /// + Task GetCapabilities(string wpsUri); + + /// + /// Get the process offerings that a process is capable of. + /// + /// The address pointing to the WPS server. + /// List of process summaries having a valid identifier. + /// A collection of the processes offered. + Task DescribeProcess(string wpsUri, params ProcessSummary[] processSummaries); + + /// + /// Get the process offerings that a process is capable of. + /// + /// The address pointing to the WPS server. + /// List of valid identifiers for the processes. + /// A collection of the processes offered. + Task DescribeProcess(string wpsUri, params string[] processIdentifiers); + + /// + /// Get the status of an ongoing job. + /// + /// The address pointing to the WPS server. + /// The id of the job to be checked. + /// Detailed information about the job status. + Task GetJobStatus(string wpsUri, string jobId); + + /// + /// Get the raw result of a synchronously executed request. + /// + /// The type of the result + /// The address pointing to the WPS server. + /// The execution request to be sent to the WPS server. + /// The result deserialized as . + Task GetRawResultAs(string wpsUri, ExecuteRequest request); + + /// + /// Get the raw result of a synchronously executed request. + /// + /// The address pointing to the WPS server. + /// The execution request to be sent to the WPS server. + /// The raw string result. + Task GetRawResult(string wpsUri, ExecuteRequest request); + + /// + /// Get the documented result of a synchronously executed request. + /// + /// The type of the data included in the result. + /// The address pointing to the WPS server. + /// The execution request to be sent to the WPS server. + /// The document containing additional information about the result and the output data. + Task> GetDocumentedResult(string wpsUri, ExecuteRequest request); + + } +} From b83de21a328c3bf3cf336a54df9b4d066ec2ce26 Mon Sep 17 00:00:00 2001 From: Mihai Stan Date: Wed, 12 Jun 2019 17:01:41 +0200 Subject: [PATCH 64/76] Add GetCapabilities and DescribeProcess request to the WPS client --- src/Wps/Wps.Client.Tests/WpsClientTests.cs | 139 +++++++++++++++++++++ src/Wps/Wps.Client/Services/WpsClient.cs | 111 ++++++++++++++++ 2 files changed, 250 insertions(+) create mode 100644 src/Wps/Wps.Client.Tests/WpsClientTests.cs create mode 100644 src/Wps/Wps.Client/Services/WpsClient.cs diff --git a/src/Wps/Wps.Client.Tests/WpsClientTests.cs b/src/Wps/Wps.Client.Tests/WpsClientTests.cs new file mode 100644 index 0000000..50c9394 --- /dev/null +++ b/src/Wps/Wps.Client.Tests/WpsClientTests.cs @@ -0,0 +1,139 @@ +using FluentAssertions; +using Moq; +using Moq.Protected; +using System; +using System.Net; +using System.Net.Http; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using Wps.Client.Models; +using Wps.Client.Models.Execution; +using Wps.Client.Models.Ows; +using Wps.Client.Models.Requests; +using Wps.Client.Services; +using Xunit; + +namespace Wps.Client.Tests +{ + public class WpsClientTests + { + + private const string MockUri = "http://mock.uri"; + + /* + * Helpers + */ + + private static HttpMessageHandler GetMockedMessageHandlerForResponse(string response, HttpStatusCode code = HttpStatusCode.OK) + { + var handlerMock = new Mock(MockBehavior.Strict); + handlerMock.Protected() + .Setup>( + "SendAsync", + ItExpr.IsAny(), + ItExpr.IsAny()) + .ReturnsAsync(new HttpResponseMessage + { + StatusCode = code, + Content = new StringContent(response, Encoding.UTF8) + }) + .Verifiable(); + + return handlerMock.Object; + } + + /* + * Get Capabilities Tests + */ + + [Theory] + [EmbeddedResourceData("Wps.Client.Tests/Resources/GetCapabilities.xml")] + public async Task GetCapabilities_ValidUriGiven_ShouldPass(string httpClientResponseXml) + { + var wpsClient = new WpsClient(new HttpClient(GetMockedMessageHandlerForResponse(httpClientResponseXml)), new XmlSerializationService()); + + var capabilities = await wpsClient.GetCapabilities(MockUri); + capabilities.Should().NotBeNull(); + } + + [Fact] + public async Task GetCapabilities_NullUriGiven_ShouldThrow_ArgumentNullException() + { + var wpsClient = new WpsClient(new HttpClient(), new XmlSerializationService()); + await Assert.ThrowsAsync(() => wpsClient.GetCapabilities(null)); + } + + /* + * Describe Process Tests + */ + + [Theory] + [EmbeddedResourceData("Wps.Client.Tests/Resources/ExceptionReport.xml")] + public async Task DescribeProcess_InvalidStringIdentifiersGiven_ShouldThrowOwsException(string httpClientResponseXml) + { + var wpsClient = new WpsClient(new HttpClient(GetMockedMessageHandlerForResponse(httpClientResponseXml, HttpStatusCode.BadRequest)), new XmlSerializationService()); + + await Assert.ThrowsAsync(() => wpsClient.DescribeProcess(MockUri, "invalid 1", "invalid 2")); + } + + [Theory] + [EmbeddedResourceData("Wps.Client.Tests/Resources/ExceptionReport.xml")] + public async Task DescribeProcess_InvalidSummariesGiven_ShouldThrowOwsException(string httpClientResponseXml) + { + var summaries = new[] + { + new ProcessSummary {Identifier = "invalid 1"}, + new ProcessSummary {Identifier = "invalid 2"} + }; + + var wpsClient = new WpsClient(new HttpClient(GetMockedMessageHandlerForResponse(httpClientResponseXml, HttpStatusCode.BadRequest)), new XmlSerializationService()); + + await Assert.ThrowsAsync(() => wpsClient.DescribeProcess(MockUri, summaries)); + } + + [Theory] + [EmbeddedResourceData("Wps.Client.Tests/Resources/ProcessOfferings.xml")] + public async Task DescribeProcess_ValidStringIdentifiersGiven_ShouldReturnTwoProcesses(string httpClientResponseXml) + { + var wpsClient = new WpsClient(new HttpClient(GetMockedMessageHandlerForResponse(httpClientResponseXml)), new XmlSerializationService()); + + var collection = await wpsClient.DescribeProcess(MockUri, "i.gensigset", "i.gensig"); + collection.Should().HaveCount(2); + } + + [Theory] + [EmbeddedResourceData("Wps.Client.Tests/Resources/ProcessOfferings.xml")] + public async Task DescribeProcess_ValidSummariesGiven_ShouldReturnTwoProcesses(string httpClientResponseXml) + { + var summaries = new[] + { + new ProcessSummary {Identifier = "i.gensigset"}, + new ProcessSummary {Identifier = "i.gensig"} + }; + + var wpsClient = new WpsClient(new HttpClient(GetMockedMessageHandlerForResponse(httpClientResponseXml)), new XmlSerializationService()); + + var collection = await wpsClient.DescribeProcess(MockUri, summaries); + collection.Should().HaveCount(2); + } + + [Fact] + public async Task DescribeProcess_NullUriGiven_ShouldThrowArgumentNullException() + { + var wpsClient = new WpsClient(new HttpClient(), new XmlSerializationService()); + + await Assert.ThrowsAsync(() => wpsClient.DescribeProcess(null, string.Empty)); + } + + [Fact] + public async Task DescribeProcess_EmptyIdentifiersGiven_ShouldThrowArgumentNullException() + { + var wpsClient = new WpsClient(new HttpClient(), new XmlSerializationService()); + + await Assert.ThrowsAsync(() => wpsClient.DescribeProcess(MockUri, new string[0])); + } + + } +} + diff --git a/src/Wps/Wps.Client/Services/WpsClient.cs b/src/Wps/Wps.Client/Services/WpsClient.cs new file mode 100644 index 0000000..3751aad --- /dev/null +++ b/src/Wps/Wps.Client/Services/WpsClient.cs @@ -0,0 +1,111 @@ +using System; +using System.Linq; +using System.Net.Http; +using System.Text; +using System.Threading.Tasks; +using Wps.Client.Models; +using Wps.Client.Models.Execution; +using Wps.Client.Models.Ows; +using Wps.Client.Models.Requests; +using Wps.Client.Models.Responses; + +namespace Wps.Client.Services +{ + public class WpsClient : IWpsClient, IDisposable + { + + private readonly HttpClient _httpClient; + private readonly IXmlSerializer _serializationService; + + public WpsClient(HttpClient httpClient, IXmlSerializer serializationService) + { + _httpClient = httpClient; + _serializationService = serializationService; + } + + private async Task GetRequestResult(string wpsUri, RequestBase request) + { + if (wpsUri == null) throw new ArgumentNullException(nameof(wpsUri)); + if (request == null) throw new ArgumentNullException(nameof(request)); + + var requestXml = _serializationService.Serialize(request); + var response = await _httpClient.PostAsync(wpsUri, + new StringContent(requestXml, Encoding.UTF8)); + + var content = await response.Content.ReadAsStringAsync(); + if (response.IsSuccessStatusCode) + { + return content; + } + + var exceptionReport = _serializationService.Deserialize(content); + var exception = exceptionReport.Exceptions.FirstOrDefault(); + if (exception != null) + { + throw exception; + } + + throw new Exception("Could not get a valid response from the WPS server."); + } + + public async Task GetCapabilities(string wpsUri) + { + if (wpsUri == null) throw new ArgumentNullException(nameof(wpsUri)); + + var request = new GetCapabilitiesRequest(); + + var content = await GetRequestResult(wpsUri, request); + var result = _serializationService.Deserialize(content); + + return result; + } + + public Task DescribeProcess(string wpsUri, params ProcessSummary[] processSummaries) + { + return DescribeProcess(wpsUri, processSummaries.Select(ps => ps.Identifier).ToArray()); + } + + public async Task DescribeProcess(string wpsUri, params string[] processIdentifiers) + { + if (wpsUri == null) throw new ArgumentNullException(nameof(wpsUri)); + if (processIdentifiers == null) throw new ArgumentNullException(nameof(processIdentifiers)); + + if (!processIdentifiers.Any()) throw new InvalidOperationException("You cannot get the description of an empty list of identifiers."); + + var request = new DescribeProcessRequest + { + Identifiers = processIdentifiers + }; + + var content = await GetRequestResult(wpsUri, request); + var result = _serializationService.Deserialize(content); + + return result; + } + + public Task GetJobStatus(string wpsUri, string jobId) + { + throw new NotImplementedException(); + } + + public Task GetRawResultAs(string wpsUri, ExecuteRequest request) + { + throw new NotImplementedException(); + } + + public Task GetRawResult(string wpsUri, ExecuteRequest request) + { + throw new NotImplementedException(); + } + + public Task> GetDocumentedResult(string wpsUri, ExecuteRequest request) + { + throw new NotImplementedException(); + } + + public void Dispose() + { + _httpClient?.Dispose(); + } + } +} From f430899a52e6c338ec37ea260e0835221bc13f26 Mon Sep 17 00:00:00 2001 From: Mihai Stan Date: Wed, 12 Jun 2019 17:15:41 +0200 Subject: [PATCH 65/76] Explicitly specify the mime type of the post request --- src/Wps/Wps.Client/Services/WpsClient.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Wps/Wps.Client/Services/WpsClient.cs b/src/Wps/Wps.Client/Services/WpsClient.cs index 3751aad..acf4e7d 100644 --- a/src/Wps/Wps.Client/Services/WpsClient.cs +++ b/src/Wps/Wps.Client/Services/WpsClient.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Linq; using System.Net.Http; using System.Text; @@ -30,7 +30,7 @@ private async Task GetRequestResult(string wpsUri, RequestBase request) var requestXml = _serializationService.Serialize(request); var response = await _httpClient.PostAsync(wpsUri, - new StringContent(requestXml, Encoding.UTF8)); + new StringContent(requestXml, Encoding.UTF8, "text/xml")); var content = await response.Content.ReadAsStringAsync(); if (response.IsSuccessStatusCode) From 2ed025dfbec860c7d4c2c32ad6cd6c9fa2829bea Mon Sep 17 00:00:00 2001 From: Mihai Stan Date: Thu, 13 Jun 2019 14:18:42 +0200 Subject: [PATCH 66/76] Add GetStatus request to the WPS client --- .../Wps.Client.Tests/Resources/JobStatus.xml | 6 ++++ .../Wps.Client.Tests/Wps.Client.Tests.csproj | 2 ++ src/Wps/Wps.Client.Tests/WpsClientTests.cs | 32 +++++++++++++++++++ src/Wps/Wps.Client/Services/WpsClient.cs | 17 ++++++++-- 4 files changed, 54 insertions(+), 3 deletions(-) create mode 100644 src/Wps/Wps.Client.Tests/Resources/JobStatus.xml diff --git a/src/Wps/Wps.Client.Tests/Resources/JobStatus.xml b/src/Wps/Wps.Client.Tests/Resources/JobStatus.xml new file mode 100644 index 0000000..2711e46 --- /dev/null +++ b/src/Wps/Wps.Client.Tests/Resources/JobStatus.xml @@ -0,0 +1,6 @@ + + + FB6DD4B0-A2BB-11E3-A5E2-0800200C9A66 + Running + 2014-12-24T16:00:00Z + \ No newline at end of file diff --git a/src/Wps/Wps.Client.Tests/Wps.Client.Tests.csproj b/src/Wps/Wps.Client.Tests/Wps.Client.Tests.csproj index a3e44f2..0347c4b 100644 --- a/src/Wps/Wps.Client.Tests/Wps.Client.Tests.csproj +++ b/src/Wps/Wps.Client.Tests/Wps.Client.Tests.csproj @@ -9,12 +9,14 @@ + + diff --git a/src/Wps/Wps.Client.Tests/WpsClientTests.cs b/src/Wps/Wps.Client.Tests/WpsClientTests.cs index 50c9394..a842588 100644 --- a/src/Wps/Wps.Client.Tests/WpsClientTests.cs +++ b/src/Wps/Wps.Client.Tests/WpsClientTests.cs @@ -134,6 +134,38 @@ public async Task DescribeProcess_EmptyIdentifiersGiven_ShouldThrowArgumentNullE await Assert.ThrowsAsync(() => wpsClient.DescribeProcess(MockUri, new string[0])); } + /* + * GetJobStatus Tests + */ + + [Theory] + [EmbeddedResourceData("Wps.Client.Tests/Resources/JobStatus.xml")] + public async Task GetStatus_ValidJobIdGiven_ShouldReturnJobStatus(string httpClientResponseXml) + { + const string expectedJobId = "FB6DD4B0-A2BB-11E3-A5E2-0800200C9A66"; + var wpsClient = new WpsClient(new HttpClient(GetMockedMessageHandlerForResponse(httpClientResponseXml)), new XmlSerializationService()); + + var status = await wpsClient.GetJobStatus(MockUri, expectedJobId); + status.Should().NotBeNull(); + status.JobId.Should().Be(expectedJobId); + } + + [Fact] + public async Task GetStatus_NullJobIdGiven_ShouldThrowArgumentNullException() + { + var wpsClient = new WpsClient(new HttpClient(), new XmlSerializationService()); + + await Assert.ThrowsAsync(() => wpsClient.GetJobStatus(MockUri, null)); + } + + [Fact] + public async Task GetStatus_NullWpsUriGiven_ShouldThrowArgumentNullException() + { + var wpsClient = new WpsClient(new HttpClient(), new XmlSerializationService()); + + await Assert.ThrowsAsync(() => wpsClient.GetJobStatus(null, string.Empty)); + } + } } diff --git a/src/Wps/Wps.Client/Services/WpsClient.cs b/src/Wps/Wps.Client/Services/WpsClient.cs index acf4e7d..e189d7e 100644 --- a/src/Wps/Wps.Client/Services/WpsClient.cs +++ b/src/Wps/Wps.Client/Services/WpsClient.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Linq; using System.Net.Http; using System.Text; @@ -83,9 +83,20 @@ public async Task DescribeProcess(string wpsUri, para return result; } - public Task GetJobStatus(string wpsUri, string jobId) + public async Task GetJobStatus(string wpsUri, string jobId) { - throw new NotImplementedException(); + if (wpsUri == null) throw new ArgumentNullException(nameof(wpsUri)); + if (jobId == null) throw new ArgumentNullException(nameof(jobId)); + + var request = new GetStatusRequest + { + JobId = jobId + }; + + var content = await GetRequestResult(wpsUri, request); + var result = _serializationService.Deserialize(content); + + return result; } public Task GetRawResultAs(string wpsUri, ExecuteRequest request) From bac02fc743583fdadcbc224d5953a5eddb79d4a6 Mon Sep 17 00:00:00 2001 From: Mihai Stan Date: Thu, 13 Jun 2019 15:29:43 +0200 Subject: [PATCH 67/76] Add GetRawResult and GetDocumentedResult request to the WPS client --- .../Resources/SynchronousDocumentedResult.xml | 10 ++ .../Wps.Client.Tests/Wps.Client.Tests.csproj | 2 + src/Wps/Wps.Client.Tests/WpsClientTests.cs | 148 +++++++++++++++++- src/Wps/Wps.Client/Services/IWpsClient.cs | 9 -- src/Wps/Wps.Client/Services/WpsClient.cs | 27 +++- 5 files changed, 177 insertions(+), 19 deletions(-) create mode 100644 src/Wps/Wps.Client.Tests/Resources/SynchronousDocumentedResult.xml diff --git a/src/Wps/Wps.Client.Tests/Resources/SynchronousDocumentedResult.xml b/src/Wps/Wps.Client.Tests/Resources/SynchronousDocumentedResult.xml new file mode 100644 index 0000000..e5a3455 --- /dev/null +++ b/src/Wps/Wps.Client.Tests/Resources/SynchronousDocumentedResult.xml @@ -0,0 +1,10 @@ + + + 68ad4685-9aad-478c-ac80-0df343cef4df + 2019-06-13T15:25:29.219458Z + + + 150 + + + \ No newline at end of file diff --git a/src/Wps/Wps.Client.Tests/Wps.Client.Tests.csproj b/src/Wps/Wps.Client.Tests/Wps.Client.Tests.csproj index 0347c4b..ba28453 100644 --- a/src/Wps/Wps.Client.Tests/Wps.Client.Tests.csproj +++ b/src/Wps/Wps.Client.Tests/Wps.Client.Tests.csproj @@ -11,6 +11,7 @@ + @@ -18,6 +19,7 @@ + diff --git a/src/Wps/Wps.Client.Tests/WpsClientTests.cs b/src/Wps/Wps.Client.Tests/WpsClientTests.cs index a842588..bf7655e 100644 --- a/src/Wps/Wps.Client.Tests/WpsClientTests.cs +++ b/src/Wps/Wps.Client.Tests/WpsClientTests.cs @@ -25,13 +25,14 @@ public class WpsClientTests * Helpers */ - private static HttpMessageHandler GetMockedMessageHandlerForResponse(string response, HttpStatusCode code = HttpStatusCode.OK) + private static HttpMessageHandler GetMockedMessageHandlerForResponse(string response, HttpStatusCode code = HttpStatusCode.OK, string expectedRequestContent = null) { var handlerMock = new Mock(MockBehavior.Strict); handlerMock.Protected() .Setup>( "SendAsync", - ItExpr.IsAny(), + expectedRequestContent == null ? ItExpr.IsAny() : ItExpr.Is( + m => m.Content.ReadAsStringAsync().Result.Equals(expectedRequestContent)), ItExpr.IsAny()) .ReturnsAsync(new HttpResponseMessage { @@ -166,6 +167,149 @@ public async Task GetStatus_NullWpsUriGiven_ShouldThrowArgumentNullException() await Assert.ThrowsAsync(() => wpsClient.GetJobStatus(null, string.Empty)); } + /* + * GetRawResult Tests + */ + + [Fact] + public async Task GetRawResult_ValidRequestGiven_ShouldReturnResult() + { + const string expectedResultXml = "150"; + + var request = new ExecuteRequest + { + Inputs = new[] + { + new DataInput + { + Data = new LiteralDataValue{Value = "test"} + } + }, + ExecutionMode = ExecutionMode.Synchronous, + ResponseType = ResponseType.Raw + }; + + var expectedRequestXml = new XmlSerializationService().Serialize(request); + + var wpsClient = new WpsClient(new HttpClient(GetMockedMessageHandlerForResponse(expectedResultXml, HttpStatusCode.OK, expectedRequestXml)), new XmlSerializationService()); + + var result = await wpsClient.GetRawResult(MockUri, request); + result.Should().Be(expectedResultXml); + } + + [Fact] + public async Task GetRawResult_NullWpsUriGiven_ShouldThrowArgumentNullException() + { + var wpsClient = new WpsClient(new HttpClient(), new XmlSerializationService()); + + await Assert.ThrowsAsync(() => wpsClient.GetRawResult(null, new ExecuteRequest())); + } + + [Fact] + public async Task GetRawResult_NullRequestGiven_ShouldThrowArgumentNullException() + { + var wpsClient = new WpsClient(new HttpClient(), new XmlSerializationService()); + + await Assert.ThrowsAsync(() => wpsClient.GetRawResult(MockUri, null)); + } + + [Fact] + public async Task GetRawResult_DocumentedResponseTypeGiven_ShouldThrowInvalidOperationException() + { + var wpsClient = new WpsClient(new HttpClient(), new XmlSerializationService()); + + await Assert.ThrowsAsync(() => wpsClient.GetRawResult(MockUri, new ExecuteRequest + { + ResponseType = ResponseType.Document + })); + } + + [Fact] + public async Task GetRawResult_AsynchronousExecutionModeGiven_ShouldThrowInvalidOperationException() + { + var wpsClient = new WpsClient(new HttpClient(), new XmlSerializationService()); + + await Assert.ThrowsAsync(() => wpsClient.GetRawResult(MockUri, new ExecuteRequest + { + ExecutionMode = ExecutionMode.Asynchronous + })); + } + /* + * GetDocumentedResult Tests + */ + + [Theory] + [EmbeddedResourceData("Wps.Client.Tests/Resources/SynchronousDocumentedResult.xml")] + public async Task GetDocumentedResult_ValidRequestGiven_ShouldReturnResult(string expectedHttpResponse) + { + var request = new ExecuteRequest + { + Identifier = "org.n52.javaps.test.EchoProcess", + Inputs = new[] + { + new DataInput + { + Data = new LiteralDataValue{ Value = "test" } + } + }, + Outputs = new [] + { + new DataOutput + { + MimeType = "text/xml" + } + }, + ExecutionMode = ExecutionMode.Synchronous, + ResponseType = ResponseType.Document + }; + + var expectedRequestXml = new XmlSerializationService().Serialize(request); + + var wpsClient = new WpsClient(new HttpClient(GetMockedMessageHandlerForResponse(expectedHttpResponse, HttpStatusCode.OK, expectedRequestXml)), new XmlSerializationService()); + + var result = await wpsClient.GetDocumentedResult(MockUri, request); + result.Should().NotBeNull(); + result.Outputs.Should().HaveCount(1); + } + + [Fact] + public async Task GetDocumentedResult_NullWpsUriGiven_ShouldThrowArgumentNullException() + { + var wpsClient = new WpsClient(new HttpClient(), new XmlSerializationService()); + + await Assert.ThrowsAsync(() => wpsClient.GetDocumentedResult(null, new ExecuteRequest())); + } + + [Fact] + public async Task GetDocumentedResult_NullRequestGiven_ShouldThrowArgumentNullException() + { + var wpsClient = new WpsClient(new HttpClient(), new XmlSerializationService()); + + await Assert.ThrowsAsync(() => wpsClient.GetDocumentedResult(MockUri, null)); + } + + [Fact] + public async Task GetDocumentedResult_RawResponseTypeGiven_ShouldThrowInvalidOperationException() + { + var wpsClient = new WpsClient(new HttpClient(), new XmlSerializationService()); + + await Assert.ThrowsAsync(() => wpsClient.GetDocumentedResult(MockUri, new ExecuteRequest + { + ResponseType = ResponseType.Raw + })); + } + + [Fact] + public async Task GetDocumentedResult_SynchronousExecutionModeGiven_ShouldThrowInvalidOperationException() + { + var wpsClient = new WpsClient(new HttpClient(), new XmlSerializationService()); + + await Assert.ThrowsAsync(() => wpsClient.GetDocumentedResult(MockUri, new ExecuteRequest + { + ExecutionMode = ExecutionMode.Synchronous + })); + } + } } diff --git a/src/Wps/Wps.Client/Services/IWpsClient.cs b/src/Wps/Wps.Client/Services/IWpsClient.cs index 2ea06f7..c5e5f78 100644 --- a/src/Wps/Wps.Client/Services/IWpsClient.cs +++ b/src/Wps/Wps.Client/Services/IWpsClient.cs @@ -39,15 +39,6 @@ public interface IWpsClient /// Detailed information about the job status. Task GetJobStatus(string wpsUri, string jobId); - /// - /// Get the raw result of a synchronously executed request. - /// - /// The type of the result - /// The address pointing to the WPS server. - /// The execution request to be sent to the WPS server. - /// The result deserialized as . - Task GetRawResultAs(string wpsUri, ExecuteRequest request); - /// /// Get the raw result of a synchronously executed request. /// diff --git a/src/Wps/Wps.Client/Services/WpsClient.cs b/src/Wps/Wps.Client/Services/WpsClient.cs index e189d7e..1121efe 100644 --- a/src/Wps/Wps.Client/Services/WpsClient.cs +++ b/src/Wps/Wps.Client/Services/WpsClient.cs @@ -99,19 +99,30 @@ public async Task GetJobStatus(string wpsUri, string jobId) return result; } - public Task GetRawResultAs(string wpsUri, ExecuteRequest request) + public async Task GetRawResult(string wpsUri, ExecuteRequest request) { - throw new NotImplementedException(); - } + if (wpsUri == null) throw new ArgumentNullException(nameof(wpsUri)); + if (request == null) throw new ArgumentNullException(nameof(request)); - public Task GetRawResult(string wpsUri, ExecuteRequest request) - { - throw new NotImplementedException(); + if (request.ExecutionMode != ExecutionMode.Synchronous) throw new InvalidOperationException("Cannot execute request as auto or asynchronous in a synchronous session."); + if (request.ResponseType != ResponseType.Raw) throw new InvalidOperationException($"Raw response type is required for the request in function {nameof(GetRawResult)}."); + + var content = await GetRequestResult(wpsUri, request); + return content; } - public Task> GetDocumentedResult(string wpsUri, ExecuteRequest request) + public async Task> GetDocumentedResult(string wpsUri, ExecuteRequest request) { - throw new NotImplementedException(); + if (wpsUri == null) throw new ArgumentNullException(nameof(wpsUri)); + if (request == null) throw new ArgumentNullException(nameof(request)); + + if (request.ExecutionMode != ExecutionMode.Synchronous) throw new InvalidOperationException("Cannot execute request as auto or asynchronous in a synchronous session."); + if (request.ResponseType != ResponseType.Document) throw new InvalidOperationException($"Document response type is required for the request in function {nameof(GetDocumentedResult)}."); + + var content = await GetRequestResult(wpsUri, request); + var result = _serializationService.Deserialize>(content); + + return result; } public void Dispose() From ab682baaa0d340cc115b003f3ce84b0cefe7883b Mon Sep 17 00:00:00 2001 From: Mihai Stan Date: Fri, 14 Jun 2019 15:03:16 +0200 Subject: [PATCH 68/76] Implement missing properties for GetCapabilities request --- .../ModelSerializationTests.cs | 12 ++++++----- .../Resources/Requests/GetCapabilities.xml | 15 ++++++++++++++ .../Wps.Client.Tests/Wps.Client.Tests.csproj | 2 ++ .../Models/Requests/GetCapabilitiesRequest.cs | 20 ++++++++++++++++++- src/Wps/Wps.Client/Models/Requests/Request.cs | 6 ++++++ .../Wps.Client/Models/Requests/RequestBase.cs | 2 +- src/Wps/Wps.Client/Services/WpsClient.cs | 2 +- 7 files changed, 51 insertions(+), 8 deletions(-) create mode 100644 src/Wps/Wps.Client.Tests/Resources/Requests/GetCapabilities.xml create mode 100644 src/Wps/Wps.Client/Models/Requests/Request.cs diff --git a/src/Wps/Wps.Client.Tests/ModelSerializationTests.cs b/src/Wps/Wps.Client.Tests/ModelSerializationTests.cs index 630f70e..8708738 100644 --- a/src/Wps/Wps.Client.Tests/ModelSerializationTests.cs +++ b/src/Wps/Wps.Client.Tests/ModelSerializationTests.cs @@ -21,17 +21,19 @@ public ModelSerializationTests(XmlSerializationService serializer) _serializer = serializer; } - [Fact] - public void SerializeGetCapabilitiesRequest_ValidRequestGiven_ShouldPass() + [Theory] + [EmbeddedResourceData("Wps.Client.Tests/Resources/Requests/GetCapabilities.xml")] + public void SerializeGetCapabilitiesRequest_ValidRequestGiven_ShouldPass(string expectedXml) { - const string expectedXml = @""; - // Remove white spaces and new line characters for XML comparison. var trimmedExpectedXml = Regex.Replace(expectedXml, @"\s+", string.Empty); var request = new GetCapabilitiesRequest() { - Service = "WPS" + AcceptedFormats = new[] { "text/xml", "text/plain" }, + AcceptedVersions = new[] { "1.0.0", "2.0.0" }, + Sections = new[] { "section 1", "section 2" }, + UpdateSequence = "update sequence" }; var resultXml = _serializer.Serialize(request); diff --git a/src/Wps/Wps.Client.Tests/Resources/Requests/GetCapabilities.xml b/src/Wps/Wps.Client.Tests/Resources/Requests/GetCapabilities.xml new file mode 100644 index 0000000..6c9df81 --- /dev/null +++ b/src/Wps/Wps.Client.Tests/Resources/Requests/GetCapabilities.xml @@ -0,0 +1,15 @@ + + + + 1.0.0 + 2.0.0 + + + section 1 + section 2 + + + text/xml + text/plain + + \ No newline at end of file diff --git a/src/Wps/Wps.Client.Tests/Wps.Client.Tests.csproj b/src/Wps/Wps.Client.Tests/Wps.Client.Tests.csproj index ba28453..e109a05 100644 --- a/src/Wps/Wps.Client.Tests/Wps.Client.Tests.csproj +++ b/src/Wps/Wps.Client.Tests/Wps.Client.Tests.csproj @@ -11,6 +11,7 @@ + @@ -19,6 +20,7 @@ + diff --git a/src/Wps/Wps.Client/Models/Requests/GetCapabilitiesRequest.cs b/src/Wps/Wps.Client/Models/Requests/GetCapabilitiesRequest.cs index 264c3d9..d797885 100644 --- a/src/Wps/Wps.Client/Models/Requests/GetCapabilitiesRequest.cs +++ b/src/Wps/Wps.Client/Models/Requests/GetCapabilitiesRequest.cs @@ -6,8 +6,26 @@ namespace Wps.Client.Models.Requests { [Serializable] [XmlRoot("GetCapabilities", Namespace = ModelNamespaces.Wps)] - public class GetCapabilitiesRequest : RequestBase + public class GetCapabilitiesRequest : Request { + [XmlAttribute("updateSequence", Namespace = ModelNamespaces.Ows)] + public string UpdateSequence { get; set; } + + [XmlArray("AcceptVersions", Namespace = ModelNamespaces.Ows)] + [XmlArrayItem("Version", Namespace = ModelNamespaces.Ows)] + public string[] AcceptedVersions { get; set; } = {"2.0.0"}; + + [XmlArray("Sections", Namespace = ModelNamespaces.Ows)] + [XmlArrayItem("Section", Namespace = ModelNamespaces.Ows)] + public string[] Sections { get; set; } + + [XmlArray("AcceptFormats", Namespace = ModelNamespaces.Ows)] + [XmlArrayItem("OutputFormat", Namespace = ModelNamespaces.Ows)] + public string[] AcceptedFormats { get; set; } + + [XmlAttribute("service", Namespace = ModelNamespaces.Wps)] + public string Service { get; set; } = "WPS"; + } } \ No newline at end of file diff --git a/src/Wps/Wps.Client/Models/Requests/Request.cs b/src/Wps/Wps.Client/Models/Requests/Request.cs new file mode 100644 index 0000000..bf2d181 --- /dev/null +++ b/src/Wps/Wps.Client/Models/Requests/Request.cs @@ -0,0 +1,6 @@ +namespace Wps.Client.Models.Requests +{ + public abstract class Request + { + } +} diff --git a/src/Wps/Wps.Client/Models/Requests/RequestBase.cs b/src/Wps/Wps.Client/Models/Requests/RequestBase.cs index bfffb04..eb34790 100644 --- a/src/Wps/Wps.Client/Models/Requests/RequestBase.cs +++ b/src/Wps/Wps.Client/Models/Requests/RequestBase.cs @@ -2,7 +2,7 @@ namespace Wps.Client.Models.Requests { - public abstract class RequestBase + public abstract class RequestBase : Request { /// diff --git a/src/Wps/Wps.Client/Services/WpsClient.cs b/src/Wps/Wps.Client/Services/WpsClient.cs index 1121efe..4f8091b 100644 --- a/src/Wps/Wps.Client/Services/WpsClient.cs +++ b/src/Wps/Wps.Client/Services/WpsClient.cs @@ -23,7 +23,7 @@ public WpsClient(HttpClient httpClient, IXmlSerializer serializationService) _serializationService = serializationService; } - private async Task GetRequestResult(string wpsUri, RequestBase request) + private async Task GetRequestResult(string wpsUri, Request request) { if (wpsUri == null) throw new ArgumentNullException(nameof(wpsUri)); if (request == null) throw new ArgumentNullException(nameof(request)); From a706cb1ca885896cb16df768b626d8ae39cf9bde Mon Sep 17 00:00:00 2001 From: Mihai Stan Date: Mon, 17 Jun 2019 14:06:40 +0200 Subject: [PATCH 69/76] Manually deserialize StatusInfo to support nullable types --- src/Wps/Wps.Client/Models/StatusInfo.cs | 84 +++++++++++++++++++++---- 1 file changed, 73 insertions(+), 11 deletions(-) diff --git a/src/Wps/Wps.Client/Models/StatusInfo.cs b/src/Wps/Wps.Client/Models/StatusInfo.cs index f3b5aba..eb52322 100644 --- a/src/Wps/Wps.Client/Models/StatusInfo.cs +++ b/src/Wps/Wps.Client/Models/StatusInfo.cs @@ -1,4 +1,7 @@ using System; +using System.Globalization; +using System.Xml; +using System.Xml.Schema; using System.Xml.Serialization; using Wps.Client.Utils; @@ -8,44 +11,103 @@ namespace Wps.Client.Models /// Object used to provide identification and status information about a running job. /// [XmlRoot("StatusInfo", Namespace = ModelNamespaces.Wps)] - public class StatusInfo + public class StatusInfo : IXmlSerializable { /// /// Unambiguously identifier of a job within a WPS instance. /// - [XmlElement("JobID", Namespace = ModelNamespaces.Wps)] public string JobId { get; set; } /// /// Well-known identifier describing the status of the job. /// - [XmlElement("Status", Namespace = ModelNamespaces.Wps, Type = typeof(JobStatus))] public JobStatus Status { get; set; } /// /// Date and time by which the job and its results will be no longer accessible. /// - [XmlElement("ExpirationDate", Namespace = ModelNamespaces.Wps, Type = typeof(DateTime))] - public DateTime ExpirationDate { get; set; } + public DateTime? ExpirationDate { get; set; } /// /// Date and time by which the processing job will be finished. /// - [XmlElement("EstimatedCompletion", Namespace = ModelNamespaces.Wps, Type = typeof(DateTime))] - public DateTime EstimatedCompletion { get; set; } + public DateTime? EstimatedCompletion { get; set; } /// /// Date and time for the next suggested status polling. /// - [XmlElement("NextPoll", Namespace = ModelNamespaces.Wps, Type = typeof(DateTime))] - public DateTime NextPollDateTime { get; set; } + public DateTime? NextPollDateTime { get; set; } /// /// Percentage of process that has been completed. /// - [XmlElement("PercentCompleted", Namespace = ModelNamespaces.Wps)] - public int CompletionRate { get; set; } + public int? CompletionRate { get; set; } + public XmlSchema GetSchema() + { + return null; + } + + public void ReadXml(XmlReader reader) + { + var subtreeReader = reader.ReadSubtree(); + while (subtreeReader.Read()) + { + if(subtreeReader.NodeType == XmlNodeType.Element) + { + if (subtreeReader.LocalName.Equals("JobID")) + { + JobId = subtreeReader.ReadElementContentAsString(); + } + + if (subtreeReader.LocalName.Equals("Status")) + { + if(Enum.TryParse(subtreeReader.ReadElementContentAsString(), out JobStatus status)) + { + Status = status; + } + } + + if (subtreeReader.LocalName.Equals("ExpirationDate")) + { + if (DateTime.TryParse(subtreeReader.ReadElementContentAsString(), CultureInfo.InvariantCulture, DateTimeStyles.RoundtripKind, out var date)) + { + ExpirationDate = date; + } + } + + if (subtreeReader.LocalName.Equals("EstimatedCompletion")) + { + if (DateTime.TryParse(subtreeReader.ReadElementContentAsString(), CultureInfo.InvariantCulture, DateTimeStyles.RoundtripKind, out var date)) + { + EstimatedCompletion = date; + } + } + if (subtreeReader.LocalName.Equals("NextPoll")) + { + if (DateTime.TryParse(subtreeReader.ReadElementContentAsString(), CultureInfo.InvariantCulture, DateTimeStyles.RoundtripKind, out var date)) + { + NextPollDateTime = date; + } + } + + if (subtreeReader.LocalName.Equals("PercentCompleted")) + { + if (int.TryParse(subtreeReader.ReadElementContentAsString(), out var percent)) + { + CompletionRate = percent; + } + } + } + } + + reader.Skip(); + } + + public void WriteXml(XmlWriter writer) + { + throw new NotImplementedException(); + } } } From 17dba498c088d64a4e3e66e553a4f17f6a99f622 Mon Sep 17 00:00:00 2001 From: Mihai Stan Date: Mon, 17 Jun 2019 14:53:22 +0200 Subject: [PATCH 70/76] Add GetResult operation to the WPS client --- .../Resources/Responses/Result.xml | 10 ++++++ .../Wps.Client.Tests/Wps.Client.Tests.csproj | 2 ++ src/Wps/Wps.Client.Tests/WpsClientTests.cs | 32 +++++++++++++++++++ src/Wps/Wps.Client/Services/IWpsClient.cs | 12 ++++++- src/Wps/Wps.Client/Services/WpsClient.cs | 18 ++++++++++- 5 files changed, 72 insertions(+), 2 deletions(-) create mode 100644 src/Wps/Wps.Client.Tests/Resources/Responses/Result.xml diff --git a/src/Wps/Wps.Client.Tests/Resources/Responses/Result.xml b/src/Wps/Wps.Client.Tests/Resources/Responses/Result.xml new file mode 100644 index 0000000..641f997 --- /dev/null +++ b/src/Wps/Wps.Client.Tests/Resources/Responses/Result.xml @@ -0,0 +1,10 @@ + + + 6b3ef43a-ed8d-4063-8c65-e1171687f256 + 2019-06-17T14:17:53.910549Z + + + 150 + + + \ No newline at end of file diff --git a/src/Wps/Wps.Client.Tests/Wps.Client.Tests.csproj b/src/Wps/Wps.Client.Tests/Wps.Client.Tests.csproj index e109a05..3e53550 100644 --- a/src/Wps/Wps.Client.Tests/Wps.Client.Tests.csproj +++ b/src/Wps/Wps.Client.Tests/Wps.Client.Tests.csproj @@ -12,6 +12,7 @@ + @@ -21,6 +22,7 @@ + diff --git a/src/Wps/Wps.Client.Tests/WpsClientTests.cs b/src/Wps/Wps.Client.Tests/WpsClientTests.cs index bf7655e..cb1179b 100644 --- a/src/Wps/Wps.Client.Tests/WpsClientTests.cs +++ b/src/Wps/Wps.Client.Tests/WpsClientTests.cs @@ -167,6 +167,38 @@ public async Task GetStatus_NullWpsUriGiven_ShouldThrowArgumentNullException() await Assert.ThrowsAsync(() => wpsClient.GetJobStatus(null, string.Empty)); } + /* + * GetResult Tests + */ + + [Theory] + [EmbeddedResourceData("Wps.Client.Tests/Resources/Responses/Result.xml")] + public async Task GetResult_ValidJobIdGiven_ShouldReturnJobStatus(string httpClientResponseXml) + { + const string jobId = "6b3ef43a-ed8d-4063-8c65-e1171687f256"; + var wpsClient = new WpsClient(new HttpClient(GetMockedMessageHandlerForResponse(httpClientResponseXml)), new XmlSerializationService()); + + var result = await wpsClient.GetResult(MockUri, jobId); + result.JobId.Should().Be(jobId); + result.Outputs.Should().HaveCount(1); + } + + [Fact] + public async Task GetResult_NullJobIdGiven_ShouldThrowArgumentNullException() + { + var wpsClient = new WpsClient(new HttpClient(), new XmlSerializationService()); + + await Assert.ThrowsAsync(() => wpsClient.GetJobStatus(MockUri, null)); + } + + [Fact] + public async Task GetResult_NullWpsUriGiven_ShouldThrowArgumentNullException() + { + var wpsClient = new WpsClient(new HttpClient(), new XmlSerializationService()); + + await Assert.ThrowsAsync(() => wpsClient.GetJobStatus(null, string.Empty)); + } + /* * GetRawResult Tests */ diff --git a/src/Wps/Wps.Client/Services/IWpsClient.cs b/src/Wps/Wps.Client/Services/IWpsClient.cs index c5e5f78..27a0d94 100644 --- a/src/Wps/Wps.Client/Services/IWpsClient.cs +++ b/src/Wps/Wps.Client/Services/IWpsClient.cs @@ -1,4 +1,5 @@ -using System.Threading.Tasks; +using System; +using System.Threading.Tasks; using Wps.Client.Models; using Wps.Client.Models.Requests; using Wps.Client.Models.Responses; @@ -39,6 +40,15 @@ public interface IWpsClient /// Detailed information about the job status. Task GetJobStatus(string wpsUri, string jobId); + /// + /// Get the result of an asynchronously executed job. + /// + /// The type of the data included in the result. + /// The address pointing to the WPS server. + /// The id of the job to be whose result should be fetched. + /// The document containing additional information about the result and the output data. + Task> GetResult(string wpsUri, string jobId); + /// /// Get the raw result of a synchronously executed request. /// diff --git a/src/Wps/Wps.Client/Services/WpsClient.cs b/src/Wps/Wps.Client/Services/WpsClient.cs index 4f8091b..2b91ae4 100644 --- a/src/Wps/Wps.Client/Services/WpsClient.cs +++ b/src/Wps/Wps.Client/Services/WpsClient.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Linq; using System.Net.Http; using System.Text; @@ -99,6 +99,22 @@ public async Task GetJobStatus(string wpsUri, string jobId) return result; } + public async Task> GetResult(string wpsUri, string jobId) + { + if (wpsUri == null) throw new ArgumentNullException(nameof(wpsUri)); + if (jobId == null) throw new ArgumentNullException(nameof(jobId)); + + var request = new GetResultRequest + { + JobId = jobId + }; + + var content = await GetRequestResult(wpsUri, request); + var result =_serializationService.Deserialize>(content); + + return result; + } + public async Task GetRawResult(string wpsUri, ExecuteRequest request) { if (wpsUri == null) throw new ArgumentNullException(nameof(wpsUri)); From ac2d40cc83019e551aff4363f7df0c0987ca72aa Mon Sep 17 00:00:00 2001 From: Mihai Stan Date: Mon, 17 Jun 2019 16:26:36 +0200 Subject: [PATCH 71/76] Add GetExceptionForRequest method This method is mainly used when the user knows there is an expected exception thrown by the WPS server. --- src/Wps/Wps.Client.Tests/WpsClientTests.cs | 31 ++++++++++++++++++++++ src/Wps/Wps.Client/Services/IWpsClient.cs | 9 +++++++ src/Wps/Wps.Client/Services/WpsClient.cs | 23 +++++++++++++++- 3 files changed, 62 insertions(+), 1 deletion(-) diff --git a/src/Wps/Wps.Client.Tests/WpsClientTests.cs b/src/Wps/Wps.Client.Tests/WpsClientTests.cs index cb1179b..f129fdc 100644 --- a/src/Wps/Wps.Client.Tests/WpsClientTests.cs +++ b/src/Wps/Wps.Client.Tests/WpsClientTests.cs @@ -199,6 +199,36 @@ public async Task GetResult_NullWpsUriGiven_ShouldThrowArgumentNullException() await Assert.ThrowsAsync(() => wpsClient.GetJobStatus(null, string.Empty)); } + /* + * GetResult Tests + */ + + [Theory] + [EmbeddedResourceData("Wps.Client.Tests/Resources/ExceptionReport.xml")] + public async Task GetExceptionForRequest_ValidJobIdGiven_ShouldReturnJobStatus(string httpClientResponseXml) + { + var wpsClient = new WpsClient(new HttpClient(GetMockedMessageHandlerForResponse(httpClientResponseXml, HttpStatusCode.BadRequest)), new XmlSerializationService()); + + var result = await wpsClient.GetExceptionForRequest(MockUri, new GetResultRequest()); + result.Exceptions.Should().HaveCount(3); + } + + [Fact] + public async Task GetExceptionForRequest_NullJobIdGiven_ShouldThrowArgumentNullException() + { + var wpsClient = new WpsClient(new HttpClient(), new XmlSerializationService()); + + await Assert.ThrowsAsync(() => wpsClient.GetJobStatus(MockUri, null)); + } + + [Fact] + public async Task GetExceptionForRequest_NullWpsUriGiven_ShouldThrowArgumentNullException() + { + var wpsClient = new WpsClient(new HttpClient(), new XmlSerializationService()); + + await Assert.ThrowsAsync(() => wpsClient.GetJobStatus(null, string.Empty)); + } + /* * GetRawResult Tests */ @@ -266,6 +296,7 @@ public async Task GetRawResult_AsynchronousExecutionModeGiven_ShouldThrowInvalid ExecutionMode = ExecutionMode.Asynchronous })); } + /* * GetDocumentedResult Tests */ diff --git a/src/Wps/Wps.Client/Services/IWpsClient.cs b/src/Wps/Wps.Client/Services/IWpsClient.cs index 27a0d94..d218e76 100644 --- a/src/Wps/Wps.Client/Services/IWpsClient.cs +++ b/src/Wps/Wps.Client/Services/IWpsClient.cs @@ -1,6 +1,7 @@ using System; using System.Threading.Tasks; using Wps.Client.Models; +using Wps.Client.Models.Ows; using Wps.Client.Models.Requests; using Wps.Client.Models.Responses; @@ -32,6 +33,14 @@ public interface IWpsClient /// A collection of the processes offered. Task DescribeProcess(string wpsUri, params string[] processIdentifiers); + /// + /// Get the exception report for a given request. + /// + /// The address pointing to the WPS server. + /// The request to be sent to the WPS server. + /// The exception report generated by the faulty exception. + Task GetExceptionForRequest(string wpsUri, Request request); + /// /// Get the status of an ongoing job. /// diff --git a/src/Wps/Wps.Client/Services/WpsClient.cs b/src/Wps/Wps.Client/Services/WpsClient.cs index 2b91ae4..7a278e2 100644 --- a/src/Wps/Wps.Client/Services/WpsClient.cs +++ b/src/Wps/Wps.Client/Services/WpsClient.cs @@ -14,6 +14,8 @@ namespace Wps.Client.Services public class WpsClient : IWpsClient, IDisposable { + private const string RequestMimeType = "text/xml"; + private readonly HttpClient _httpClient; private readonly IXmlSerializer _serializationService; @@ -30,7 +32,7 @@ private async Task GetRequestResult(string wpsUri, Request request) var requestXml = _serializationService.Serialize(request); var response = await _httpClient.PostAsync(wpsUri, - new StringContent(requestXml, Encoding.UTF8, "text/xml")); + new StringContent(requestXml, Encoding.UTF8, RequestMimeType)); var content = await response.Content.ReadAsStringAsync(); if (response.IsSuccessStatusCode) @@ -83,6 +85,25 @@ public async Task DescribeProcess(string wpsUri, para return result; } + public async Task GetExceptionForRequest(string wpsUri, Request request) + { + if (wpsUri == null) throw new ArgumentNullException(nameof(wpsUri)); + if (request == null) throw new ArgumentNullException(nameof(request)); + + var requestXml = _serializationService.Serialize(request); + var response = await _httpClient.PostAsync(wpsUri, + new StringContent(requestXml, Encoding.UTF8, RequestMimeType)); + + var content = await response.Content.ReadAsStringAsync(); + if (response.IsSuccessStatusCode) + { + throw new InvalidOperationException($"Expected an ExceptionReport but the answer from the server was successful, {response.StatusCode}."); + } + + var exceptionReport = _serializationService.Deserialize(content); + return exceptionReport; + } + public async Task GetJobStatus(string wpsUri, string jobId) { if (wpsUri == null) throw new ArgumentNullException(nameof(wpsUri)); From 30b9f396b8f5c78ae993d51a9140a80f5ff040c7 Mon Sep 17 00:00:00 2001 From: Mihai Stan Date: Mon, 17 Jun 2019 17:43:27 +0200 Subject: [PATCH 72/76] Add session for the asynchronous based WPS model This model is only used in an asynchronous context, providing useful events to the user so he can track progress of the entire interaction without having to do the polling work. --- src/Wps/Wps.Client.Tests/SessionTests.cs | 191 ++++++++++++++++++ .../Arguments/SessionFailedEventArgs.cs | 17 ++ .../Arguments/SessionFinishedEventArgs.cs | 17 ++ .../Arguments/SessionPolledEventArgs.cs | 18 ++ src/Wps/Wps.Client/Services/Session.cs | 113 +++++++++++ 5 files changed, 356 insertions(+) create mode 100644 src/Wps/Wps.Client.Tests/SessionTests.cs create mode 100644 src/Wps/Wps.Client/Services/Arguments/SessionFailedEventArgs.cs create mode 100644 src/Wps/Wps.Client/Services/Arguments/SessionFinishedEventArgs.cs create mode 100644 src/Wps/Wps.Client/Services/Arguments/SessionPolledEventArgs.cs create mode 100644 src/Wps/Wps.Client/Services/Session.cs diff --git a/src/Wps/Wps.Client.Tests/SessionTests.cs b/src/Wps/Wps.Client.Tests/SessionTests.cs new file mode 100644 index 0000000..b72ae99 --- /dev/null +++ b/src/Wps/Wps.Client.Tests/SessionTests.cs @@ -0,0 +1,191 @@ +using FluentAssertions; +using System; +using System.Threading.Tasks; +using Wps.Client.Models; +using Wps.Client.Models.Execution; +using Wps.Client.Models.Ows; +using Wps.Client.Models.Requests; +using Wps.Client.Models.Responses; +using Wps.Client.Services; +using Xunit; + +namespace Wps.Client.Tests +{ + public class SessionTests + { + + private const string JobId = "test-job-id"; + + [Fact] + public async Task CreateSession_FailedJobGiven_ShouldRaiseFailedEvent() + { + ExceptionReport exceptionReport = null; + var session = new Session(new MockWpsClient(new StatusInfo + { + Status = JobStatus.Failed + }), string.Empty, JobId); + + session.Failed += (sender, args) => { exceptionReport = args.ExceptionReport; }; + + await session.StartPolling(); + + exceptionReport.Should().NotBeNull(); + } + + [Fact] + public void CreateSession_RunningJobGiven_ShouldRaisePolledEvent() + { + var expectedNextPollDate = new DateTime(20, 1, 20, 20, 20, 20); + var receivedNextPollDate = DateTime.MinValue; + + var session = new Session(new MockWpsClient(new StatusInfo + { + Status = JobStatus.Running, + NextPollDateTime = expectedNextPollDate + }), string.Empty, JobId); + + session.Polled += (sender, args) => { receivedNextPollDate = args.NextPollAt; }; + + session.StartPolling().Wait(TimeSpan.FromSeconds(4)); + + receivedNextPollDate.Should().Be(expectedNextPollDate); + } + + [Fact] + public void CreateSession_SucceededJobGiven_ShouldRaiseFinishedEvent() + { + Result resultDocument = null; + + var session = new Session(new MockWpsClient(new StatusInfo + { + Status = JobStatus.Succeeded, + }), string.Empty, JobId); + + session.Finished += (sender, args) => + { + resultDocument = args.Result; + }; + + session.StartPolling().Wait(TimeSpan.FromSeconds(4)); + + resultDocument.JobId.Should().Be(JobId); + resultDocument.ExpirationDate.Should().NotBe(DateTime.MinValue); + resultDocument.Outputs.Should().HaveCount(1); + } + + [Fact] + public void PollAtNextDate_RunningJobGiven_With_5_SecondInterval_ShouldPollOnceWithin4Seconds() + { + var pollCount = 0; + + var session = new Session(new MockWpsClient(new StatusInfo + { + Status = JobStatus.Running, + NextPollDateTime = DateTime.Now.AddSeconds(5), + }), string.Empty, JobId); + + session.Polled += (sender, args) => { pollCount++; }; + + session.StartPolling().Wait(TimeSpan.FromSeconds(4)); + + pollCount.Should().Be(1); + } + + [Fact] + public void PollAtNextDate_RunningJobGiven_With_DefaultPollInterval_ShouldPollFourTimesWithin7Seconds() + { + var pollCount = 0; + + var statusInfo = new StatusInfo + { + Status = JobStatus.Running, + }; + + var session = new Session(new MockWpsClient(statusInfo), string.Empty, JobId); + + session.Polled += (sender, args) => + { + pollCount++; + }; + + session.StartPolling().Wait(TimeSpan.FromSeconds(7)); + + pollCount.Should().Be(4); + } + + private class MockWpsClient : IWpsClient + { + + private readonly StatusInfo _jobStatusReturn; + + public MockWpsClient(StatusInfo jobStatusReturn) + { + _jobStatusReturn = jobStatusReturn; + } + + public Task GetCapabilities(string wpsUri) + { + throw new NotImplementedException(); + } + + public Task DescribeProcess(string wpsUri, params ProcessSummary[] processSummaries) + { + throw new NotImplementedException(); + } + + public Task DescribeProcess(string wpsUri, params string[] processIdentifiers) + { + throw new NotImplementedException(); + } + + public Task GetExceptionForRequest(string wpsUri, Request request) + { + if (request is GetResultRequest req && req.JobId.Equals(JobId)) + { + return Task.FromResult(new ExceptionReport()); + } + + return null; + } + + public Task GetJobStatus(string wpsUri, string jobId) + { + return Task.FromResult(_jobStatusReturn); + } + + public Task> GetResult(string wpsUri, string jobId) + { + if (jobId.Equals(JobId)) + { + return Task.FromResult(new Result + { + JobId = JobId, + ExpirationDate = DateTime.Now, + Outputs = new[] + { + new ResultOutput() + } + }); + } + + throw new InvalidOperationException($"Job id should have been ${JobId}."); + } + + public Task GetRawResult(string wpsUri, ExecuteRequest request) + { + throw new NotImplementedException(); + } + + public Task> GetDocumentedResult(string wpsUri, ExecuteRequest request) + { + throw new NotImplementedException(); + } + + public Task> AsyncGetDocumentResultAs(string wpsUri, ExecuteRequest request) + { + throw new NotImplementedException(); + } + } + + } +} diff --git a/src/Wps/Wps.Client/Services/Arguments/SessionFailedEventArgs.cs b/src/Wps/Wps.Client/Services/Arguments/SessionFailedEventArgs.cs new file mode 100644 index 0000000..4447c5c --- /dev/null +++ b/src/Wps/Wps.Client/Services/Arguments/SessionFailedEventArgs.cs @@ -0,0 +1,17 @@ +using System; +using Wps.Client.Models.Ows; + +namespace Wps.Client.Services.Arguments +{ + public class SessionFailedEventArgs : EventArgs + { + + public ExceptionReport ExceptionReport { get; } + + public SessionFailedEventArgs(ExceptionReport exceptionReport) + { + ExceptionReport = exceptionReport; + } + + } +} diff --git a/src/Wps/Wps.Client/Services/Arguments/SessionFinishedEventArgs.cs b/src/Wps/Wps.Client/Services/Arguments/SessionFinishedEventArgs.cs new file mode 100644 index 0000000..f4efee4 --- /dev/null +++ b/src/Wps/Wps.Client/Services/Arguments/SessionFinishedEventArgs.cs @@ -0,0 +1,17 @@ +using System; +using Wps.Client.Models; + +namespace Wps.Client.Services.Arguments +{ + public class SessionFinishedEventArgs : EventArgs + { + + public Result Result { get; } + + public SessionFinishedEventArgs(Result result) + { + Result = result; + } + + } +} diff --git a/src/Wps/Wps.Client/Services/Arguments/SessionPolledEventArgs.cs b/src/Wps/Wps.Client/Services/Arguments/SessionPolledEventArgs.cs new file mode 100644 index 0000000..77fe99b --- /dev/null +++ b/src/Wps/Wps.Client/Services/Arguments/SessionPolledEventArgs.cs @@ -0,0 +1,18 @@ +using System; + +namespace Wps.Client.Services.Arguments +{ + public class SessionPolledEventArgs : EventArgs + { + + public DateTime PolledAt { get; } + public DateTime NextPollAt { get; } + + public SessionPolledEventArgs(DateTime polledAt, DateTime nextPollAt) + { + PolledAt = polledAt; + NextPollAt = nextPollAt; + } + + } +} diff --git a/src/Wps/Wps.Client/Services/Session.cs b/src/Wps/Wps.Client/Services/Session.cs new file mode 100644 index 0000000..1ae50fd --- /dev/null +++ b/src/Wps/Wps.Client/Services/Session.cs @@ -0,0 +1,113 @@ +using System; +using System.Threading; +using System.Threading.Tasks; +using Wps.Client.Models; +using Wps.Client.Models.Requests; +using Wps.Client.Services.Arguments; +using Task = System.Threading.Tasks.Task; + +namespace Wps.Client.Services +{ + public class Session + { + + /// + /// Event raised when the session has completed successfully. + /// + public event EventHandler> Finished; + + /// + /// Event raised when there was error during the execution. + /// + public event EventHandler Failed; + + /// + /// Event raised when the session polling system has polled the server for the job status. + /// + public event EventHandler Polled; + + /// + /// This is the interval that the next polling is going to occur if no next date polling has been given from the server. + /// + private const double DefaultNextPollInterval = 2.0; + + public string JobId { get; } + public bool IsRunning { get; private set; } + + private readonly IWpsClient _wpsClient; + private readonly string _wpsUri; + private readonly CancellationTokenSource _cancellationTokenSource; + + private DateTime _nextPoll; + private DateTime _lastPoll; + + public Session(IWpsClient wpsClient, string wpsUri, string jobId) + { + _wpsClient = wpsClient ?? throw new ArgumentNullException(nameof(wpsClient)); + _wpsUri = wpsUri ?? throw new ArgumentNullException(nameof(_wpsUri)); + JobId = jobId ?? throw new ArgumentNullException(nameof(jobId)); + + _cancellationTokenSource = new CancellationTokenSource(); + } + + /// + /// Start the indefinitely long polling task. It is ran in LongRunning mode which ends up creating a new separate thread for the task. + /// + /// The task containing the polling calls + public Task StartPolling() + { + IsRunning = true; + return Task.Factory.StartNew(() => + { + do + { + var jobStatus = _wpsClient.GetJobStatus(_wpsUri, JobId).Result; + switch (jobStatus.Status) + { + case JobStatus.Running: + if (jobStatus.NextPollDateTime.HasValue) + { + _nextPoll = jobStatus.NextPollDateTime.Value; + } + else + { + _nextPoll = DateTime.Now + TimeSpan.FromSeconds(DefaultNextPollInterval); + } + + _lastPoll = DateTime.Now; + Polled?.Invoke(this, new SessionPolledEventArgs(DateTime.Now, _nextPoll)); + break; + case JobStatus.Succeeded: + var result = _wpsClient.GetResult(_wpsUri, JobId).Result; + + Finished?.Invoke(this, new SessionFinishedEventArgs(result)); + _cancellationTokenSource.Cancel(); + break; + case JobStatus.Failed: + var exceptionReport = _wpsClient.GetExceptionForRequest(_wpsUri, new GetResultRequest + { + JobId = JobId + }).Result; + + Failed?.Invoke(this, new SessionFailedEventArgs(exceptionReport)); + _cancellationTokenSource.Cancel(); + break; + } + + if (!_cancellationTokenSource.IsCancellationRequested) + { + if(_nextPoll > _lastPoll) + { + Task.Delay(_nextPoll - _lastPoll).Wait(); + } + } + + } while (!_cancellationTokenSource.Token.IsCancellationRequested); + + IsRunning = false; + }, _cancellationTokenSource.Token, TaskCreationOptions.LongRunning, + TaskScheduler.Current); + } + + } +} From 00184ae49affd2fb496de85f2c9778ba42accc7c Mon Sep 17 00:00:00 2001 From: Mihai Stan Date: Mon, 17 Jun 2019 18:00:50 +0200 Subject: [PATCH 73/76] Add asynchronous request execution call to the WPS client --- .../Resources/Responses/AcceptedJobStatus.xml | 5 ++ .../Wps.Client.Tests/Wps.Client.Tests.csproj | 2 + src/Wps/Wps.Client.Tests/WpsClientTests.cs | 78 +++++++++++++++++++ src/Wps/Wps.Client/Services/IWpsClient.cs | 11 ++- src/Wps/Wps.Client/Services/WpsClient.cs | 22 +++++- 5 files changed, 116 insertions(+), 2 deletions(-) create mode 100644 src/Wps/Wps.Client.Tests/Resources/Responses/AcceptedJobStatus.xml diff --git a/src/Wps/Wps.Client.Tests/Resources/Responses/AcceptedJobStatus.xml b/src/Wps/Wps.Client.Tests/Resources/Responses/AcceptedJobStatus.xml new file mode 100644 index 0000000..c313b07 --- /dev/null +++ b/src/Wps/Wps.Client.Tests/Resources/Responses/AcceptedJobStatus.xml @@ -0,0 +1,5 @@ + + + test-job-id + Accepted + \ No newline at end of file diff --git a/src/Wps/Wps.Client.Tests/Wps.Client.Tests.csproj b/src/Wps/Wps.Client.Tests/Wps.Client.Tests.csproj index 3e53550..7033ca1 100644 --- a/src/Wps/Wps.Client.Tests/Wps.Client.Tests.csproj +++ b/src/Wps/Wps.Client.Tests/Wps.Client.Tests.csproj @@ -12,6 +12,7 @@ + @@ -22,6 +23,7 @@ + diff --git a/src/Wps/Wps.Client.Tests/WpsClientTests.cs b/src/Wps/Wps.Client.Tests/WpsClientTests.cs index f129fdc..4175760 100644 --- a/src/Wps/Wps.Client.Tests/WpsClientTests.cs +++ b/src/Wps/Wps.Client.Tests/WpsClientTests.cs @@ -8,11 +8,13 @@ using System.Threading; using System.Threading.Tasks; using Wps.Client.Models; +using Wps.Client.Models.Data; using Wps.Client.Models.Execution; using Wps.Client.Models.Ows; using Wps.Client.Models.Requests; using Wps.Client.Services; using Xunit; +using Xunit.Sdk; namespace Wps.Client.Tests { @@ -373,6 +375,82 @@ public async Task GetDocumentedResult_SynchronousExecutionModeGiven_ShouldThrowI })); } + /* + * GetDocumentedResult Tests + */ + + [Theory] + [EmbeddedResourceData("Wps.Client.Tests/Resources/Responses/AcceptedJobStatus.xml")] + public async Task AsyncGetDocumentedResult_ValidRequestGiven_ShouldReturnResult(string expectedHttpResponse) + { + var request = new ExecuteRequest + { + Identifier = "org.n52.javaps.test.EchoProcess", + Inputs = new[] + { + new DataInput + { + Data = new LiteralDataValue{ Value = "test" } + } + }, + Outputs = new [] + { + new DataOutput + { + MimeType = "text/xml" + } + }, + ExecutionMode = ExecutionMode.Asynchronous, + ResponseType = ResponseType.Document + }; + + var expectedRequestXml = new XmlSerializationService().Serialize(request); + + var wpsClient = new WpsClient(new HttpClient(GetMockedMessageHandlerForResponse(expectedHttpResponse, HttpStatusCode.OK, expectedRequestXml)), new XmlSerializationService()); + + var session = await wpsClient.AsyncGetDocumentResultAs(MockUri, request); + session.Should().NotBeNull(); + session.JobId.Should().Be("test-job-id"); + } + + [Fact] + public async Task AsyncGetDocumentedResult_NullWpsUriGiven_ShouldThrowArgumentNullException() + { + var wpsClient = new WpsClient(new HttpClient(), new XmlSerializationService()); + + await Assert.ThrowsAsync(() => wpsClient.AsyncGetDocumentResultAs(null, new ExecuteRequest())); + } + + [Fact] + public async Task AsyncGetDocumentedResult_NullRequestGiven_ShouldThrowArgumentNullException() + { + var wpsClient = new WpsClient(new HttpClient(), new XmlSerializationService()); + + await Assert.ThrowsAsync(() => wpsClient.AsyncGetDocumentResultAs(MockUri, null)); + } + + [Fact] + public async Task AsyncGetDocumentedResult_RawResponseTypeGiven_ShouldThrowInvalidOperationException() + { + var wpsClient = new WpsClient(new HttpClient(), new XmlSerializationService()); + + await Assert.ThrowsAsync(() => wpsClient.GetDocumentedResult(MockUri, new ExecuteRequest + { + ResponseType = ResponseType.Raw + })); + } + + [Fact] + public async Task AsyncGetDocumentedResult_SynchronousExecutionModeGiven_ShouldThrowInvalidOperationException() + { + var wpsClient = new WpsClient(new HttpClient(), new XmlSerializationService()); + + await Assert.ThrowsAsync(() => wpsClient.GetDocumentedResult(MockUri, new ExecuteRequest + { + ExecutionMode = ExecutionMode.Synchronous + })); + } + } } diff --git a/src/Wps/Wps.Client/Services/IWpsClient.cs b/src/Wps/Wps.Client/Services/IWpsClient.cs index d218e76..d8cae31 100644 --- a/src/Wps/Wps.Client/Services/IWpsClient.cs +++ b/src/Wps/Wps.Client/Services/IWpsClient.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Threading.Tasks; using Wps.Client.Models; using Wps.Client.Models.Ows; @@ -75,5 +75,14 @@ public interface IWpsClient /// The document containing additional information about the result and the output data. Task> GetDocumentedResult(string wpsUri, ExecuteRequest request); + /// + /// Get the documented result of an asynchronously executed request. + /// + /// The type of the data included in the result. + /// The address pointing to the WPS server. + /// The execution request to be sent to the WPS server. + /// A session containing the events raised when different actions occur at every poll. + Task> AsyncGetDocumentResultAs(string wpsUri, ExecuteRequest request); + } } diff --git a/src/Wps/Wps.Client/Services/WpsClient.cs b/src/Wps/Wps.Client/Services/WpsClient.cs index 7a278e2..5ba14dc 100644 --- a/src/Wps/Wps.Client/Services/WpsClient.cs +++ b/src/Wps/Wps.Client/Services/WpsClient.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Linq; using System.Net.Http; using System.Text; @@ -162,6 +162,26 @@ public async Task> GetDocumentedResult(string wpsUri, Execu return result; } + public async Task> AsyncGetDocumentResultAs(string wpsUri, ExecuteRequest request) + { + if (wpsUri == null) throw new ArgumentNullException(nameof(wpsUri)); + if (request == null) throw new ArgumentNullException(nameof(request)); + + if (request.ExecutionMode != ExecutionMode.Asynchronous) throw new InvalidOperationException("Cannot execute request as auto or synchronous in an asynchronous session."); + if (request.ResponseType != ResponseType.Document) throw new InvalidOperationException($"Document response type is required for the request in function {nameof(AsyncGetDocumentResultAs)}."); + + var content = await GetRequestResult(wpsUri, request); + var result = _serializationService.Deserialize(content); + + if (result.Status == JobStatus.Accepted || result.Status == JobStatus.Running) + { + var session = new Session(this, wpsUri, result.JobId); + return session; + } + + throw new Exception($"The execution request could not be started. (Response status: {result.Status})"); + } + public void Dispose() { _httpClient?.Dispose(); From bcc168a1e3c0ff3ae9bfa13e3b5924dadf202d0b Mon Sep 17 00:00:00 2001 From: Mihai Stan Date: Mon, 17 Jun 2019 18:15:23 +0200 Subject: [PATCH 74/76] Remove unused file --- src/Wps/Wps.Client.Tests/UnitTest1.cs | 13 ------------- 1 file changed, 13 deletions(-) delete mode 100644 src/Wps/Wps.Client.Tests/UnitTest1.cs diff --git a/src/Wps/Wps.Client.Tests/UnitTest1.cs b/src/Wps/Wps.Client.Tests/UnitTest1.cs deleted file mode 100644 index 64f709c..0000000 --- a/src/Wps/Wps.Client.Tests/UnitTest1.cs +++ /dev/null @@ -1,13 +0,0 @@ -using Xunit; - -namespace Wps.Client.Tests -{ - public class UnitTest1 - { - [Fact] - public void Test1() - { - - } - } -} From c5a77abf447ce89b9b73768c87e8de8b11dba542 Mon Sep 17 00:00:00 2001 From: Mihai Stan Date: Fri, 21 Jun 2019 00:02:22 +0200 Subject: [PATCH 75/76] Update README.md --- README.md | 90 ++++++++++++++++++++++++++++++++++++++++++++-- media/52n-logo.svg | 40 +++++++++++++++++++++ 2 files changed, 128 insertions(+), 2 deletions(-) create mode 100644 media/52n-logo.svg diff --git a/README.md b/README.md index f849e0e..9a7936f 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,88 @@ -# wpsclient4arcgispro -This repository contains a .NET WPS client for ArcGIS Pro. +

+ +

WPS.NET

+

+ +

+ Description • + Installation • + Usage • + License +

+ +# WPS.NET + +## Description + +This project was created initially during the [2019th GSoC](https://summerofcode.withgoogle.com/projects/#6595064984240128). + +Its purpose is mainly to allow users to integrate the WPS standard in their program seamlessly without having to reimplement the specification every time. The library was built in C# to integrate with the .NET ecosystem. It is easily extensible and featureful providing the users the two main implementation of execution types, synchronous and asynchronous. + +## Installation + +You can get the package from NuGet. + +### dotnet CLI + +```dotnet add package WPS.NET``` + +### PowerShell + +```Install-Package WPS.NET``` + +## Usage + +### Getting the server capabilities + +```cs +var client = new WpsClient(httpClient, xmlSerializer); +var capabilities = await client.GetCapabilities("server.uri"); +``` + +### Describing a process + +```cs +var client = new WpsClient(httpClient, xmlSerializer); +var processOfferings = await client.DescribeProcess("server.uri", "process identifier"); +``` + +### Creating a request + +```cs +var request = new ExecuteRequest +{ + Identifier = "process identifier", + Inputs = new[] + { + new DataInput + { + Identifier = "input identifier", + Data = new LiteralDataValue{ Value = "test" } + } + }, + Outputs = new[] + { + new DataOutput + { + Identifier = "output identifier" + } + }, + ExecutionMode = ExecutionMode.Synchronous, + ResponseType = ResponseType.Raw +}; +``` + +### Executing a request + +```cs +var client = new WpsClient(httpClient, xmlSerializer); +var session = await client.AsyncGetDocumentResultAs("server.uri", request); + +var pollingTask = session.StartPolling(); +session.Finished += (sender, args) => { /* Use the result here. */ }; +session.Failed += (sender, args) => { /* The execution has failed. */ }; +session.Polled += (sender, args) => { /* The sesssion poller has checked for the status. */ }; +``` + +## License +[Apache v2.0](https://www.apache.org/licenses/LICENSE-2.0) \ No newline at end of file diff --git a/media/52n-logo.svg b/media/52n-logo.svg new file mode 100644 index 0000000..a86ee4d --- /dev/null +++ b/media/52n-logo.svg @@ -0,0 +1,40 @@ + + + +image/svg+xml \ No newline at end of file From cc50e543e84839e71399ca1a0fc09b602b352f6c Mon Sep 17 00:00:00 2001 From: Mihai Date: Mon, 24 Jun 2019 14:10:12 +0200 Subject: [PATCH 76/76] Update README.md --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 9a7936f..231c81b 100644 --- a/README.md +++ b/README.md @@ -22,11 +22,11 @@ Its purpose is mainly to allow users to integrate the WPS standard in their prog You can get the package from NuGet. -### dotnet CLI +### .NET CLI ```dotnet add package WPS.NET``` -### PowerShell +### Packet Manager ```Install-Package WPS.NET``` @@ -85,4 +85,4 @@ session.Polled += (sender, args) => { /* The sesssion poller has checked for the ``` ## License -[Apache v2.0](https://www.apache.org/licenses/LICENSE-2.0) \ No newline at end of file +[Apache v2.0](https://www.apache.org/licenses/LICENSE-2.0)