Skip to content

Latest commit

 

History

History
523 lines (441 loc) · 22.7 KB

11.5 Operations.md

File metadata and controls

523 lines (441 loc) · 22.7 KB

##subsec Operations

Custom operations (Actions and Functions) allow encapsulating logic for modifying or requesting data that goes beyond simple CRUD described in the preceding sections of this chapter. See Action, ActionImport, Function, and FunctionImport in OData-CSDLJSON or OData-CSDLXML.

##subsubsec Binding an Operation to a Resource

Actions and Functions MAY be bound to any type or collection, similar to defining a method in a class in object-oriented programming. The first parameter of a bound operation is the binding parameter.

The namespace- or alias-qualified name of a bound operation may be appended to any URL that identifies a resource whose type matches, or is derived from, the type of the binding parameter. The resource identified by that URL is used as the binding parameter value. Only aliases defined in the metadata document of the service can be used in URLs.

::: example Example ##ex: the function MostRecentOrder can be bound to any URL that identifies a SampleModel.Customer

<Function Name="MostRecentOrder" IsBound="true">
  <Parameter Name="customer" Type="SampleModel.Customer" />
  <ReturnType Type="SampleModel.Order" />
</Function>

:::

::: example Example ##ex: invoke the MostRecentOrder function with the value of the binding parameter customer being the entity identified by http://host/service/Customers(6)

GET http://host/service/Customers(6)/SampleModel.MostRecentOrder()

:::

::: example Example ##ex: the function Comparison can be bound to any URL that identifies a collection of entities

<Function Name="Comparison" IsBound="true">
  <Parameter Name="in" Type="Collection(Edm.EntityType)" />
  <ReturnType Type="Diff.Overview" />
</Function>

:::

::: example Example ##ex: invoke the Comparison function on the set of red products

GET http://host/service/Products/$filter(Color eq 'Red')/Diff.Comparison()

:::

##subsubsec Applying an Action to Members of a Collection

A bound operation with a single-valued binding parameter can be applied to each member of a collection by appending the path segment /$each to the resource path of the collection, followed by a forward slash and the namespace- or alias-qualified name of the bound operation. In this case the type of the collection members MUST match or be derived from the type of the binding parameter.

The resource path of the collection MAY contain type-cast or filter segments to subset the collection.

The response is a collection with members that are instances of the result type of the bound operation. If the bound operation returns a collection, the response is a collection of collections.

::: example Example ##ex: invoke the MostRecentOrder function on each entity in the entity set Customers

GET http://host/service/Customers/$each/SampleModel.MostRecentOrder()

:::

The client MAY specify the continue-on-error preference, in which case the service MAY continue processing actions after a failure. In this case, the service MUST, regardless of the return preference, return a response containing at least the members identified by the request for which the action failed. Such members MUST be annotated with term Core.DataModificationException with a failedOperation value of invoke.

If the continue-on-error preference has not been specified, and the service is unable to invoke the action against all of the entities identified by the request, then it MUST return an error response and MUST NOT apply the action to any of the members of the collection.

##subsubsec Advertising Available Operations within a Payload

Services MAY return actions and/or functions bound to a particular entity or entity collection as part of the representation of the entity or entity collection within the payload. The representation of an action or function depends on the format.

::: example Example ##ex: given a GET request to http://host/service/Customers('ALFKI'), the service might respond with a Customer that includes the SampleEntities.MostRecentOrder function bound to the entity

{
  "@context": ...,
  "CustomerID": "ALFKI",
  "CompanyName": "Alfreds Futterkiste",
  "#SampleEntities.MostRecentOrder": {
    "title": "Most Recent Order",
    "target": "Customers('ALFKI')/SampleEntities.MostRecentOrder()"
  },
  ...
}

:::

An efficient format that assumes client knowledge of metadata may omit actions and functions from the payload  whose target URL can be computed via metadata following standard conventions defined in OData-URL.

Services can advertise that a function or action is not available for a particular instance by setting its value to null.

::: example Example ##ex: the SampleEntities.MostRecentOrder function is not available for customer 'ALFKI'

