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

Provide ability to handle JsonApi parameters in WebApi action itself manually #181

Merged
merged 3 commits into from
Jan 29, 2018

Conversation

sergey-litvinov-work
Copy link
Contributor

Hi Jouke,

Here is pull request for issue #120 . In our case we don't use EntityFramework, but we want to consume already parsed parameters (include\filters\page\sort), so then we can handle them manually in our WebApi controllers\actions. Current issue is that if i specify AllowsQueryAttribute, then Saule will parse request and will try to apply it automatically that isn't what we need. I tried to follow your comments in that ticket by making QueryContext as publicly accessible, but with internal setters, so for external callers it will be immutable

This pull request tries to handle such use cases:

  1. Provide immutable QueryContext to WebApi action, so action can handle it manually. There is a code in AllowsManuallyHandledQueryAttribute that passes current QueryContext to action if action has it as parameter. For example in case of Include, we can check what properties were specified in QueryContext and only then we would load them from our persistent storage. Otherwise we won't load them. Currently Saule does't allow to us to do it and we need to load the whole entity with DisableDefaultIncluded attribute, and then Saule will include specified properties. But not all consumer needs all properties.
  2. Disable internal Saule handling for sort\page\filter
  3. Allow user to use fields in filter that aren't present in actual model. For example we have entity and it can have different IsDeleted state, and we want to specify it like &filter[include-ancient-records]=true and right now Saule will fail here as it requires that IncludeAncientRecords should be present in entity itself
  4. Allow automatic WebApi binding for filters. As filter properties can be specified in kebab case - WebApi won't map them, so i added JsonApiQueryValueProvider that gets parameters from query, and then converts them to PascalCase, so WebApi can bind them. Use case here is that we can have own PersonFilter like in added tests, where i can specify custom properties. I'm a bit afraid that it will affect something else, but it should be used after default QueryStringValueProvider so it will have lower priority in model binding

I added 19 unit tests that should validate these cases. And also i verified that existing unit tests are still valid and passes.
I also have some doubts about naming and not sure that ManuallyHandledQuery is good description for that, so i'm totally open for any other suggestions.

I haven't updated any docs, as i'm sure you will have some comments about code and it might need more changes. but if you want, i can add some documenation to docs/content folder about this feature

Thanks!

Sergey Litvinov added 2 commits December 25, 2017 18:36
…onApi parameters so we can make custom handling for them in the action.
@joukevandermaas
Copy link
Owner

Sorry for the delayed response. Due to the holidays I didn't have time to review this. I will take a look as soon as possible.

Thanks for contributing!

Copy link
Owner

@joukevandermaas joukevandermaas left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks really great. 👍

Since we are now making them public API, perhaps we should rename the contexts to something nicer, e.g. FilterContext, SortContext, IncludeContext. Same goes for IncludeProperty, SortDirection etc.

Other than that I had a few questions/suggestions but nothing major. Awesome work!

/// Indicates that filter, paging, include and sorting should be parsed. But they won't be automatically applied to WebApi action
/// and action should handle them manually
/// </summary>
public class AllowsManuallyHandledQueryAttribute : ActionFilterAttribute
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder if this attribute is needed at all? Perhaps it's enough to detect a QueryContext parameter? I'm not sure if that's possible and if that would still cover the same use cases.

In any case maybe HandlesQuery or ManuallyHanldesQuery might be a nicer name?

Copy link
Contributor Author

@sergey-litvinov-work sergey-litvinov-work Jan 28, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i renamed it to HandlesQuery. We can detect this parameter, but we still would need to initialize Sort\Filter\Include like we do in AllowsQueryAttribute. One way would be to move that detection to AllowsQueryAttribute, and then it will process them automatically or won't do anything depending on QueryContext parameter in action. But AllowsQueryAttribute might be a bit confusing as for me.

There is another point, that HandlesQuery attribute gives a bit more flexiblity, so developer can use QueryContext in action, and Saule still can do automatic IQueryable processing if needed. So it's up to developer, but yeah, additional attribute confusing it a bit. One point for future that might makes sense is to combine AllowsQueryAttribute and HandlesQueryAttribute and also add such enum to attribute constructor:

public enum AutomaticProcessing
{
	None = 0,
	Sort = 1,
	Pagination = 2,
	Include = 4,
	Filter = 8,
	All = Sort | Pagination | Include | Filter
}

So we can specify it in such way:

public IHttpActionResult Get()
{
	
}

and it will initialize all query context properties from request, but will apply only filtering on action result. And all other actions won't be applied. So it would have more precise way to configure what needs to be processed and what not. And then in my case i can specify AllowsQuery(AutomaticProcissing.None) that will be equivalent to current HandlesQueryAttribute and then handle QueryContext in action itself.

If you like this idea, i can update the code.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I do like it, but I also feel that the current approach is probably simpler. If we ever do a Saule 2.0 I'll consider cleaning up the attribute situation.

[HttpGet]
[AllowsManuallyHandledQuery]
[Paginated]
[DisableDefaultIncluded]
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does DisableDefaultIncluded make sense in combination with ManuallyHandlesQuery? At first glance it does not and maybe it should error (?). If that's hard then IMO ManuallyHandlesQuery should take precedence and all other query-related attributes should be ignored.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That makes sense. I've added validation to HandlesQueryAttribute, so it will return an error when any\both DisableDefaultIncluded \ AllowsQuery are specified.

renamed Including\Filtering\Sorting properties in the same way
renamed AllowsManuallyHandledQueryAttribute to HandlesQueryAttribute
added validations to HandlesQueryAttribute
@sergey-litvinov-work
Copy link
Contributor Author

sergey-litvinov-work commented Jan 28, 2018

Sorry, for long answer. pushed change with such renames\changes:

  1. QueryContext.Filtering\Sorting\Including to Filter\Sort\Include
  2. Including\Filtering\Sorting properties to Include\Filter\Sort property
  3. Also renamed Filtering\Sorting intercepter to Filter\Sort interceptor
  4. AllowsManuallyHandledQueryAttribute to HandlesQueryAttribute
  5. renamed two test classes for interceptors from FilteringInterceptorTests\Sorting to FilterInterceptorTests\Sort
  6. added validation in HandlesQueryAttribute for other attributes

Also added answers to your comments

@joukevandermaas
Copy link
Owner

Great! I will publish a new pre-release with these changes in it. Thanks for the hard work! 🎉

@joukevandermaas joukevandermaas merged commit 0cfeacc into joukevandermaas:master Jan 29, 2018
@sergey-litvinov-work
Copy link
Contributor Author

Thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants