Resource Sets

Many resources may be in one of n discrete states. The state of the resource is often represented by a string property named state. Changing states differs from updating other properties which are typically done with PUT or PATCH, because such transitions are guarded by business rules which the service enforces. For example, in the Transfers API, you can’t cancel a scheduled transfer which is currently being processed by the bank (its too late!), nor can you resume a transfer which is not suspended. To enable (and enforce) this in a clean API manner, Apiture APIs model state transitions with operations which place a resource into one of n discrete resource sets. The resource set represents (abstractly) a set of all resources that are in a specific state.

For example, after creating a scheduled transfer, the user may want to suspend the recurrence, resume a suspended recurring transfer, or cancel it. Rather than requiring the user to PATCH the resource (which can update many other fields), the state property is immutable via PATCH or PUT. To change the state of the transfer, use one of the links in the transfer resource. A scheduled transfer response contains these links in the HAL response:

{
  "_id" : "3d28fb7c-16e9-4ee5-bd37-37971bbe5e46",
  "_profile" : "http://api.apiture.com/schemas/transfers/scheduledTransfer/v1.0.0/profile.json",
   "amount": {
    "value": "345.50",
    "currency": "USD"
  },
  "description": "Car payment",
  "state": "recurring",
  "schedule": {
    "start": "2018-02-05",
    "count": 3,
    "every": "P1M",
    "end": "2021-04-05"
  },
  "..." : "... other properties ...",
  "_links" : {
    "self" : { "href" : "/transfers/scheduledTransfers/3d28fb7c-16e9-4ee5-bd37-37971bbe5e46" },
    "apiture:suspendScheduledTransfer" : {
      "href" : "https://api.thirdpartybank.com/transfers/suspendedScheduledTransfers?scheduledTransfer=3d28fb7c-16e9-4ee5-bd37-37971bbe5e46"
      },
    "apiture:resumeScheduledTransfer" : {
      "href" : "https://api.thirdpartybank.com/transfers/resumedScheduledTransfers?scheduledTransfer=3d28fb7c-16e9-4ee5-bd37-37971bbe5e46"
      },
    "apiture:cancelScheduledTransfer" : {
      "href" : "https://api.thirdpartybank.com/transfers/canceledScheduledTransfers?scheduledTransfer=3d28fb7c-16e9-4ee5-bd37-37971bbe5e46"
      }
 }

This is HATEOAS in action! These links are implemented by a POST to resource set URIs, passing the ID or URI of the current transfer as a query parameter. The complexity is hidden behind the link, but inspecting the OpenAPI definition and the links at runtime would show that one honors the link with a POST operation, such as the following:

POST https://api.thirdpartybank.com/transfers/suspendedScheduledTransfers?scheduledTransfer=3d28fb7c-16e9-4ee5-bd37-37971bbe5e46

The API can enable these links if and only if the state transition is allowed: if a transfer is in a processing state, the apiture:suspendScheduledTransfer link is not available in the set of links within the transfer’s JSON representation. Also, the client cannot change the state at all from scheduled to processing or from processing to failed because those state transitions are managed by the service only. Thus, there is no link or operation to put the transfer into the processing resource set or into the completed resource set.

Note that other links and state transitions can be added in the future. Also, entitlements limit who can execute those links/operations: the user can suspend their scheduled transfers, but only the service can execute the transition to processing to actually execute the transfer of funds in the back end, or set the terminal state to failed or completed.

This pattern follows the uniform interface constraint of REST APIs: the URIs are addresses of resources (in this case, resource sets), and the APIs use POST to perform the operation of placing a resource into that resource sets.