-
Notifications
You must be signed in to change notification settings - Fork 2.7k
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
Add guidance to use composable functions instead of filter-as-segment feature #520
base: vNext
Are you sure you want to change the base?
Changes from all commits
92c7c95
b7d1110
b84171a
651933d
9cec174
dc86042
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
# Filter as segment | ||
|
||
There is an [OData feature](https://docs.oasis-open.org/odata/odata/v4.01/odata-v4.01-part2-url-conventions.html#sec_AddressingaSubsetofaCollection) which allows having a `$filter` in a URL segment. | ||
This feature is useful whenever there are operations on a collection and the client wants to perform those operations on a *subset* of the collection. | ||
For example, the `riskyUsers` API on Microsoft Graph has an action defined to let clients "dismiss" risky users (i.e. consider those users "not risky"): | ||
|
||
```xml | ||
<Action Name="dismiss" IsBound="true"> | ||
<Parameter Name="bindingParameter" Type="Collection(microsoft.graph.riskyUser)" /> | ||
<Parameter Name="userIds" Type="Collection(Edm.String)" /> | ||
</Action> | ||
``` | ||
|
||
Using this action, clients can call | ||
|
||
```http | ||
POST /identityProtection/riskyUsers/dismiss | ||
{ | ||
"userIds": [ | ||
"{userId1}", | ||
"{userId2}", | ||
... | ||
] | ||
} | ||
``` | ||
|
||
in order to dismiss the risky users with the provided IDs. Using the filter-as-segment OData feature, the action could instead be defined as: | ||
|
||
```xml | ||
<Action Name="dismiss" IsBound="true"> | ||
<Parameter Name="bindingParameter" Type="Collection(self.riskyUser)" /> | ||
</Action> | ||
``` | ||
|
||
and clients could call | ||
|
||
```http | ||
POST /identityProtection/riskyUsers/$filter=@f/dismiss?@f=id IN ('{userId1}','{userId2}',...) | ||
``` | ||
|
||
Doing this is beneficial due to the robust nature of OData filter expressions: clients will be able to dismiss risky users based on any supported filter without the service team needing to implement a new `dismiss` overload that filters based on the new criteria. | ||
However, there are some concerns about the discoverability of using the filter-as-segment feature, as well as the support of [parameter aliasing](https://docs.oasis-open.org/odata/odata/v4.01/odata-v4.01-part2-url-conventions.html#sec_ParameterAliases) that's required. | ||
As a result, functions should be introduced that act in the same way as the filter-as-segment: | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think naming a specific function as "filter" is very confusing for users who expect a standard $filter behavior. I believe it does not follow our naming guidelines without having a product-specific prefix. Unless we will treat this function similarly to delta? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. namespace qualification of functions is optional in msgraph. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. customFilter or filterFunction |
||
|
||
```xml | ||
<Function Name="filter" IsBound="true" IsComposable="true"> | ||
<Parameter Name="bindingParameter" Type="Collection(microsoft.graph.riskyUser)" Nullable="false" /> | ||
<Parameter Name="expression" Type="Edm.String" Nullable="false" /> | ||
<ReturnType Type="Collection(microsoft.graph.riskyUser)" /> | ||
</Function> | ||
``` | ||
|
||
Clients would now be able to call | ||
|
||
```http | ||
POST /identityProtection/riskyUsers/filter(expression='id IN (''{userId1}'',''{userId2}'',...)')/dismiss | ||
``` | ||
|
||
NOTE: the `'` literal in the filter expression must be escaped with `''` | ||
|
||
An example implementation of a filter function using OData WebApi can be found [here](https://github.com/OData/AspNetCoreOData/commit/7732f7e6b812d9a79a73529562f2e74b68e2794f). |
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.
Why is this positioned as an article? i think it make sense to create a pattern and clearly specify when this solution is a good one.
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.
This should probably just be included as another scenario of #512
We might rename 512 slightly to reflect this, but doesn't seem like this should be a separate pattern, when the pattern is "replacing a complex filter with a function".