19 ACTION INVOKE RESOURCE

The action invoke resource is used to either invoke an action on a particular domain object instance or a service, or to just validate arguments to an action without invoking it. It is usually obtained from the detailed representation §C18.2 returned by an action resource §C17.5.

The endpoint URL for this resource for a domain service is:

/services/{serviceId}/actions/{actionId}/invoke

where:

  • {serviceId} is a unique identifier for the service

  • {actionId} is the action identifier

The endpoint URL for this resource for a domain object is:

/objects/{domainType}/{instanceId}/actions/{actionId}/invoke

where:

  • {domainType} uniquely identifies the object’s type, and

  • {instanceId} uniquely identifies an object instance of that type

  • {actionId} is the action identifier

19.1 HTTP GET

Invoke an action and return a representation. Alternatively, validate the query arguments without invoking the action.

The action invoked must be query-only (does not modify any persisted objects); a typical example is to search for objects from a domain service repository.

The action cannot be void (it must return some representation).

19.1.1 Request

The request can either be to invoke the action, or to request validation of arguments using the reserved x-ro-validate-only query parameter §A3.2.

19.1.1.1 Query String

Query arguments should be formatted as a map (§A2.9.2), and encoded in the URL (§A2.9.2.3). Note that if any argument is a blob/clob, then its value must be in-lined (URL encoded for a blob) [1].

In addition, the following may optionally be included in the map:

  • x-ro-domain-model (optional, §A3.1)

    • "simple"

    • "formal"

  • x-ro-validate-only (optional, §A3.2)

    • "true"

      • the argument map can be incomplete; only those arguments provided will be validated.

19.1.1.2 Headers

  • Accept

    • application/json

    • application/json;profile="…​/action-result"

There is no need to pass If-Match for query-only actions.

19.1.1.3 Body

  • N/A

19.1.2 Success Response

19.1.2.1 Status code

  • 200 "OK"

19.1.2.2 Headers

  • Content-Length:

    • size of the entity body

  • Content-Type (if returning a domain object):

    • application/json;profile="…​/action-result";x-ro-domain-type="yyy"

      • where yyy indicates the domain type (for object representations, §A2.4.2);

        • the domain type id (if simple scheme)

        • URI of domain type (if formal scheme)

  • Content-Type (if returning a list of domain objects):

    • application/json;profile="…​/action-result";x-ro-element-type="yyy"

      • where yyy indicates the domain type (for object representations, §A2.4.2);

        • the domain type id (if simple scheme)

        • URI of domain type (if formal scheme)

  • Content-Type (if returning a scalar or void):

    • application/json;profile="…​/action-result"

  • Caching headers:

    • TRANSACTIONAL, see §A2.13

      • if the object is transactional

    • NON_EXPIRING, see §A2.13

      • if the implementation can determine that the returned representation is safe to cache (e.g. the returned objects are immutable reference data)

Note that an ETag is never returned for an action result. A client that wishes to modify the returned domain object must therefore re-retrieve it explicitly.

19.1.2.3 Body

As per §C19.4.

19.2 HTTP PUT

Invoke an action and return a representation if the action returns a result. Alternatively, validate the query arguments but do not invoke the action.

The action invoked must be idempotent (though may have side-effects). An example might be Order#submit(), which (depending on how the application logic is written) might have the same post-conditions irrespective of whether the order has already been submitted or not.

19.2.1 Request

19.2.1.1 Query String

  • none

19.2.1.2 Headers

  • Accept

    • application/json

    • application/json;profile="…​/action-result"

  • If-Match

    • timestamp digest

      • obtained from ETag header of representation

19.2.1.3 Body

Arguments should be formatted as a map (§A2.9.2), and sent as the body (§A2.9.2.3). Note that if any argument is a blob/clob, then its value must be in-lined (URL encoded for a blob).

In addition:

  • x-ro-domain-model (optional, §A3.1)

    • "simple"

    • "formal"

  • x-ro-validate-only (optional, §A3.2)

    • "true"

      • only validate the request, do not invoke the action

19.2.2 Success Response

As per §C19.1.2.

19.3 HTTP POST

Invoke an action, and return a representation if the action returns a result. Alternatively, validate the query arguments but do not invoke the action. The action invoked can have side effects and need not be idempotent.

19.3.1 Request

19.3.1.1 Query String

  • none

