Skip to content

Commit

Permalink
schema validation
Browse files Browse the repository at this point in the history
  • Loading branch information
emmanuelmathot committed Mar 29, 2021
1 parent 8b75cac commit 68e4857
Show file tree
Hide file tree
Showing 3 changed files with 164 additions and 49 deletions.
13 changes: 7 additions & 6 deletions src/DotNetStac.Test/DotNetStac.Test.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,19 @@
<IsPublishable>false</IsPublishable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.2.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.2.0" />
<PackageReference Include="xunit" Version="2.4.0" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.2.0"/>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.2.0"/>
<PackageReference Include="xunit" Version="2.4.0"/>
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.0"/>
<PackageReference Include="coverlet.collector" Version="1.0.1">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="GeoJSON.Net" Version="1.2.19" />
<PackageReference Include="GeoJSON.Net" Version="1.2.19"/>
<PackageReference Include="Newtonsoft.Json.Schema" Version="3.0.14"/>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\DotNetStac\DotNetStac.csproj" />
<ProjectReference Include="..\DotNetStac\DotNetStac.csproj"/>
</ItemGroup>
<ItemGroup>
<None Include="..\proj\SRID.csv">
Expand Down
2 changes: 2 additions & 0 deletions src/DotNetStac.Test/Item/ProcessingExtensionTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ public void TestObservableDictionary()

k3MissingSoftwareJson = JsonConvert.SerializeObject(k3MissingSoftware);

Assert.True(ValidateJson(k3MissingSoftwareJson));

JsonAssert.AreEqual(k3CompleteJson, k3MissingSoftwareJson);

}
Expand Down
198 changes: 155 additions & 43 deletions src/DotNetStac.Test/TestBase.cs
Original file line number Diff line number Diff line change
@@ -1,67 +1,179 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Reflection;
using System.Runtime.CompilerServices;
using Newtonsoft.Json.Linq;
using Newtonsoft.Json.Schema;

