Skip to content

Commit 0bfba08

Browse files
author
Bart Koelman
committed
Added ResourceFieldAttribute.Type and use it from QueryExpression.ToFullString() to provide improved debug info
1 parent ae645c9 commit 0bfba08

28 files changed

+232
-95
lines changed

src/JsonApiDotNetCore.Annotations/Resources/Annotations/RelationshipAttribute.cs

+20-21
Original file line numberDiff line numberDiff line change
@@ -14,21 +14,8 @@ public abstract class RelationshipAttribute : ResourceFieldAttribute
1414
{
1515
private protected static readonly CollectionConverter CollectionConverter = new();
1616

17-
/// <summary>
18-
/// The CLR type in which this relationship is declared.
19-
/// </summary>
20-
internal Type? LeftClrType { get; set; }
21-
22-
/// <summary>
23-
/// The CLR type this relationship points to. In the case of a <see cref="HasManyAttribute" /> relationship, this value will be the collection element
24-
/// type.
25-
/// </summary>
26-
/// <example>
27-
/// <code><![CDATA[
28-
/// public ISet<Tag> Tags { get; set; } // RightClrType: typeof(Tag)
29-
/// ]]></code>
30-
/// </example>
31-
internal Type? RightClrType { get; set; }
17+
// These are definitely assigned after building the resource graph, which is why their public equivalents are declared as non-nullable.
18+
private ResourceType? _rightType;
3219

3320
/// <summary>
3421
/// The <see cref="PropertyInfo" /> of the Entity Framework Core inverse navigation, which may or may not exist. Even if it exists, it may not be exposed
@@ -52,15 +39,28 @@ public abstract class RelationshipAttribute : ResourceFieldAttribute
5239
public PropertyInfo? InverseNavigationProperty { get; set; }
5340

5441
/// <summary>
55-
/// The containing resource type in which this relationship is declared.
42+
/// The containing resource type in which this relationship is declared. Identical to <see cref="ResourceFieldAttribute.Type" />.
5643
/// </summary>
57-
public ResourceType LeftType { get; internal set; } = null!;
44+
public ResourceType LeftType => Type;
5845

5946
/// <summary>
6047
/// The resource type this relationship points to. In the case of a <see cref="HasManyAttribute" /> relationship, this value will be the collection
6148
/// element type.
6249
/// </summary>
63-
public ResourceType RightType { get; internal set; } = null!;
50+
/// <example>
51+
/// <code><![CDATA[
52+
/// public ISet<Tag> Tags { get; set; } // RightType: Tag
53+
/// ]]></code>
54+
/// </example>
55+
public ResourceType RightType
56+
{
57+
get => _rightType!;
58+
internal set
59+
{
60+
ArgumentGuard.NotNull(value, nameof(value));
61+
_rightType = value;
62+
}
63+
}
6464

6565
/// <summary>
6666
/// Configures which links to write in the relationship-level links object for this relationship. Defaults to <see cref="LinkTypes.NotConfigured" />,
@@ -91,12 +91,11 @@ public override bool Equals(object? obj)
9191

9292
var other = (RelationshipAttribute)obj;
9393

94-
return LeftClrType == other.LeftClrType && RightClrType == other.RightClrType && Links == other.Links && CanInclude == other.CanInclude &&
95-
base.Equals(other);
94+
return _rightType?.ClrType == other._rightType?.ClrType && Links == other.Links && CanInclude == other.CanInclude && base.Equals(other);
9695
}
9796

9897
public override int GetHashCode()
9998
{
100-
return HashCode.Combine(LeftClrType, RightClrType, Links, CanInclude, base.GetHashCode());
99+
return HashCode.Combine(_rightType?.ClrType, Links, CanInclude, base.GetHashCode());
101100
}
102101
}

src/JsonApiDotNetCore.Annotations/Resources/Annotations/ResourceFieldAttribute.cs

+15
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
using System.Reflection;
22
using JetBrains.Annotations;
3+
using JsonApiDotNetCore.Configuration;
34

45
// ReSharper disable NonReadonlyMemberInGetHashCode
56

@@ -15,6 +16,7 @@ public abstract class ResourceFieldAttribute : Attribute
1516
// These are definitely assigned after building the resource graph, which is why their public equivalents are declared as non-nullable.
1617
private string? _publicName;
1718
private PropertyInfo? _property;
19+
private ResourceType? _type;
1820