19.3.1.2 Headers

  • Accept

    • application/json

    • application/json;profile="…​/action-result"

  • If-Match

    • timestamp digest

      • obtained from ETag header of representation

19.3.1.3 Body

Arguments should be formatted as a map (§A2.9.2), and sent as the body (§A2.9.2.5). Note that if any argument is a blob/clob, then its value must be in-lined (URL encoded for a blob).

In addition:

  • x-ro-domain-model (optional, §A3.1)

    • "simple"

    • "formal"

  • x-ro-validate-only (optional, §A3.2)

    • "true"

      • only validate the request, do not invoke the action

19.3.2 Success Response

19.3.2.1 Status code

Successfully invoking an action with possible side effects can return either a 200 or a 201.

  • 200 "OK"

    • the action was successfully executed.

  • 201 "Created"

    • only permitted when the action returns a domain object (that is "resultType" json-property is "object")

    • indicates that this object was newly created.

19.3.2.2 Headers

  • Location (if returning 201):

    • URL of the newly-created action

  • Content-Length:

    • size of the entity body

  • Content-Type (if returning a domain object):

    • application/json;profile="…​/action-result";x-ro-domain-type="yyy"

      • where yyy indicates the domain type (for object representations, §A2.4.2;

        • the domain type id (if simple scheme)

        • URI of domain type (if formal scheme)

  • Content-Type (if returning a list of domain objects):

    • application/json;profile="…​/action-result";x-ro-element-type="yyy"

      • where yyy indicates the domain type (of the objects referenced in the list, §A2.4.2;

        • the domain type id (if simple scheme)

        • URI of domain type (if formal scheme)

  • Content-Type (if returning a scalar or void):

    • application/json;profile="…​/action-result"

  • Caching headers:

    • TRANSACTIONAL, see §A2.13

      • if the object is transactional

    • NON_EXPIRING, see §A2.13

      • if the implementation can determine that the returned representation is safe to cache (e.g. the returned objects are immutable reference data)

Note that an ETag is never returned for an action result. A client that wishes to modify the returned domain object must therefore follow the self link on the in-lined object to retrieve that object directly as an object representation (which will then have an Etag).

19.3.2.3 Body

As per §C19.4. If a 201 is returned, the "resultType" json-property must be "object".

19.4 Representation

If the "x-ro-validate-only" query parameter was passed in and the validation succeeded, then no representation will be returned. Instead:

  • if the validation succeeded, then a 204 (success, no content) is returned

  • If the validation failed then a representation will be returned, with a status code 400 (bad request).

See §C13 for further details.

Otherwise (ie, if the invocation was not validate-only), then all action invocations will return an actionresult representation. This representation provides details of the action invocation, and (for non-void actions) also in-lines the representation of the result of the invocation.

For example:

{
  "links": [ {
      "rel": "self",
      "href": "http://~/services/TaskRepository/actions/countUrgentTasksFor/invoke",
      "type": "application/json;profile=\".../action-result\"",
      "arguments": {
        "employee": {
          "href": "http://~/objects/EMP/090123"
        }
      }
    }, ...
  ],
  "resultType": ...
  "value": ...,
  "extensions": { ... }
}

where:

JSON-Property Description

links

list of links to other resources.

links[rel=self]

(optional) link to the action invocation resource that generated the representation (applies only to query-only actions)

resultType

either "object", "list", "scalar" or "void" result (optional) the action result itself.
Not present if void action.

extensions

additional metadata about the representation.

The "self" link can be used as a bookmark so that the action can easily be resubmitted. However, the link is only included in the representation if the action is query-only. This is to prevent accidental bookmarking of links that if followed would result in side-effects.

The "resulttype" indicates whether there is an in-lined representation (for an action returning a domain object, a list, a scalar) or none (if void).

Finally, the "result" holds the representation of the returned domain object, list, or scalar. This is discussed in sections below.

19.4.1 Action returning a Domain Object

If the action invocation returns a domain object, then the actionresult representation will in-line the domain object’s representation (§C14.1):

Slide17
Figure 1. ACTION RESULT FOR OBJECT

For example, the following might be the result of invoking an action representing Customer's favoriteProduct() action:

{
  "links": [ {
      "rel": "self",
      "href": "http://~/objects/CUS/123/actions/favoriteProduct/invoke",
      "type": "application/json;profile=\".../action-result\"",
      "arguments": {},
      "method": "GET"
  } ],
  "resultType": "object", "
  result": {
    "links": [ {
        "rel": "self",
        "href": "http://~/objects/PRD/2468",
        "type": "application/json;profile=\".../object\"",
        "method": "GET"
      }, ...
    ],
    "members": { ... },
    "extensions": { ... } ...
  },
  "extensions": { ... }
}

Note that this representation has two "self" links:

  • links[rel=self]

    • is the link to the action invocation.

  • result.links[rel=self]

    • is the link to the returned domain object.

If the action returned null, then the "result" json-property will still be present, but set to the JSON value null:

{
  ...
  "resultType": "object",
  "result": null,
  ...
}

19.4.2 Action Returning a List

If the action invocation returns a list, then the actionresult representation will in-line a list representation (§B11):

Slide6
Figure 2. ACTION RESULT FOR LIST

For example, the following might be the result of invoking an action resource §C17.5 representing CustomerRepository's findBlacklistedCustomers() action:

{
  "links": [ {
      "rel": "self",
      "href": "http://~/services/CustomerRepository/actions/findBlackListedCustomers/invoke",
      "type": "application/json;profile=\".../action-result\"",
      "arguments": {},
      "method": "GET"
  } ],
  "resultType": "list",
  "result": {
    "links": [ {
        "rel": ".../element-type",
        "href": "http://~/domain-types/CUS,
        "type": "application/json;profile=\".../domain-type\"",
        "method": "GET"
    } ],
    "value": [ {
        "ref": ".../element",
        "href": "http://~/objects/CUS/123",
        "type": "application/json;profile=\".../object\"",
        "method": "GET"
      }, {
        "ref": ".../element",
        "href": "http://~/objects/CUS/456",
        "type": "application/json;profile=\".../object\"",
        "method": "GET"
      }, ...
    ],
    "extensions": { ... }
  },
  "extensions": { ... }
}

Actions that return no links typically are expected to return an empty list:

{
  ...
  "resultType": "list",
  "result": {
    ...
    "value": [ ]
    ...
  },
  ...
}

Although not recommended, it is also legal for actions to return a null list. In this case the "result" json-property will still be present, but will be set to the JSON value null:

{
  ...
  "resultType": "list",
  "result": null
  ...
}

19.4.3 Action returning a Scalar Value

If the action invocation returns a scalar, then the actionresult representation will in-line a scalar representation (§B12):

Slide7
Figure 3. ACTION RESULT FOR SCALAR

For example, the TaskRepository's countUrgentTasksFor(Employee) action might generate the following representation:

{
  "links": [ {
      "rel": "self",
      "href": "http://~/services/TaskRepository/actions/countUrgentTasksFor/invoke",
      "type": "application/json;profile=\".../action-result\"",
      "arguments": {
        "employee": {
          "href": "http://~/objects/EMP/090123"
         }
      },
      "method": "GET"
  } ],
  "resultType": "scalar",
  "result": {
    "links": [ {
        "rel": ".../returntype",
        "href": "http://~/domain-types/int,
        "type": "application/json;profile=\".../domain-type\"",
        "method": "GET"
    } ],
    "value": 25,
    "extensions": { ... }
  },
  "extensions": { ... }
}

As for actions returning lists and domain objects, if the scalar return type is non-primitive and a null is returned, then the "result" json-property will be set to the JSON null value:

{
  ...
  "resultType": "scalar",
  "result": null ...
}

19.4.4 Action returning a Void

If the action invocation does not have a return type (known as a ‘void’ method in some programming languages), then the simple actionresult representation (with no in-lined representation) will be returned.

Slide18
Figure 4. ACTION RESULT FOR VOID

For example, the Customer's toggleBlacklistStatus() action might generate the following representation:

{
  "links": [ {
      "rel": "self",
      "href": "http://~/objects/CUS/123/actions/toggleBlacklistStatus/invoke",
      "type": "application/json;profile=\".../action-result\"",
      "arguments": {},
      "method": "GET"
    }, ...
  ],
  "resultType": "void",
  "extensions": { ... }
}

Note that there is no "result" json-property.


1. It seems highly unlikely that a query-only action would have a blob/clob argument, but it is theoretically allowable. One hi-tech use case could be to search images of Customers' faces against an image obtained from a webcam. However, if the encoded size of the blob/clob exceeds the query string limit, then the action must be marked as idempotent in order that the argument be passed in the request body of a PUT.