-
Notifications
You must be signed in to change notification settings - Fork 37
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
Optional links support #153
Optional links support #153
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this approach makes sense. I originally thought the UrlPathBuilder
could return null
based on the resource, then the serializer could omit the links
hash if empty. I think your approach is more clear.
I do think we need some more tests, at least one for each of the enum values.
Saule/ApiResource.cs
Outdated
@@ -58,6 +68,8 @@ protected ApiResource() | |||
/// </summary> | |||
public string IdProperty { get; private set; } | |||
|
|||
public LinkTypes WithLinks { get; set; } = LinkTypes.All; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think ApiResource
should have any mutable properties.
var result = new JObject | ||
var result = new JObject(); | ||
|
||
if (_resource.WithLinks.HasFlag(LinkTypes.Self & LinkTypes.All)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think you can just do HasFlag(LinkTypes.Self)
, since All
includes that. Now this is only added when the value is LinkTypes.All
.
|
||
item["links"] = links; | ||
if (!string.IsNullOrEmpty(self) && relationship.WithLinks.HasFlag(LinkTypes.Related & LinkTypes.All)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same here, you don't need to do the &
.
AddUrl(links, "self", self); | ||
} | ||
|
||
if (!string.IsNullOrEmpty(related) && relationship.WithLinks.HasFlag(LinkTypes.Related & LinkTypes.All)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
And here.
Thanks @joukevandermaas, good to know I'm hopefully on the right path. Will ping you when I have had the time to make this PR complete and properly reviewable and will take your early comments into consideration. |
db31d2e
to
c38f6ee
Compare
Improved now but amount of tests still too few and should try it out on a real API implementation too. |
Additional tests added and seems to do what I expect in a real project. Ready for review now @joukevandermaas. But... I did notice a possible regression with the new serializer. Using 1.7.0.390 I'm missing the |
@bjornharrtell damn... could you give me an example or even better, a test, and I'll investigate the regression? |
@laurence79 will try to produce a unit test to demonstrate the issue. |
See testcase in #155. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think there's a slight confusion because both resources and relationships can define links.
This also needs documentation because of new public API.
|
||
item["links"] = links; | ||
if (!string.IsNullOrEmpty(self) && relationship.LinkType.HasFlag(LinkType.Self)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think for resources, we should both check their resources LinkType
as well as the relationship's LinkType
. It should probably resolve to the most restricting value.
Exampe:
PersonResource: WithLinks(All)
- BelongsTo<CompanyResource> Job, WithLinks(None)
CompanyResource: WithLinks(Self)
Now when serializing the Job
relationship, what links should show up? If I read this code correctly, currently it will serialize the self
link even if the BelongsTo
specified not to.
We probably also need a test case for this.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good question, I have a hard time getting a grip on what would be correct/desired behaviour here. Will have to think about it some more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wrote down some thoughts on this below. Please let me know what you think.
It's most likely that one of the two will just be All
. I expect the most common scenario is to just override something in one place (i.e. either the relationship or on the model, depending which best maps to the task.
This implies to me that we should take the most restrictive of the options. Examples:
- Person -> Company (Job relationship). Person and company both have link type
All
, the relationship has link typeRelated
. When serializing individual person or company records, we should serialize all links. When serializing the job relationship, we should serialize onlyrelated
links. - Person -> Company (Job relationship). Person has link type
All
, company hasRelated
. The relationship has link typeAll
. When serializing individual person records, we should serialize all links. For company records, we should not serialize anything (because there's no related links in that case). For the job relationship, we should serialize onlyrelated
links.
One case where I'm not sure:
- Person -> Company (Job relationship). Person and company both have link type
None
. The relationship has link typeRelated
. When serializing individual records, we should serialize no links. For the job relationship, what should we do? It sort of makes sense to serialize no links, but then again, the user explicitly set the link type toRelated
so they probably want those.
I guess the solution would be something like this:
- When not serializing relationships, there is only one option so this should be simple.
- Otherwise, if one of the options is
All
, take the other option. - In all other cases, use the logical 'or' of the options.
Examples of this:
All
andNone
-> serialize noneAll
andRelated
-> serialize relatedRelated
andNone
-> serialize noneRelated
andSelf
-> serialize related & self only (where they make sense)
Of course, we always have the option to just ignore one of the two:
- When serializing the
related
hash, use the relationship link type - When serializing models (i.e. the top level
data
orincluded
hash), use the resource link type
This is simple (I believe this is what the code in this PR does now) and seems the easiest to document.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm leaning on keeping it simple. The more complex path still hurts my brain. If one has other needs it could be implemented as a custom UrlBuilder. That said I might be shooting myself in the foot here. What is your opinion?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, I tend to agree. We can always expand on it later.
&& kv.Value.SourceObject != null) | ||
{ | ||
relationshipId = (string)data["id"]; | ||
} | ||
|
||
var links = new JObject(); | ||
AddUrl(links, "self", _urlBuilder.BuildRelationshipPath(node.Resource, node.Key.Id.ToString(), kv.Value.Relationship)); | ||
AddUrl(links, "related", _urlBuilder.BuildRelationshipPath(node.Resource, node.Key.Id.ToString(), kv.Value.Relationship, relationshipId)); | ||
var self = _urlBuilder.BuildRelationshipPath(node.Resource, node.Key.Id.ToString(), relationship); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We don't need to generate these if we might not use them, I think they should go inside the if
s below.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Needed because I check for empty link generation. I.e unrelated to the LinkType configuration I want to avoid generating the links if the UrlBuilder returns null which I think is in line what you once wrote in #121?
I like the changes! I think this can be merged once the documentation has been updated (I think |
Great, will add |
Added documentation and tentatively removed |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The markdown parser used by GH pages is different, and IIRC there needs to be a new line before code blocks.
docs/Generating-links.md
Outdated
``` | ||
|
||
When defining a `Relationship`: | ||
```csharp |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think there needs to be a new line before a code block or the parser messes up the layout.
docs/Generating-links.md
Outdated
specific `ApiResource` or `Relationship` by setting it as in the following examples: | ||
|
||
In your `ApiResource` constructor: | ||
```csharp |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think there needs to be a new line before a code block or the parser messes up the layout.
Thanks for seeing this through! It turned out really nice, I think. |
Aims to resolve #150.
Not yet finished though but wanted to ask about the approach. I couldn't see it possible to handle this in the
UrlPathBuilder
but had to involve theResourceSerializer
. Also, I'm not sure about what a "canonical" url is and so have problems understanding the purpose of Related vs RelatedCanonical.