Implicit and Externalized Fetches

When a ServiceDataProvider is configured with properties described in the Service Data Provider Properties section, it will, for the most part, manage fetching data and notifying components implicitly. The exception is the 'fetchChainId'.

Implicit Fetch

A typical configuration for an implicitly fetching ServiceDataProvider would look like this:

"incidentListDataProviderImplicit": {
  "type": "vb/ServiceDataProvider2",
  "constructorParams": [
    {
      "endpoint": "ifixitfast-service/getIncidents",
      "headers": {},
      "keyAttributes": "id",
      "itemsPath": "result",
      "uriParameters": {
        "technician": "{{ $application.user.userId }}"
      }
    }
  ]
}

It is important to note that a ServiceDataProvider variable does not cache its data, just its configuration. The data is also not persisted to history, session or localStorage.

Since the data can be arbitrarily large data sets, it is recommended that page authors use other means to cache data on the client, such as the JET offline toolkit cache. This applies to externalized fetches as well.

Externalized Fetch via an Action Chain

When a 'fetchChainId' property is present, the ServiceDataProvider delegates the fetch to the action chain. A typical configuration for a ServiceDataProvider variable (supporting a fetchFirst capability) that externalizes REST might look like this:

"variables": {
  "incidentListTableSource": {
    "type": "vb/ServiceDataProvider2",
    "constructorParams": [
      {
        "endpoint": "ifixitfast-service/getIncidents",
        "fetchChainId": "fetchIncidentListChain",
        "keyAttributes": "id",
        "itemsPath": "result",
        "responseType": "application:incidentsResponse"
      }
    ]
  }
},
"chains": {
  "fetchIncidentListChain": {
    ...
  },
}

Only the following properties are allowed to be configured (or are relevant):

  • capabilities: when this property isn't set, the 'fetchFirst' fetch capability is assumed.

  • endpoint: required to be set in order to determine capabilities for the service / endpoint

  • fetchChainId

  • itemsPath

  • keyAttributes

  • mergeTransformOptions: this property is defined on the ServiceDataProvider variable, because merging transform options only applies when an action chain (with a RestAction) is called in the context of a data provider fetch call.

  • responseType

The type definition of "application:incidentsResponse" used by the 'responseType' property can be seen in this example. This structure is similar to the one returned from a REST response. Note that itemsPath is always located within the 'body' property of the response that is returned.

For example, the app-flow.json file for the ServiceDataProvider configuration shown above could look like this:

"incidentsResponse": {
  "type": {
    "status": "string",
    "headers": "object",
    "body": {
      "result": "application:incidentSummary[]"
    }
  }
},
"incidentSummary": {
  "type": {
    "id": "string",
    "problem": "string",
    "priority": "string",
    "status": "string",
    "customer": "application:customer"
  }
},

A sample return value from the action chain would look like this:

{
  "status": "200",
  "headers": {},
  "body": {
    "result": [
      {
        "id": "incident_1",
        "problem": "heater broken",
        "priority": "high",
        "status": "open",
        "customer": {}
      }
    ]
  }
}

Generally, users externalize fetches to ensure full control over how the request and response are processed.

For example, users can connect custom sort and filter query parameters either in the service endpoint or in the REST action. This is the preferred configuration approach. Properties like sortCriteria, filterCriterion, transforms etc., are defined on the JET ListDataProviderView configuration, will eventually be combined with those configured on the Rest action and together will be used when building the Request. Any changes to these properties can be done via the mergeTransformsOptions property.

In the example below, the action chain 'fetchIncidentListChain' defined in the fetchChainId property of the ServiceDataProvider variable above has a typical chain configuration, one of which is a RestAction.

  1. The 'hookHandler' property under configuration chain variable will be automatically generated at design time and is always set to vb/RestHookHandler.

  2. If the REST response returns a structure that is exactly what the ServiceDataProvider expects, this can be returned directly (as in the example below). But if the REST response is different from the expected responseType, then an action that maps the REST response to the structure defined by 'responseType' on the SDP needs to be configured.

  3. The last action in the chain will always be a ReturnAction whose payload resembles the REST response whose body resembles 'responseType'. The incidentsResponse response variable in the chain is provided for clarity but is not used by the chain.

  4. If more fields are returned than what the responseType has, SDP will attempt to auto-map the result to the response type.
  5. It's important to not set the 'returnType' property when a ReturnAction is already present in the chain for SDP, because this additionally coerces the response returned to the caller.
"chains": {
   "fetchIncidentListChain": {
    "variables": {
      "configuration": {
        "type": {
          "hookHandler": "vb/RestHookHandler"
        },
        "description": "the configuration for the rest action",
        "input": "fromCaller",
        "required": true
      },
      "incidentsResponse": {
        "type": "application:incidentsResponse"
      }
    },
    "root": "fetchIncidentList",
    "actions": {
      "fetchIncidentList": {
        "module": "vb/action/builtin/restAction",
        "parameters": {
          "endpoint": "ifixitfast-service/getIncidents",
          "uriParams": {
            "technician": "{{ $application.user.userId }}"
          },
          "hookHandler": "{{ $variables.configuration.hookHandler }}",
          "requestTransformOptions": {
            "sort": "{{ $page.variables.sortExpression }}",
            "filter": "{{ $page.variables.filterAtomicExpression }}"
          },
          "requestTransformFunctions": {
            "paginate": "{{ $page.functions.paginate }}",
            "query": "{{ $page.functions.query }}",
            "filter": "{{ $page.functions.filter }}",
            "sort": "{{ $page.functions.sort }}"
          },
          "responseTransformFunctions": {
            "paginate": "{{ $page.functions.paginateResponse }}"
          }
        },
        "outcomes": {
          "success": "returnSuccessResponse",
          "failure": "returnFailureResponse"
        }
      },
      "returnSuccessResponse": {
        "module": "vb/action/builtin/returnAction",
        "parameters": {
          "outcome": "success",
          "payload": "{{ $chain.results.fetchIncidentList }}"
        }
      },
      "returnFailureResponse": {
        "module": "vb/action/builtin/returnAction",
        "parameters": {
          "outcome": "failure",
          "payload": "{{ $chain.results.fetchIncidentList }}"
        }
      }
    }
  }
}