-
Notifications
You must be signed in to change notification settings - Fork 8
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
capabilities documentation #296
base: main
Are you sure you want to change the base?
Changes from all commits
f46db94
d90509a
e9b13d5
5092166
613f935
c5aeba4
11ed37c
295f308
84d3b80
2f2c3df
71455b3
97a98a9
547025b
7d2a8f0
97e3833
d11c005
d7561a7
988b759
669b159
cdff501
5160f5c
7576143
83d568d
5b78109
0888c64
4532baf
c6d60d8
d4a106e
552d52f
094f74c
be9a9bc
49789c7
b6f1431
bf0b894
dcad7a7
3deaedb
e552401
68ad6db
471331d
8a29dcf
068e905
230b808
d0bc868
1fd839c
f7db280
21363c0
2d99e34
fe93376
cf7d345
24cc57f
2d00eb6
d8ec19e
2c163d5
ee52f47
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,337 @@ | ||
--- | ||
id: rsdl-capabilities | ||
title: Capabilities | ||
sidebar_label: RSDL API Capabilities | ||
--- | ||
|
||
# Path Centric Service Capabilities | ||
|
||
The previous sections described how to define the format of the request and response bodies based on types and their relationships. These structures also imply which URL paths are valid in the service: starting with the service properties and following properties of the structured types. For example in the service below, `orders` is a service property which allows access via the `/orders` URL. Since an order has multiple order items via the `items` property, the URL `/orders/<order id>/items` and `/orders/<order id>/items/<item id>` are also a valid URLs. | ||
|
||
Without further constraints this would allow a huge number of URLs that a service would need to support. And it is important not just to specify which paths are allowed, but also to specify different functionality and behaviors supported for different paths. | ||
|
||
The following sections introduce the notion of paths and capabilities that the service provides per path. | ||
|
||
## Example service | ||
|
||
For the examples in the section of this document, we assume the following type definitions. This is a simplified service that provides a top level orders collection together with their (contained) order items. Each item references a SKU, that can be accessed via the top level entity set. | ||
|
||
```rsdl | ||
type Order { | ||
key id: String | ||
created: DateTime | ||
status: OrderStatus | ||
items: [OrderItem] | ||
} | ||
|
||
enum OrderStatus { | ||
Open | ||
Archived | ||
Canceled | ||
} | ||
|
||
type OrderItem { | ||
key id: String | ||
sku: SKU | ||
amount: Integer | ||
} | ||
|
||
type SKU { | ||
key id: String | ||
name: String | ||
description: String | ||
unitPrice: Decimal | ||
} | ||
|
||
service { | ||
orders: [Order] | ||
skus: [SKU] | ||
} | ||
``` | ||
|
||
The amount of potential URLs that the service may support is large - in larger services potentially unbounded. Just to name a few of the more important ones: | ||
|
||
``` http | ||
/skus, /skus/{id}, /orders, /orders/{id}, /orders/{id}/items, /orders/{id}/items/{id}, | ||
/orders/{id}/items/{id}/skus, /orders/{id}/items/{id}/skus/{id}, ... | ||
``` | ||
|
||
The path-centric view in RSDL allows to enumerate the allowed requests and specify which query options are supported for these requests. | ||
|
||
## HTTP capabilities | ||
The first level of the above mentioned capabilities is to specify the path of the URL and the HTTP method, Here is a partial RSDL to demonstrate this. | ||
|
||
``` rsdl | ||
path /orders { | ||
GET { ... } | ||
POST { ... } | ||
} | ||
|
||
path /orders/{id} { | ||
GET { ... } | ||
PATCH { ... } | ||
DELETE { ... } | ||
} | ||
|
||
path /orders/{id}/items { | ||
GET { ... } | ||
POST { ... } | ||
} | ||
|
||
path /orders/{id}/items/{id} { | ||
GET { ... } | ||
DELETE { ... } | ||
} | ||
|
||
path /skus { | ||
GET { ... } | ||
} | ||
|
||
``` | ||
The effect of this is that for each declared `path` and HTTP method the service is expected to return data, in the form specified by the corresponding type. The service is free to respond with success messages for other combinations but the service guarantees to work for the ones listed. | ||
|
||
Important to notice is that the paths after the `path` keyword are not literal paths but sort of templates. Many of the interesting paths in a REST service have path segments that are entity ids. This is indicated via the `{id}` syntax: the `id` in there is exactly the name of the key property. | ||
|
||
The placeholders `...` are used to declare which query options are supported by the service. For example a GET capability lists that certain $filter options are allowed or that paging is supported via the $top, $skip options. More details in the next sections. | ||
|
||
The specific capabilities that can be used in the HTTP capabilities section instead of the `...` in the example above can vary by HTTP method. Here is an overview | ||
|
||
| | filter | expand | paging | count | | ||
|--------|:--------------------:|:--------------------:|:--------------------:|:--------------------:| | ||
| GET | ✓ | ✓ | ✓ | ✓ | | ||
| POST | ✓<sup>1</sup> | ✓<sup>1</sup> | ✓<sup>1</sup> | ✓<sup>1</sup> | | ||
| PATCH | ✓<sup>1</sup> | ✓<sup>1</sup> | ✓<sup>1</sup> | ✓<sup>1</sup> | | ||
| PUT | ✓<sup>1</sup> | ✓<sup>1</sup> | ✓<sup>1</sup> | ✓<sup>1</sup> | | ||
| DELETE | | | | | | ||
|
||
[1] to shape the POST/PATCH/PUT response. Rarely used but supported<br/> | ||
[2] deleting multiple items. Rarely used but supported<br/> | ||
[!!TODO: check if filter segment on delete is allowed in RAPID ] | ||
|
||
## Individual Query capabilities | ||
|
||
### Filter capabilities | ||
|
||
The filter capability allows to specify which property can be used in which filter expression. There is a vast amount of possible filter expressions (see [OData System Query Option `$filter`](https://docs.oasis-open.org/odata/odata/v4.01/odata-v4.01-part1-protocol.html#sec_SystemQueryOptionfilter)). Therefore, the filter capabilities allow to specify a few well-known but restrictive expressions or allow any expression. | ||
|
||
The format for filter capabilities is a sequence of pairs of a so called operator group and a list of property names. An operator group is constraining the form of the expression allowed in the $filter system query option. | ||
|
||
| operator group | comment | | ||
|----------------|-----------------------------------------------------------------------------------------------------------------------| | ||
| eq | `<property> eq <literal>` or <br/> `<property> in (<literal1>, <literal2>, ... ) ` | | ||
| range | `<literal> le <property> and <property> le <literal>` <br/> or equivalent expressions with operators `ge`, `gt`, `lt` | | ||
| ranges | a disjunction of `range` expressions | | ||
| prefix | `startswith(<property>, <literal>)` | | ||
| text | `<string op>(<property>, <literal>)`,<br/>where `<string op>` is one of `startswith`, `endswith`, `contains` | | ||
| any | any expression including expressions combined with `not`, `and`, and `or` | | ||
|
||
In RSDL this | ||
|
||
```rsdl | ||
path /orders { | ||
GET { | ||
filter { | ||
eq { id name description createdDate fulfillmentDate } | ||
ranges { createdDate description } | ||
prefix { name } | ||
text { description } | ||
} | ||
|
||
filter { | ||
eq except { description } | ||
ranges { createdDate description } | ||
prefix { name } | ||
text { description } | ||
} | ||
Comment on lines
+133
to
+145
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. Really two occurrences of |
||
} | ||
} | ||
``` | ||
|
||
### Expand capabilities | ||
|
||
The expand capability allows to specify which properties can be used in `$expand` query parameter. | ||
|
||
The format is a sequence of properties that can be expanded. | ||
|
||
```rsdl | ||
path /orders { | ||
GET { | ||
expand { | ||
items | ||
} | ||
} | ||
} | ||
``` | ||
|
||
The expand capability introduces a nesting of capabilities since the type of the expandable property can itself be used in select, filter, and expand. | ||
|
||
```rsdl | ||
|
||
|
||
type Order { | ||
|
||
} | ||
|
||
capability Order defaultOrder { | ||
filter { | ||
eq { id name } | ||
prefix { nam description } | ||
range { createdDateTime } | ||
} | ||
expand { | ||
items { | ||
filter { | ||
eq { id name } | ||
prefix { name } | ||
} | ||
expand { | ||
sku | ||
} | ||
} | ||
} | ||
paging | ||
count | ||
} | ||
|
||
capability Order limitedOrder { | ||
filter { | ||
eq { id name } | ||
} | ||
} | ||
|
||
path /orders { | ||
GET { | ||
defaultOrder | ||
} | ||
} | ||
|
||
path /customers/{id}/orders { | ||
GET { | ||
limitedOrder | ||
} | ||
} | ||
``` | ||
|
||
- `/order?$expand=items` | ||
- `/order?$expand=items(expand=sku)` | ||
- `/order?$expand=items(expand=sku; filter=id eq '100')` | ||
- `/order?$expand=items(select=id; expand=sku; filter=id eq '100')` | ||
|
||
|
||
### Paging capabilities | ||
|
||
The paging capability allows to specify if a request returning a collection can specify $top and $skip query parameters. | ||
|
||
```rsdl | ||
path /orders { | ||
GET { | ||
paging | ||
} | ||
} | ||
``` | ||
|
||
The paging capability has no parameters itself. If a service uses a different way to implement paging, please refer to the [extensibility](#extensibility) section below | ||
|
||
The paging capability is typically seen in a GET capability but can also be nested in an expand capability. | ||
|
||
### Count capabilities | ||
|
||
The count capability allows to specify if a request returning a collection can specify $count query parameter. | ||
|
||
```rsdl | ||
path /orders { | ||
GET { | ||
count | ||
} | ||
} | ||
``` | ||
|
||
## Extensibility | ||
|
||
The RSDL API capabilities allow to specify custom capabilities, so called traits, for the service to implement that are outside the scope of the patterns known by the RSDL specification. | ||
|
||
One example for these could be the handling of requests when throttling is needed, where the service responds with certain headers for requests that cross a throttling threshold. There are multiple established pattern (protocol) to implement this but it is not covered in RSDL directly. But it can be specified in RSDL as an extension and the service implementation can read it and configure the right behavior based on the traits in RSDL. | ||
|
||
The individual trait is treated as simple opaque (meta-) data that is passed on the service in the form of a trait name and a list of key-value pairs. The implementation is then free to interpret the presence of the trait and the parameters as it sees fit. | ||
|
||
```rsdl | ||
path /orders { | ||
GET { | ||
traits { | ||
longRunningOperation | ||
topWithoutSkip | ||
throttling { level: "premium\u12a4" } | ||
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. Are the values always strings, or can they also be arrays, objects, numbers, booleans, i.e. "embedded JSON"? |
||
} | ||
} | ||
} | ||
``` | ||
|
||
|
||
## Other path based characteristics | ||
|
||
```rsdl | ||
path /user/{id}/recentOrders { | ||
# references an Order contained in /orders | ||
targets: /orders | ||
targets: unbound | ||
# 'targets' ':' ( path / 'unbound' ) | ||
Comment on lines
+275
to
+277
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.
|
||
|
||
GET { } | ||
} | ||
|
||
path /orders { | ||
# top level entity set | ||
GET { } | ||
|
||
} | ||
|
||
path /orders/{id}/items { | ||
# containment navigation (informally an entity set) | ||
GET { } | ||
|
||
} | ||
``` | ||
|
||
|
||
|
||
|
||
## Syntax | ||
|
||
!! NOTE: incomplete | ||
|
||
``` abnf | ||
path-capability = "path" path "{" | ||
[ get-capabilities ] | ||
[ post-capabilities ] | ||
[ patch-capabilities ] | ||
[ delete-capabilities ] | ||
"}" | ||
|
||
query-capabilities = [ expand-capabilities ] / | ||
[ filter-capabilities ] / [ select-capabilities ] / | ||
[ paging-capabilities ] / [ count-capabilities ] / | ||
Comment on lines
+310
to
+312
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. Better move after |
||
|
||
get-capabilities = "GET" "{" [ query-capabilities ] "}" | ||
|
||
post-capabilities = "POST" "{" "}" | ||
|
||
patch-capabilities = "PATCH" "{" "}" | ||
|
||
delete-capabilities = "DELETE" "{" "}" | ||
|
||
expand-capabilities = "expand" ["{" | ||
*( property-name "{" query-capabilities "}" ) | ||
"}"] | ||
|
||
filter-capabilities = "filter" ["{" operator-filter "}"] | ||
operator-filter = operator-group "{" *property-name "}" | ||
operator-group = "eq" / "range" / "ranges" / "prefix" / "strings" / "any" | ||
|
||
paging-capabilities = "paging" ["{" "}"] | ||
count-capabilities = "count" ["{" "}"] | ||
|
||
property-name = identifier | ||
path = "`" *( "/" identifier ) "`" | ||
|
||
``` | ||
|
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.
Sentence looks incomplete