{
  "@context": ...,
  "CustomerID": "ALFKI",
  "CompanyName": "Alfreds Futterkiste",
  "#SampleEntities.MostRecentOrder": null,
  ...
}

:::

##subsubsec Functions

Functions are operations exposed by an OData service that MUST return data and MUST have no observable side effects.

##subsubsubsec Invoking a Function

To invoke a function bound to a resource, the client issues a GET request to a function URL. A function URL may be obtained from a previously returned entity representation or constructed by appending the namespace- or alias-qualified function name to a URL that identifies a resource whose type is the same as, or derived from, the type of the binding parameter of the function. The value for the binding parameter is the value of the resource identified by the URL prior to appending the function name, and additional parameter values are specified using inline parameter syntax. If the function URL is obtained from a previously returned entity representation, parameter aliases that are identical to the parameter name preceded by an at (@) sign MUST be used. Clients MUST check if the obtained URL already contains a query part and appropriately precede the parameters either with an ampersand (&) or a question mark (?).

Services MAY additionally support invoking functions using the unqualified function name by defining one or more default namespaces through the Core.DefaultNamespace term defined in  OData-VocCore.

Functions can be used within $filter or $orderby system query options. Such functions can be bound to a resource, as described above, or called directly by specifying the namespace- (or alias-) qualified function name. Parameter values for functions within $filter or $orderby are specified according to the inline parameter syntax.

To invoke a function through a function import the client issues a GET request to a URL identifying the function import and passing parameter values using inline parameter syntax. The canonical URL for a function import is the service root, followed by the name of the function import. Services MAY support omitting the parentheses when invoking a function import with no parameters, but for maximum interoperability MUST also support invoking the function import with empty parentheses.

If the function is composable, additional path segments may be appended to the URL that identifies the composable function (or function import) as appropriate for the type returned by the function (or function import). The last path segment determines the system query options and HTTP verbs that can be used with this this URL, e.g. if the last path segment is a multi-valued navigation property, a POST request may be used to create a new entity in the identified collection.

::: example Example ##ex: add a new item to the list of items of the shopping cart returned by the composable MyShoppingCart function import

POST http://host/service/MyShoppingCart()/Items
 
...

:::

Parameter values passed to functions MUST be specified either as a URL literal (for primitive values) or as a JSON formatted OData object (for complex values, or collections of primitive or complex values). Entity typed values are passed as JSON formatted entities that MAY include a subset of the properties, or just the entity reference, as appropriate to the function.  

If a collection-valued function has no result for a given parameter value combination, the response is the format-specific representation of an empty collection. If a single-valued function with a nullable return-type has no result, the service returns 204 No Content.

If a single-valued function with a non-nullable return type has no result, the service returns 4xx. For functions that return a single entity 404 Not Found is the appropriate response code.

For a composable function the processing is stopped when the function result requires a 4xx response, and continues otherwise.

Function imports preceded by the $root literal MAY be used in the $filter or $orderby system query options, see OData-URL.

##subsubsubsubsec Inline Parameter Syntax

Parameter values are specified inline by appending a comma-separated list of parameter values, enclosed by parenthesis to the function name.

Each parameter value is represented as a name/value pair in the format Name=Value, where Name is the name of the parameter to the function and Value is the parameter value.

::: example Example ##ex: invoke a Sales.EmployeesByManager function which takes a single ManagerID parameter via the function import EmployeesByManager

GET http://host/service/EmployeesByManager(ManagerID=3)

:::

::: example Example ##ex: return all Customers whose City property returns "Western" when passed to the Sales.SalesRegion function

GET http://host/service/Customers?
      $filter=Sales.SalesRegion(City=$it/City) eq 'Western'

:::

A parameter alias can be used in place of an inline parameter value. The value for the alias is specified as a separate query option using the name of the parameter alias.

::: example Example ##ex: invoke a Sales.EmployeesByManager function via the function import EmployeesByManager, passing 3 for the ManagerID parameter

GET http://host/service/EmployeesByManager(ManagerID=@p1)?@p1=3

:::

