Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

APM Server specification validation #984

Merged
merged 28 commits into from
Jan 5, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
9ff5721
Initial specification validator implementation
russcam Oct 16, 2020
a6cb097
Get the actual schema
russcam Oct 16, 2020
6216656
don't null check for now
russcam Oct 16, 2020
eb96b70
Check for multiple json schema types
russcam Oct 19, 2020
76e7dac
Use latest working schema version
russcam Oct 19, 2020
26f738a
use JsonSchemaProperty name
russcam Oct 19, 2020
46fc3a0
Add service to span context
russcam Oct 19, 2020
544642a
Validate anyOf and oneOf schemas
russcam Oct 19, 2020
5acc35b
Add enum to determine how validation is performed
russcam Oct 19, 2020
a450c5e
Add link to apm-server bug
russcam Oct 19, 2020
362c175
Generate multple instances for anyOf and oneOf
russcam Oct 19, 2020
b058f96
Rename properties
russcam Oct 19, 2020
2a1d358
Remove ServiceContext from SpanContext
russcam Oct 22, 2020
3ecfd72
Validate input to mock APM server against JSON schema
russcam Oct 22, 2020
c14a313
Tidy up
russcam Oct 22, 2020
24f9dcd
Force TLS 1.2 on Full Framework
russcam Oct 22, 2020
a9295c5
Use MaxLengthAttribute in validation
russcam Oct 28, 2020
6ffa9e0
readonly
russcam Oct 28, 2020
c09fae2
Use v2 server specs
russcam Dec 14, 2020
358f8ea
Allow testing against fork branches
russcam Dec 14, 2020
b2b83b1
Noop types comply with schema
russcam Dec 14, 2020
eeef828
Handle no "type" property for a property
russcam Dec 14, 2020
1a2993e
code cleanup
russcam Dec 14, 2020
01aec12
Run test against spec master branch
russcam Dec 14, 2020
561d797
Merge branch 'master' into feature/schema-validation
russcam Dec 15, 2020
c2e54d3
Merge branch 'master' into feature/schema-validation
russcam Dec 16, 2020
70266d8
Merge branch 'master' into feature/schema-validation
russcam Dec 21, 2020
450fad1
Update validator
russcam Dec 21, 2020
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions ElasticApmAgent.sln
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Elastic.Apm.StackExchange.R
EndProject
Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "scripts", "build\scripts\scripts.fsproj", "{CB623206-F69E-4004-8527-D4B971AA981A}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Elastic.Apm.Specification", "src\Elastic.Apm.Specification\Elastic.Apm.Specification.csproj", "{5D076C7F-1F8B-4B11-9910-48717D133963}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Elastic.Apm.AspNetCore.Static.Tests", "test\Elastic.Apm.AspNetCore.Static.Tests\Elastic.Apm.AspNetCore.Static.Tests.csproj", "{2250D888-E4CC-4B2B-AF31-5C78D76EC73D}"
EndProject
Global
Expand Down Expand Up @@ -264,6 +266,10 @@ Global
{6D413E0A-1CB7-4A2D-8E99-DB4F58D3463E}.Release|Any CPU.Build.0 = Release|Any CPU
{CB623206-F69E-4004-8527-D4B971AA981A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{CB623206-F69E-4004-8527-D4B971AA981A}.Release|Any CPU.ActiveCfg = Release|Any CPU
{5D076C7F-1F8B-4B11-9910-48717D133963}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{5D076C7F-1F8B-4B11-9910-48717D133963}.Debug|Any CPU.Build.0 = Debug|Any CPU
{5D076C7F-1F8B-4B11-9910-48717D133963}.Release|Any CPU.ActiveCfg = Release|Any CPU
{5D076C7F-1F8B-4B11-9910-48717D133963}.Release|Any CPU.Build.0 = Release|Any CPU
{2250D888-E4CC-4B2B-AF31-5C78D76EC73D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{2250D888-E4CC-4B2B-AF31-5C78D76EC73D}.Debug|Any CPU.Build.0 = Debug|Any CPU
{2250D888-E4CC-4B2B-AF31-5C78D76EC73D}.Release|Any CPU.ActiveCfg = Release|Any CPU
Expand Down Expand Up @@ -309,6 +315,7 @@ Global
{9FBC8D8C-E600-464D-AE4E-AC1FB59485F6} = {3C791D9C-6F19-4F46-B367-2EC0F818762D}
{6D413E0A-1CB7-4A2D-8E99-DB4F58D3463E} = {267A241E-571F-458F-B04C-B6C4DE79E735}
{CB623206-F69E-4004-8527-D4B971AA981A} = {B406113B-0917-4531-AFEE-66DDB952590F}
{5D076C7F-1F8B-4B11-9910-48717D133963} = {3734A52F-2222-454B-BF58-1BA5C1F29D77}
{2250D888-E4CC-4B2B-AF31-5C78D76EC73D} = {267A241E-571F-458F-B04C-B6C4DE79E735}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
Expand Down
16 changes: 16 additions & 0 deletions src/Elastic.Apm.Specification/Elastic.Apm.Specification.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFrameworks>netstandard2.0;net461</TargetFrameworks>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="NJsonSchema" Version="10.2.2" />
<PackageReference Include="SharpZipLib" Version="1.3.0" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\Elastic.Apm\Elastic.Apm.csproj" />
</ItemGroup>

</Project>
42 changes: 42 additions & 0 deletions src/Elastic.Apm.Specification/ImplementationProperty.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
// Licensed to Elasticsearch B.V under
// one or more agreements.
// Elasticsearch B.V licenses this file to you under the Apache 2.0 License.
// See the LICENSE file in the project root for more information

using System;

namespace Elastic.Apm.Specification
{
/// <summary>
/// A property defined on a type that implements the APM server specification
/// </summary>
public class ImplementationProperty
{
public ImplementationProperty(string name, Type propertyType, Type declaringType)
{
Name = name;
PropertyType = propertyType;
DeclaringType = declaringType;
}

/// <summary>
/// The name of the property
/// </summary>
public string Name { get; }

/// <summary>
/// The type of the property
/// </summary>
public Type PropertyType { get; }

/// <summary>
/// The type that declares the property
/// </summary>
public Type DeclaringType { get; }

/// <summary>
/// The max length that the property value can have
/// </summary>
public int? MaxLength { get; set; }
}
}
33 changes: 33 additions & 0 deletions src/Elastic.Apm.Specification/JsonSchemaExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
// Licensed to Elasticsearch B.V under
// one or more agreements.
// Elasticsearch B.V licenses this file to you under the Apache 2.0 License.
// See the LICENSE file in the project root for more information

using System.IO;
using NJsonSchema;

namespace Elastic.Apm.Specification
{
public static class JsonSchemaExtensions
{
/// <summary>
/// Gets the schema $id if it exists, the name of the property if the schema represents a property,
/// or the file name of the schema document path.
/// </summary>
/// <param name="schema"></param>
/// <returns></returns>
public static string GetNameOrSpecificationId(this JsonSchema schema)
{
if (schema.ExtensionData != null && schema.ExtensionData.TryGetValue("$id", out var id))
return id.ToString();

if (schema is JsonSchemaProperty schemaProperty && !string.IsNullOrEmpty(schemaProperty.Name))
return schemaProperty.Name;

if (!string.IsNullOrEmpty(schema.DocumentPath))
return Path.GetFileNameWithoutExtension(schema.DocumentPath);

return null;
}
}
}
51 changes: 51 additions & 0 deletions src/Elastic.Apm.Specification/TypeValidationError.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
// Licensed to Elasticsearch B.V under
// one or more agreements.
// Elasticsearch B.V licenses this file to you under the Apache 2.0 License.
// See the LICENSE file in the project root for more information

using System;
using System.IO;

namespace Elastic.Apm.Specification
{
public class TypeValidationIgnore
{
public string Schema { get; }
public string PropertyName { get; }
public string Message { get; }

public TypeValidationIgnore(string schema, string propertyName, string message)
{
Schema = schema;
PropertyName = propertyName;
Message = message;
}

public override string ToString() => $"ignored property name: {PropertyName}, schema: {Schema}. {Message}.";
}

public class TypeValidationError
{
public string Schema { get; }
public string PropertyName { get; }
public string Message { get; }

public Type SpecType { get; }

public TypeValidationError(Type specType, string schema, string propertyName, string message)
{
SpecType = specType;
Schema = schema;
PropertyName = propertyName;
Message = message;
}

public static TypeValidationError NotFound(Type specType, string id, string name) =>
new TypeValidationError(specType, id, name, "not found");

public static TypeValidationError ExpectedType(Type specType, Type propertyType, string expectedType, string id, string name) =>
new TypeValidationError(specType, id, name, $"expected type '{expectedType}' but found '{propertyType.FullName}'");

public override string ToString() => $"{SpecType}, property name: {PropertyName}, schema: {Schema}. {Message}.";
}
}
52 changes: 52 additions & 0 deletions src/Elastic.Apm.Specification/TypeValidationResult.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
// Licensed to Elasticsearch B.V under
// one or more agreements.
// Elasticsearch B.V licenses this file to you under the Apache 2.0 License.
// See the LICENSE file in the project root for more information

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Elastic.Apm.Specification
{
public class TypeValidationResult
{
private readonly List<TypeValidationError> _errors = new List<TypeValidationError>();
private readonly List<TypeValidationIgnore> _ignores = new List<TypeValidationIgnore>();
public Type Type { get; }
public string SpecificationId { get; }
public Validation Validation { get; }
public IEnumerable<TypeValidationError> Errors => _errors;

public IEnumerable<TypeValidationIgnore> Ignores => _ignores;

public bool Success => !Errors.Any();

internal void AddError(TypeValidationError error) => _errors.Add(error);

internal void AddIgnore(TypeValidationIgnore error) => _ignores.Add(error);

public TypeValidationResult(Type type, string specificationId, Validation validation)
{
Type = type;
SpecificationId = specificationId;
Validation = validation;
}

public override string ToString()
{
var builder = new StringBuilder(Type.FullName)
.Append(": ")
.AppendLine(Success ? "success" : "failure");

foreach (var error in Errors)
builder.AppendLine(error.ToString());

foreach (var ignore in Ignores)
builder.AppendLine(ignore.ToString());

return builder.ToString();
}
}
}
27 changes: 27 additions & 0 deletions src/Elastic.Apm.Specification/Validation.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
// Licensed to Elasticsearch B.V under
// one or more agreements.
// Elasticsearch B.V licenses this file to you under the Apache 2.0 License.
// See the LICENSE file in the project root for more information

namespace Elastic.Apm.Specification
{
/// <summary>
/// Determines how validation is performed
/// </summary>
public enum Validation
{
/// <summary>
/// Validates the type against the specification. A type must be a valid implementation of the specification, but
/// it may be only a subset of the properties i.e. schema properties of type "null" may not be implemented.
/// </summary>
TypeToSpec,
/// <summary>
/// Validates the specification against the type. A type must match the specification exactly in order to be valid
/// </summary>
/// <remarks>
/// It's expected that the type is the implementation of the entire specification and not a subset
/// of properties.
/// </remarks>
SpecToType
}
}
Loading