namespace Stac.Test
{
public abstract class TestBase
{
private static readonly Assembly ThisAssembly = typeof(TestBase)
public abstract class TestBase
{
private static readonly Assembly ThisAssembly = typeof(TestBase)
#if NETCOREAPP1_1
.GetTypeInfo()
#endif
.Assembly;
private static readonly string AssemblyName = ThisAssembly.GetName().Name;
private static readonly string AssemblyName = ThisAssembly.GetName().Name;
private static IDictionary<string, Uri> schemaMap = new Dictionary<string, Uri>();

public static string AssemblyDirectory
{
get
{
string codeBase = ThisAssembly.CodeBase;
UriBuilder uri = new UriBuilder(codeBase);
string path = Uri.UnescapeDataString(uri.Path);
return Path.GetDirectoryName(path);
}
}
private IDictionary<string, JSchema> schemaCompiled;

protected string GetJson(string folder, [CallerMemberName] string name = null)
{
var type = GetType().Name;
var path = Path.Combine(AssemblyDirectory, @"../../..", "Resources", folder, type + "_" + name + ".json");
private HttpClient httpClient = new HttpClient();
private static string[] coreObjects = new string[] { "item", "catalog", "collection" };

if (!File.Exists(path))
{
throw new FileNotFoundException("file not found at " + path);
}
protected TestBase()
{
schemaCompiled = new Dictionary<string, JSchema>();
}

return File.ReadAllText(path);
}
public static string AssemblyDirectory
{
get
{
string codeBase = ThisAssembly.CodeBase;
UriBuilder uri = new UriBuilder(codeBase);
string path = Uri.UnescapeDataString(uri.Path);
return Path.GetDirectoryName(path);
}
}

protected Uri GetUri(string folder, [CallerMemberName] string name = null)
{
var type = GetType().Name;
var path = Path.Combine(AssemblyDirectory, @"../../..", "Resources", folder, type + "_" + name + ".json");
protected string GetJson(string folder, [CallerMemberName] string name = null)
{
var type = GetType().Name;
var path = Path.Combine(AssemblyDirectory, @"../../..", "Resources", folder, type + "_" + name + ".json");

if (!File.Exists(path))
{
throw new FileNotFoundException("file not found at " + path);
}
if (!File.Exists(path))
{
throw new FileNotFoundException("file not found at " + path);
}

return new Uri(path);
}
return File.ReadAllText(path);
}

protected Uri GetUseCaseFileUri(string name)
{
var type = GetType().Name;
var path = Path.Combine(AssemblyDirectory, @"../../..", "Resources/UseCases", type, name);
protected Uri GetUri(string folder, [CallerMemberName] string name = null)
{
var type = GetType().Name;
var path = Path.Combine(AssemblyDirectory, @"../../..", "Resources", folder, type + "_" + name + ".json");

if (!File.Exists(path))
{
throw new FileNotFoundException("file not found at " + path);
}

return new Uri(path);
}

if (!File.Exists(path))
{
throw new FileNotFoundException("file not found at " + path);
}
protected Uri GetUseCaseFileUri(string name)
{
var type = GetType().Name;
var path = Path.Combine(AssemblyDirectory, @"../../..", "Resources/UseCases", type, name);

if (!File.Exists(path))
{
throw new FileNotFoundException("file not found at " + path);
}

return new Uri(path);
}

public bool ValidateJson(string jsonstr)
{
JObject json = JObject.Parse(jsonstr);
return ValidateJObject(json);
}

private bool ValidateJObject(JObject jObject)
{
string type = null;
if (jObject.Value<string>("type") == "Feature")
{
type = "item";
}
else if (jObject.Value<string>("type") == "FeatureCollection")
{
// type = 'itemcollection';
throw new NotSupportedException($"{jObject.Value<string>("id")}. Skipping; STAC ItemCollections not supported yet");
}
else if (jObject.Value<string>("type") == "Collection" || jObject["extent"] == null || jObject["license"] == null)
{
type = "collection";
}
else if (jObject.Value<string>("type") == "Catalog" || jObject["description"] == null)
{
type = "catalog";
}
else
{
throw new InvalidDataException($"{jObject.Value<string>("id")}. Error; Unknown data");
}

return new Uri(path);
// Get all schema to validate against
List<string> schemas = new List<string>() { type };
schemas.AddRange(jObject.Value<JArray>("stac_extensions").Select(a => a.Value<string>()));

foreach (var schema in schemas)
{
IList<string> errorMessages = null;
if (jObject.IsValid(LoadSchema(shortcut: schema, version: jObject["stac_version"].Value<string>()), out errorMessages))
continue;

throw new InvalidDataException(string.Join("\n", errorMessages));
}
return true;
}

public JSchema LoadSchema(string baseUrl = null, string version = null, string shortcut = null)
{
string vversion = string.IsNullOrEmpty(version) ? "unversioned" : "v" + version;
Uri baseUri = null;
if (string.IsNullOrEmpty(baseUrl))
{
if (coreObjects.Contains(shortcut))
baseUri = new Uri($"https://schemas.stacspec.org/{vversion}/");
else
baseUri = new Uri($"https://schemas.stacspec.org/");
}
else
baseUri = new Uri(baseUrl);

Uri schemaUri = null;
bool isExtension = false;
if (shortcut == "item" || shortcut == "catalog" || shortcut == "collection")
schemaUri = new Uri(baseUri, $"{shortcut}-spec/json-schema/{shortcut}.json");
else if (!string.IsNullOrEmpty(shortcut))
{
if (shortcut == "proj")
{
// Capture a very common mistake and give a better explanation (see #4)
throw new Exception("'stac_extensions' must contain 'projection instead of 'proj'.");
}
schemaUri = new Uri(baseUri, $"extensions/{shortcut}/json-schema/schema.json`");
isExtension = true;
}
else
{
schemaUri = baseUri;
}

if (!string.IsNullOrEmpty(baseUrl) && schemaMap.ContainsKey(baseUrl))
{
schemaUri = schemaMap[baseUrl];
}

if (schemaCompiled.ContainsKey(schemaUri.ToString()))
{
return schemaCompiled[schemaUri.ToString()];
}
else
{
schemaCompiled[schemaUri.ToString()] = JSchema.Parse(httpClient.GetStringAsync(schemaUri).GetAwaiter().GetResult(), new JSchemaUrlResolver());
return schemaCompiled[schemaUri.ToString()];
}
}
}
}

0 comments on commit 68e4857

Please sign in to comment.