1921
/// <summary>
2022
/// The publicly exposed name of this JSON:API field. When not explicitly assigned, the configured naming convention is applied on the property name.
@@ -46,6 +48,19 @@ internal set
4648
}
4749
}
4850

51+
/// <summary>
52+
/// The containing resource type in which this field is declared.
53+
/// </summary>
54+
public ResourceType Type
55+
{
56+
get => _type!;
57+
internal set
58+
{
59+
ArgumentGuard.NotNull(value, nameof(value));
60+
_type = value;
61+
}
62+
}
63+
4964
/// <summary>
5065
/// Gets the value of this field on the specified resource instance. Throws if the property is write-only or if the field does not belong to the
5166
/// specified resource instance.

src/JsonApiDotNetCore/Configuration/ResourceGraphBuilder.cs

+16-14
Original file line numberDiff line numberDiff line change
@@ -43,23 +43,35 @@ public IResourceGraph Build()
4343

4444
var resourceGraph = new ResourceGraph(resourceTypes);
4545

46+
SetFieldTypes(resourceGraph);
4647
SetRelationshipTypes(resourceGraph);
4748
SetDirectlyDerivedTypes(resourceGraph);
4849

4950
return resourceGraph;
5051
}
5152

53+
private static void SetFieldTypes(ResourceGraph resourceGraph)
54+
{
55+
foreach (ResourceFieldAttribute field in resourceGraph.GetResourceTypes().SelectMany(resourceType => resourceType.Fields))
56+
{
57+
field.Type = resourceGraph.GetResourceType(field.Property.ReflectedType!);
58+
}
59+
}
60+
5261
private static void SetRelationshipTypes(ResourceGraph resourceGraph)
5362
{
5463
foreach (RelationshipAttribute relationship in resourceGraph.GetResourceTypes().SelectMany(resourceType => resourceType.Relationships))
5564
{
56-
relationship.LeftType = resourceGraph.GetResourceType(relationship.LeftClrType!);
57-
ResourceType? rightType = resourceGraph.FindResourceType(relationship.RightClrType!);
65+
Type rightClrType = relationship is HasOneAttribute
66+
? relationship.Property.PropertyType
67+
: relationship.Property.PropertyType.GetGenericArguments()[0];
68+
69+
ResourceType? rightType = resourceGraph.FindResourceType(rightClrType);
5870

5971
if (rightType == null)
6072
{
61-
throw new InvalidConfigurationException($"Resource type '{relationship.LeftClrType}' depends on " +
62-
$"'{relationship.RightClrType}', which was not added to the resource graph.");
73+
throw new InvalidConfigurationException($"Resource type '{relationship.LeftType.ClrType}' depends on " +
74+
$"'{rightClrType}', which was not added to the resource graph.");
6375
}
6476

6577
relationship.RightType = rightType;
@@ -254,8 +266,6 @@ private IReadOnlyCollection<RelationshipAttribute> GetRelationships(Type resourc
254266
{
255267
relationship.Property = property;
256268
SetPublicName(relationship, property);
257-
relationship.LeftClrType = resourceClrType;
258-
relationship.RightClrType = GetRelationshipType(relationship, property);
259269

260270
IncludeField(relationshipsByName, relationship);
261271
}
@@ -270,14 +280,6 @@ private void SetPublicName(ResourceFieldAttribute field, PropertyInfo property)
270280
field.PublicName ??= FormatPropertyName(property);
271281
}
272282

273-
private Type GetRelationshipType(RelationshipAttribute relationship, PropertyInfo property)
274-
{
275-
ArgumentGuard.NotNull(relationship, nameof(relationship));
276-
ArgumentGuard.NotNull(property, nameof(property));
277-
278-
return relationship is HasOneAttribute ? property.PropertyType : property.PropertyType.GetGenericArguments()[0];
279-
}
280-
281283
private IReadOnlyCollection<EagerLoadAttribute> GetEagerLoads(Type resourceClrType, int recursionDepth = 0)
282284
{
283285
AssertNoInfiniteRecursion(recursionDepth);

src/JsonApiDotNetCore/Queries/Expressions/AnyExpression.cs

+12-2
Original file line numberDiff line numberDiff line change
@@ -34,14 +34,24 @@ public override TResult Accept<TArgument, TResult>(QueryExpressionVisitor<TArgum
3434
}
3535

3636
public override string ToString()
37+
{
38+
return InnerToString(false);
39+
}
40+
41+
public override string ToFullString()
42+
{
43+
return InnerToString(true);
44+
}
45+
46+
private string InnerToString(bool toFullString)
3747
{
3848
var builder = new StringBuilder();
3949

4050
builder.Append(Keywords.Any);
4151
builder.Append('(');
42-
builder.Append(TargetAttribute);
52+
builder.Append(toFullString ? TargetAttribute.ToFullString() : TargetAttribute);
4353
builder.Append(',');
44-
builder.Append(string.Join(",", Constants.Select(constant => constant.ToString()).OrderBy(value => value)));
54+
builder.Append(string.Join(",", Constants.Select(constant => toFullString ? constant.ToFullString() : constant.ToString()).OrderBy(value => value)));
4555
builder.Append(')');
4656

4757
return builder.ToString();

src/JsonApiDotNetCore/Queries/Expressions/ComparisonExpression.cs

+5
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,11 @@ public override string ToString()
3333
return $"{Operator.ToString().Camelize()}({Left},{Right})";
3434
}
3535

36+
public override string ToFullString()
37+
{
38+
return $"{Operator.ToString().Camelize()}({Left.ToFullString()},{Right.ToFullString()})";
39+
}
40+
3641
public override bool Equals(object? obj)
3742
{
3843
if (ReferenceEquals(this, obj))

src/JsonApiDotNetCore/Queries/Expressions/CountExpression.cs

+5
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,11 @@ public override string ToString()
2828
return $"{Keywords.Count}({TargetCollection})";
2929
}
3030

31+
public override string ToFullString()
32+
{
33+
return $"{Keywords.Count}({TargetCollection.ToFullString()})";
34+
}
35+
3136
public override bool Equals(object? obj)
3237
{
3338
if (ReferenceEquals(this, obj))

src/JsonApiDotNetCore/Queries/Expressions/HasExpression.cs

+12-2
Original file line numberDiff line numberDiff line change
@@ -27,16 +27,26 @@ public override TResult Accept<TArgument, TResult>(QueryExpressionVisitor<TArgum
2727
}
2828

2929
public override string ToString()
30+
{
31+
return InnerToString(false);
32+
}
33+
34+
public override string ToFullString()
35+
{
36+
return InnerToString(true);
37+
}
38+
39+
private string InnerToString(bool toFullString)
3040
{
3141
var builder = new StringBuilder();
3242
builder.Append(Keywords.Has);
3343
builder.Append('(');
34-
builder.Append(TargetCollection);
44+
builder.Append(toFullString ? TargetCollection.ToFullString() : TargetCollection);
3545

3646
if (Filter != null)
3747
{
3848
builder.Append(',');
39-
builder.Append(Filter);
49+
builder.Append(toFullString ? Filter.ToFullString() : Filter);
4050
}
4151

4252
builder.Append(')');

src/JsonApiDotNetCore/Queries/Expressions/IncludeElementExpression.cs

+12-2
Original file line numberDiff line numberDiff line change
@@ -34,14 +34,24 @@ public override TResult Accept<TArgument, TResult>(QueryExpressionVisitor<TArgum
3434
}
3535

3636
public override string ToString()
37+
{
38+
return InnerToString(false);
39+
}
40+
41+
public override string ToFullString()
42+
{
43+
return InnerToString(true);
44+
}
45+
46+
private string InnerToString(bool toFullString)
3747
{
3848
var builder = new StringBuilder();
39-
builder.Append(Relationship);
49+
builder.Append(toFullString ? $"{Relationship.LeftType.PublicName}:{Relationship.PublicName}" : Relationship.PublicName);
4050

4151
if (Children.Any())
4252
{
4353
builder.Append('{');
44-
builder.Append(string.Join(",", Children.Select(child => child.ToString()).OrderBy(name => name)));
54+
builder.Append(string.Join(",", Children.Select(child => toFullString ? child.ToFullString() : child.ToString()).OrderBy(name => name)));
4555
builder.Append('}');
4656
}
4757

src/JsonApiDotNetCore/Queries/Expressions/IncludeExpression.cs

+11-1
Original file line numberDiff line numberDiff line change
@@ -33,9 +33,19 @@ public override TResult Accept<TArgument, TResult>(QueryExpressionVisitor<TArgum
3333
}
3434

3535
public override string ToString()
36+
{
37+
return InnerToString(false);
38+
}
39+
40+
public override string ToFullString()
41+
{
42+
return InnerToString(true);
43+
}
44+
45+
private string InnerToString(bool toFullString)
3646
{
3747
IReadOnlyCollection<ResourceFieldChainExpression> chains = IncludeChainConverter.GetRelationshipChains(this);
38-
return string.Join(",", chains.Select(child => child.ToString()).OrderBy(name => name));
48+
return string.Join(",", chains.Select(field => toFullString ? field.ToFullString() : field.ToString()).OrderBy(name => name));
3949
}
4050

4151
public override bool Equals(object? obj)

src/JsonApiDotNetCore/Queries/Expressions/IsTypeExpression.cs

+12-2
Original file line numberDiff line numberDiff line change
@@ -31,14 +31,24 @@ public override TResult Accept<TArgument, TResult>(QueryExpressionVisitor<TArgum
3131
}
3232

3333
public override string ToString()
34+
{
35+
return InnerToString(false);
36+
}
37+
38+
public override string ToFullString()
39+
{
40+
return InnerToString(true);
41+
}
42+
43+
private string InnerToString(bool toFullString)
3444
{
3545
var builder = new StringBuilder();
3646
builder.Append(Keywords.IsType);
3747
builder.Append('(');
3848

3949
if (TargetToOneRelationship != null)
4050
{
41-
builder.Append(TargetToOneRelationship);
51+
builder.Append(toFullString ? TargetToOneRelationship.ToFullString() : TargetToOneRelationship);
4252
}
4353

4454
builder.Append(',');
@@ -47,7 +57,7 @@ public override string ToString()
4757
if (Child != null)
4858
{
4959
builder.Append(',');
50-
builder.Append(Child);
60+
builder.Append(toFullString ? Child.ToFullString() : Child);
5161
}
5262

5363
builder.Append(')');

src/JsonApiDotNetCore/Queries/Expressions/LiteralConstantExpression.cs

+5
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,11 @@ public override string ToString()
2828
return $"'{value}'";
2929
}
3030

31+
public override string ToFullString()
32+
{
33+
return ToString();
34+
}
35+
3136
public override bool Equals(object? obj)
3237
{
3338
if (ReferenceEquals(this, obj))

src/JsonApiDotNetCore/Queries/Expressions/LogicalExpression.cs

+11-1
Original file line numberDiff line numberDiff line change
@@ -47,12 +47,22 @@ public override TResult Accept<TArgument, TResult>(QueryExpressionVisitor<TArgum
4747
}
4848

4949
public override string ToString()
50+
{
51+
return InnerToString(false);
52+
}
53+
54+
public override string ToFullString()
55+
{
56+
return InnerToString(true);
57+
}
58+
59+
private string InnerToString(bool toFullString)
5060
{
5161
var builder = new StringBuilder();
5262

5363
builder.Append(Operator.ToString().Camelize());
5464
builder.Append('(');
55-
builder.Append(string.Join(",", Terms.Select(term => term.ToString())));
65+
builder.Append(string.Join(",", Terms.Select(term => toFullString ? term.ToFullString() : term.ToString())));
5666
builder.Append(')');
5767

5868
return builder.ToString();

src/JsonApiDotNetCore/Queries/Expressions/MatchTextExpression.cs

+15-1
Original file line numberDiff line numberDiff line change
@@ -30,12 +30,26 @@ public override TResult Accept<TArgument, TResult>(QueryExpressionVisitor<TArgum
3030
}
3131

3232
public override string ToString()
33+
{
34+
return InnerToString(false);
35+
}
36+
37+
public override string ToFullString()
38+
{
39+
return InnerToString(true);
40+
}
41+
42+
private string InnerToString(bool toFullString)
3343
{
3444
var builder = new StringBuilder();
3545

3646
builder.Append(MatchKind.ToString().Camelize());
3747
builder.Append('(');
38-
builder.Append(string.Join(",", TargetAttribute, TextValue));
48+
49+
builder.Append(toFullString
50+
? string.Join(",", TargetAttribute.ToFullString(), TextValue.ToFullString())
51+
: string.Join(",", TargetAttribute, TextValue));
52+
3953
builder.Append(')');
4054

4155
return builder.ToString();

0 commit comments

Comments
 (0)