Services MAY in addition allow implicit parameter aliases for function imports and for functions that are the last path segment of the URL. An implicit parameter alias is the parameter name, optionally preceded by an at (@) sign. When using implicit parameter aliases, parentheses MUST NOT be appended to the function (import) name. The value for each parameter MUST be specified as a separate query option with the name of the parameter alias. If a parameter name is identical to a system query option name (without the optional $ prefix), the parameter name MUST be prefixed with an at (@) sign.

::: example Example ##ex: invoke a Sales.EmployeesByManager function via the function import EmployeesByManager, passing 3 for the ManagerID parameter using the implicit parameter alias

GET http://host/service/EmployeesByManager?ManagerID=3

:::

Non-binding parameters annotated with the term Core.OptionalParameter defined in OData-VocCore MAY be omitted. If it is annotated and the annotation specifies a DefaultValue, the omitted parameter is interpreted as having that default value. If omitted and the annotation does not specify a default value, the service is free on how to interpret the omitted parameter.

##subsubsubsec Function overload resolution

The same function name may be used multiple times within a schema, each with a different set of parameters. For unbound overloads the combination of the function name and the unordered set of parameter names MUST identify a particular function overload. For bound overloads the combination of the function name, the binding parameter type, and the unordered set of names of the non-binding parameters MUST identify a particular function overload.

All unbound overloads MUST have the same return type. Also, all bound overloads with a given binding parameter type MUST have the same return type.

If the function is bound and the binding parameter type is part of an inheritance hierarchy, the function overload is selected based on the type of the URL segment preceding the function name. A type-cast segment can be used to select a function defined on a particular type in the hierarchy, see OData-URL.

Non-binding parameters MAY be marked as optional by annotating them with the term Core.OptionalParameter defined in OData-VocCore. All parameters marked as optional MUST come after any parameters not marked as optional.

A function overload is selected if

  • The set of specified parameters exactly matches a function overload, or else
  • The set of specified parameters matches a subset of parameters that includes all non-optional parameters of exactly one function overload.

Services SHOULD avoid ambiguity, i.e. the combination of the function name, the unordered set of non-optional non-binding parameter names, plus the binding parameter type for bound overloads SHOULD identify a particular function overload. If there is ambiguity, then services MAY return 400 Bad Request with an error response body stating that the request was ambiguous.

##subsubsec Actions

Actions are operations exposed by an OData service that MAY have side effects when invoked. Actions MAY return data but MUST NOT be further composed with additional path segments.

##subsubsubsec Invoking an Action

To invoke an action bound to a resource, the client issues a POST request to an action URL. An action URL may be obtained from a previously returned entity representation or constructed by appending the namespace- or alias-qualified action name to a URL that identifies a resource whose type is the same as, or derives from, the type of the binding parameter of the action. The value for the binding parameter is the resource identified by the URL preceding the action name, and only the non-binding parameter values are passed in the request body according to the particular format.

Services MAY additionally support invoking actions using the unqualified action name by defining one or more default namespaces through the Core.DefaultNamespace term defined in  OData-VocCore.

To invoke an action through an action import, the client issues a POST request to a URL identifying the action import. The canonical URL for an action import is the service root, followed by the name of the action import. When invoking an action through an action import all parameter values MUST be passed in the request body according to the particular format.

Non-binding parameters that are nullable or annotated with the term Core.OptionalParameter defined in OData-VocCore MAY be omitted from the request body. If an omitted parameter is not annotated (and thus nullable), it MUST be interpreted as having the null value. If it is annotated and the annotation specifies a DefaultValue, the omitted parameter is interpreted as having that default value. If omitted and the annotation does not specify a default value, the service is free on how to interpret the omitted parameter. Note: a nullable non-binding parameter is equivalent to being annotated as optional with a default value of null.

4.01 services MUST support invoking actions with no non-binding parameters and parameterless action imports both without a request body and with a request body representing no parameters, according to the particular format. Interoperable clients SHOULD always include a request body, even when invoking actions with no non-binding parameters and parameterless action imports.

If the action returns results, the client SHOULD use content type negotiation to request the results in the desired format, otherwise the default content type will be used.

The client can request whether any results from the action be returned using the return Prefer header.

Actions that create and return a single entity follow the rules for entity creation and return a Location header that contains the edit URL or read URL of the created entity.

Actions without a return type respond with 204 No Content on success.

To request processing of the action only if the binding parameter value, an entity or collection of entities, is unmodified, the client includes the If-Match header with the latest known ETag value for the entity or collection of entities. The ETag value for a collection as a whole is transported in the ETag header of a collection response.

::: example Example ##ex: invoke the SampleEntities.CreateOrder action using /Customers('ALFKI') as the customer (or binding parameter). The values 2 for the quantity parameter and BLACKFRIDAY for the discountCode parameter are passed in the body of the request. Invoke the action only if the customer's ETag still matches.

POST http://host/service/Customers('ALFKI')/SampleEntities.CreateOrder
If-Match: W/"MjAxOS0wMy0yMVQxMzowNVo="`
Content-Type: application/json

{
  "items": [
    { "product": 4001, "quantity": 2 },
    { "product": 7062, "quantity": 1 },
  ],
  "discountCode": "BLACKFRIDAY"
}

:::

##subsubsubsec Action Overload Resolution

The same action name may be used multiple times within a schema provided there is at most one unbound overload, and each bound overload specifies a different binding parameter type.

If the action is bound and the binding parameter type is part of an inheritance hierarchy, the action overload is selected based on the type of the URL segment preceding the action name. A type-cast segment can be used to select an action defined on a particular type in the hierarchy, see OData-URL.

##subsec Asynchronous Requests

A Prefer header with a respond-async preference allows clients to request that the service process a Data Service Request asynchronously.

If the client has specified respond-async in the request, the service MAY process the request asynchronously and return a 202 Accepted response. A service MUST NOT reply to a Data Service Request with 202 Accepted if the request has not included the respond-async preference.

Responses that return 202 Accepted MUST include a Location header pointing to a status monitor resource that represents the current state of the asynchronous processing in addition to an optional Retry-After header indicating the time, in seconds, the client should wait before querying the service for status. Services MAY include a response body, for example, to provide additional status information.

A GET request to the status monitor resource again returns 202 Accepted response if the asynchronous processing has not finished. This response MUST again include a Location header and MAY include a Retry-After header to be used for a subsequent request. The Location header and optional Retry-After header may or may not contain the same values as returned by the previous request.

A GET request to the status monitor resource returns 200 OK once the asynchronous processing has completed. For OData 4.01 and greater responses, or OData 4.0 requests that include an Accept header that does not specify application/http, the response MUST include the AsyncResult response header. Any other headers, along with the response body, represent the result of the completed asynchronous operation. If the GET request to the status monitor includes an OData-MaxVersion header with a value of 4.0 and no Accept header, or an Accept header that includes application/http, then the body of the final 200 OK response MUST be represented as an HTTP message, as described in RFC7230, which is the full HTTP response to the completed asynchronous operation.

A DELETE request sent to the status monitor resource requests that the asynchronous processing be canceled. A 200 OK or a 204 No Content response indicates that the asynchronous processing has been successfully canceled. A client can request that the DELETE should be executed asynchronously. A 202 Accepted response indicates that the cancellation is being processed asynchronously; the client can use the returned Location header (which MUST be different from the status monitor resource of the initial request) to query for the status of the cancellation. If a delete request is not supported by the service, the service returns 405 Method Not Allowed.

After a successful DELETE request against the status monitor resource, any subsequent GET requests for the same status monitor resource returns 404 Not Found.

If an asynchronous request is cancelled for reasons other than the consumers issuing a DELETE request against the status monitor resource, a GET request to the status monitor resource returns 200 OK with a response body containing a single HTTP response with a status code in the 5xx Server Error range indicating that the operation was cancelled.

The service MUST ensure that no observable change has occurred as a result of a canceled request.

If the client waits too long to request the result of the asynchronous processing, the service responds with a 410 Gone or 404 Not Found.

The status monitor resource URL MUST differ from any other resource URL.