Service Data Provider Properties

ServiceDataProvider (SDP) exposes properties that a variable of this type can use to configure. All properties are directly accessible through the variable. Expressions like {{ $page.variables.incidentListTableSource.filterCriterion }} can be used where expressions are supported, including component (markup) attributes.

endpoint

A string that is the REST endpoint in the format 'serviceName/endpointName'. The endpoint is typically a GET endpoint that returns a collection, and is defined in the service model.

fetchChainId

A string that is the 'id' of the actionChain to use to fetch the results. See Implicit and Externalized Fetches for more information.

headers

An object of the names of one or more header properties, and the corresponding values. Any headers specified here are also set on the externalized REST action by the design time. Alternatively, if a fetchChainId is not specified, headers are passed through to the internal REST calling mechanism by the ServiceDataProvider.

idAttribute

Supports composite keys, using multiple properties. It is a string or array that is the field or fields in the response data for each row, that represents the 'id', or key, field. Deprecated; use keyAttributes instead.

keyAttributes

A string or array, that is the field or fields in the response data for each row, that represent(s) the 'id' (or key) field. Can be:
  • A property name - the key, in various contexts, will also be a string.

  • An array of property names - the key will also be an array, of values.

  • @value, use all properties - the key will also be an array, of values.

  • @index, use the index as the key - the key will be an integer.

itemsPath

A string that is the path to the root of the actual collection in the response. Example 'result' if the response returned from the endpoint looks like {count: 10, result: [...]}

capabilities 

An object that defines the capabilities supported by the ServiceDataProvider and the endpoint it uses. The capabilities object is defined by the JET DataProvider API.

This property serves as a hint for UI components bound to an SDP variable, to know about the capabilities the endpoint supports and use the correct fetch / sort / filter behaviors.

A variable of type vb/ServiceDataProvider generally defaults to a 'fetchFirst' capability if no capability is specified. This means that the endpoint associated to the SDP is assumed to support a fetchFirst behavior. The same endpoint can support other 'fetch' capabilities as well.

For example, with business object REST API GETAll endpoints, the same endpoint can provide fetchFirst / fetchByKeys ('lookup') and fetchByOffset ('randomAccess') behaviors.

With third-party services it's important for authors to carefully consider the behaviors their endpoint supports before configuring the SDP property. For example if the third-party service endpoint provides optimal 'lookup' based fetchByKeys, and a 'randomAccess' based fetchByOffset, it's important that the author implements the appropriate transforms functions to support these capabilities. Refer to the section on Request Transformation Function, particularly the 'paginate' and 'fetchByKeys' types for details.

If the same endpoint cannot be used to provide the other fetch behaviors then it might be required to use a Multi-Service Data Provider. In all other cases SDP will fallback to using the fetchFirst behavior to provide sub-optimal implementations of fetchByKeys and fetchByOffset behavior.

Key / Type sub-key Values Example Description
fetchFirst (optional) / object implementation "iteration" Blank

fetchFirst is not a capability supported by the JET DataProvider contract but is a new capability that SDP introduces.

Why this is needed?

SDP variables created prior to this enhancement will always assume the 'fetchFirst' capability, for backwards compatibility. New SDP variables created in DT 'may' choose to set this property to correctly reflect the capability the endpoint supports.

fetchByKeys (optional) / object implementation

"lookup"

"iteration"

getCustomers endpoint supports a lookup based fetchByKeys.

"customersSDP": {
  "type": "vb/ServiceDataProvider",
  "defaultValue": {
    "endpoint": "demo-data-service/getCustomers",
    "keyAttributes": "id",
    "itemsPath": "result",
    "capabilities": {
      "fetchByKeys": {
        "implementation": "lookup"
      }
    }
  }
},
(see JET DataProvider API)

the "lookup" based implementation indicates the endpoint supports fetching key(s) data using a single request.

  • For business object REST API services, most GETAll endpoints that provide a fetchFirst capability also support querying for a key. So the default business object REST API transforms uses the fetchFirst endpoint to query for the key(s) and this property need not be set. In rare cases an entirely different endpoint might be required to fetch key data, in which case a Multi Service Data Provider might be needed.
  • For all other types of services, authors must ensure a lookup based 'fetchByKeys' transforms function is provided. If not an "iteration" based implementation is used.

when a "lookup" based implementation is not provided, SDP falls back to an "iteration" based implementation. This is non-performant because it uses fetchFirst / iteration to iterate over rows until the requested key(s) are located, before returning the requested key data in the form - FetchByKeysResults.

Blank multiKeyLookup

"yes"

"no"

"capabilities" : {
  "fetchByKeys": {
    "implementation": "lookup",
    "multiKeyLookup": "no"
  }
}
Tells SDP whether endpoint can fetch multiple or a single key at a time.

defaults to 'yes'. only available when implementation is 'lookup'. This is automatically supported for business object REST API services that use the provided business object REST API transforms.

  • when fetchByKeys() is called with more than one key and the capability only supports lookup by a single key, then as an optimization SDP makes multiple fetch calls against the endpoint, one per key and assembles the results
fetchByOffset (optional) / object implementation

"iteration"

"randomAccess"

getIncidents endpoint supports a lookup based fetchByOffset.
"incidentsSDP": {
  "type": "vb/ServiceDataProvider",
  "defaultValue": {
    "endpoint": "demo-data-service/getIncidents",
    "keyAttributes": "id",
    "itemsPath": "result",
    "capabilities": {
      "fetchByOffset": {
        "implementation": "randomAccess"
      }
    }
  }
}

the "randomAccess" based implementation requires an endpoint that supports random access of requested page from an offset.

  • For business object REST API services, most GETAll endpoints support querying from a specified offset, and so the default business object REST API transforms uses this implementation automatically.
  • For all other types of services, authors must ensure a "randomAccess" based (paginate) transforms function is provided. If not an "iteration" based implementation is used.

when a "randomAccess" based implementation is not provided, SDP falls back to an "iteration" based implementation. This is non-performant because it uses fetchFirst / iteration to iterate over pages until the desired offset is reached.

filter / object operators array of supported operators
"capabilities" : {
  "filter": {
    "operators": ["$eq", "$or"]
  }
}
a map of supported filter operators.

Note: VB does not support Set types so use Array for operators

This doc does not go into the details of wiring up the 'filter' and 'sort' capabilities, but when these are set the getCapability() method on the DataProvider will use the information defined here.

For more on filter operators, see the JET documentation: oj.FilterCapability.html#operators

It's a combination of attribute and compound operators.

  • For list of attribute operators - oj.AttributeFilterDef.html#op
  • For list of component operators - oj.CompoundFilterDef.html#op
Blank textFilter any value
"capabilities" : {
  "filter": {
    "textFilter": true
  }
}

any truthy value can be set for textFilter. By default SDP sets this to true.

This value tells the consumer of the SDP that text filtering is enabled.

For business object REST API endpoints, text filtering works by default with some minimal configuration, but the service author is expected to write a filter transforms function for any complex text filtering. See Write a Filter Transforms Function for Text Filtering for details.

For 3rd party endpoints the service author must write a filter transforms function that handles the text filter, or they can turn off the capability entirely.

sort / object Blank Blank
"capabilities" : {
  "sort": {
    "attributes": "single"
  }
}
array of supported sort operators.

For more on sort capabilities, see oj.SortCapability in the JET documentation.

responseType

The type of the response that is returned by the ServiceDataProvider. This can be an object or array. When provided it is used for two purposes:

  1. To determine the fields to fetch (aka select fields) from the endpoint. A transforms author will be provided these fields via the 'select' transforms function, if they wish to edit it, but ultimately the final response is shaped by the ServiceDataProvider based on the responseType set on it (see point 2 below).
    1. When using an Oracle Cloud Application-based endpoint with ServiceDataProvider, the built-in business object REST API transforms are loaded automatically (vb/BusinessObjectsTransform for Business Objects or business object REST API services), and the select transforms function creates a 'fields' query parameter with the desired fields, both scalar and objects (and recursively includes the object's fields, as well). This will both include and expand fields.
  2. To automatically shape the response (from the fetch call) to match the responseType. Shaping a response to match the responseType usually means that missing data is 'fixed up'. This is done to ensure that binding expressions used in components work without issues.
    1. For example, an expression like {{ $current.objectVar.propA }} will fail if objectVar is missing.

      Note:

      Auto-shaping of response data is based on rules determined by the Visual Builder type system. If authors do not want the automatic shaping of data performed by ServiceDataProvider to introduce unexpected behavior, they must either ensure that the response data is 'complete', or they need to wrap binding expressions to guard against missing data. Response data can be made 'complete' either on the server-side, or the client can use a 'body' response transforms function to fix up incomplete data based on business rules.

Some additional things to consider:

When ServiceDataProvider externalizes data fetch

When author chooses to externalize the ServiceDataProvider fetch, the design-time often configures a chain with a RestAction, with most properties from the ServiceDataProvider on the action (RestAction and ServiceDataProvider configuration share similar properties). It also adds a 'hookHandler' property. There are certain properties that are best set on the ServiceDataProvider and not on the RestAction. Refer to the Externalized Fetch section for a list of properties that must be configured on the ServiceDataProvider variable.

It is recommended that 'responseType' always be configured on the ServiceDataProvider so that the 'select fields' are requested with the fetch call, and auto shaping of the response does not yield unexpected results (see note). The former is always determined by ServiceDataProvider.

Note:

For external fetches, if the RESTAction also has 'responseType' set, then it gets applied first to the response. Not only is this redundant and not performant, it's also problematic if the responseType on RestAction were to auto-shape the response to have fewer attributes than what the 'select fields' requested.

When ServiceDataProvider is used with dynamic components

Another reason for recommending that 'responseType' always be configured on the ServiceDataProvider is to address dynamic UI cases, where the responseType is not known at design-time, and 'select fields' are only provided at runtime (see note). In fact the responseType is often set to a wildcard type ('any' / 'any[]').

Note:

Dynamic collection components determine the list of attributes to fetch only at runtime. And this is provided via a fetchFirst() call to ServiceDataProvider (using the 'attributes' parameter) and not configured using the 'responseType' property (see JET FetchListParameters). When 'attributes' are provided, 'responseType' is ignored. There is also no default auto-shaping done when attributes are provided.

body

An object that represents the body for a fetch request, where the request is made to a POST based endpoint. Another example is where ElasticSearch based endpoints use a POST method to fetch search results, where the search criteria are set using the body.

uriParameters

An object that defines one or more properties that are parameters on the endpoint URL. For example, the FixitFast service has an endpoint to retrieve all incidents for a technician using the URL http://.../incidents?technician={technician}. Here 'technician' is a query parameter that will be defined under uriParameters like this:

"uriParameters": {
  "technician": "{{ $page.variables.appUser.name }}"
},

The uriParameters are used to perform a simple string replacement if the URL includes parameters that must be substituted before it's resolved. Otherwise the parameters are appended to the URL. The uriParameters are also passed to the query transform function (details below), so page authors can use the value of the above property to tweak the URI further if needed. 

pagingCriteria

An object that defines the paging defaults if needed. Generally a paging component (like listView or table) will provide the data provider with size or offset or both. If the component does not provide either size or offset, the ServiceDataProvider will use the values set on this property as defaults. The pagingCriteria are then passed to the paginate transform function (see below). Supports the following properties.

  • size: number of rows to fetch by default, when no size is provided by caller.

  • offset: the offset to start the fetch from. Defaults to 0.

  • maxSize: the default maximum number of rows to fetch when the caller (usually a component) requests that all rows be fetched. Some JET components, like oj-chart, often request all rows by setting { size: -1 }. This property can be used to control the maximum number of rows to fetch, when it may not be performant to ask the service endpoint to return all rows. If this property is not set, then the size: -1 property is passed through to the paginate transforms, and it may be necessary for transforms authors to handle -1 as the size.

  • iterationLimit: the upper limit of the number of rows that can be fetched during iteration cycles. This is only used when size isn't provided and continuous iteration of rows is required. An example is when a list of values component tries to fetch labels for selected keys and the underlying multiServiceDataProvider is not configured with a 'lookup' based fetchByKeys capability. So the ServiceDataProvider reverts to using an optimized 'iteration' based implementation that is based on the fetchFirst capability. When this happens, there could be numerous fetch requests hitting the endpoint. If the service or endpoint would like to limit this, it's important to set this value. This also gets used with the optimized fetchByOffset capability for its optimized iteration based implementation.

Page authors need to understand how the above properties are used by the ServiceDataProvider during a fetch call:

  1.  Generally, the page size used by a fetch can be defaulted using the pagingCriteria.size. This is only used when a component does not explicitly provide a size. The same is true for an offset.

  2.  When the size is provided by the caller (for example, components), this overrides the default pagingCriteria.size set on the ServiceDataProvider. 

    Note:

    When components do ask for a specific number of rows, and the ServiceDataProvider returns more rows than were explicitly requested, some components can get in an indeterminate state. In such cases, to control the fetchSize, it's better to set this property on the component. Specifically, oj-list-view has a scrollPolicyOptions.fetchSize.
  3.  Some components do not support a fetchSize property. If this is the case, you can force the fetch to be a different value from what the component requested by writing a paginate transform function where the size can be tweaked. But you might then encounter the indeterminate state described in #2.

  4.  It is generally not recommended that you set endpoint-specific size and offset parameters using the uriParameters property directly (for example, the business object REST API supports 'limit' and 'offset' query parameters that are the equivalent of the pagingCriteria.size and offset). If you do, you are on your own to write a business object REST API transform that can merge/use the value set both in the uriParameters and pagingCriteria properties. And you are also likely run into the caveats explained in #3.

filterCriterion 

An object representing a single attribute filter criterion with the properties { op, attribute, value }, where 'op' is one of the supported JET attribute operators, and 'attribute' and 'value are the name and value of the attribute respectively. It may also represent a compound filter criterion {op, criteria}, where 'op' is a compound operator, and ‘criteria’ is an array of attributes or compound criterion.

Most complex filter expressions can be expressed using the JET filterCriterion structure. Sometimes you may need to externalize fetches to build your filter criteria for the REST action.

Note:

The business object REST API transforms shipped with Visual Builder support all attribute operators except $regex. They can transform a simple attribute filter or a compound filter that is an array of attribute filter criterion.
// attribute criterion
{
  "op": "$eq",
  "attribute": "empName",
  "value": "Lucy"
}
  
// In the business object REST API, the above criterion will become the following query parameter:
//   "q=empName = 'Lucy'"
// compound criterion
{
  "op": "$or",
  "criteria": [
    {
      "op": "$gt",
      "attribute": "hireDate",
      "value": "2015-01-01"
    },
    {
      "op": "$le",
      "attribute": "hireDate",
      "value": "2018-01-01"
    }
  ]
}
  
// In the business object REST API, the above criterion will become the following query parameter:
//   "q=hireDate > '2015-01-01' or hireDate <= '2018-01-01'"

Complex grouped criteria can be expressed in JSON using the filterCriterion API, but a transform function that can handle such grouped (or nested) criteria will need to be written by page authors for the business object REST API or for other external REST services, in order to build the appropriate query parameter.

{
  "op": "$and",
  "criteria": [
    {
      "op": "$sw",
      "attribute": "project",
      "value": "BUFF"
    },
    {
      "op": "$or",
      "criteria: [
        {
          "op": "$ge",
          "attribute": "label",
          "value": "foo"
        },
        {
          "op": "$le",
          "attribute": "label",
          "value": "bar"
        }
      ]
    }
  ]
}

// In the business object REST API, the above criterion will become the following query parameter:
// "q=((project LIKE 'BUFF%') and ((label >=  'foo) or (label <= 'bar')))"

sortCriteria

An array of objects, where each object is an atomic sort expression of the form shown here.  If you have more complex structures for representing sortCriteria, you can use the externalized fetch option to build sort criteria and provide it to the REST action. See Implicit and Externalized Fetches for details.

[{
  "attribute": "<name of the field>", 
  "direction": "<'ascending' (default) or 'descending'>" 
}]

When using multiple attributes for the sortCriteria, you specify them separated by commas:

[
 {
  "attribute": "col2",
  "direction": "ascending"
 },
 {
  "attribute": "col3",
  "direction": "ascending"
 }
]

mergeTransformOptions 

This property allows a page author to set a callback to fix up or merge the final transforms options that are passed to the transform functions configured on the ServiceDataProvider. Let's say a sample endpoint, GET /customers, supports an 'ids' query parameter that can used to query customers by specific keys. For example:

/customers?ids=cus-101,cus-103

A component like oj-select-many might call the ServiceDataProvider requesting the customer data for specific keys by calling fetchByKeys() with these keys: ['cus-101', 'cus-103'].

The ServiceDataProvider does not support a declarative way to automatically map these keys programmatically to the 'ids' query parameter on the URL. Therefore, it might be necessary for the page author to use this property to set a function callback that will fix up the query transforms option. For details on writing this function, see Merge Transform Options Function.

transformsContext

A context object passed to the transform functions for both request and response. For fetchFirst calls, the context will be available for all iterations using the same iterator. Authors can manage this object as they wish. If this property is not set, an empty Object is provided by default to all transform functions. When a fetchMetadata property is provided as part of a fetch*() call, then this property is automatically set on the transformsContext Object and made available to transform functions.

  • fetchMetadata
    For Elastic searches where the query can be arbitrarily complex, callers can send extra search metadata via the fetch call. This parameter can be used to tweak the body that is used as POST-body in the query.

    Note:

    This is a Preview API and subject to change.
  • textFilterAttributes

    See Write a Filter Transforms Function for Text Filtering for details on this property.

totalSize

See getTotalSize

transforms

An object that has two properties for specifying 'request' and 'response' transform functions (callbacks).

Request transformation (or transform) functions are generally specified on the service (or endpoint) definition as it applies to all usages of the service. The transform functions specified here are only applicable for the current usage of the service or endpoint.

Request transform functions are primarily used to transform the URL or Request configuration before a request is sent to the endpoint.

Response functions can be used to process the response and return any additional state along with the response. Additional state is saved as internal state on the data source variable.

At design time, the page author will need to know whether the endpoint supports paging, sorting, filtering (or QBE), and the format/syntax for specifying these. Using the transform functions, the page author can tweak the Request to build a URL containing the paging, sorting, filtering params, and additional endpoint specific query params. 

  • request: An object whose properties refer to the type of the request transform functions, and the value the actual function. The following types are supported. See Request Transformation Function for details.

    • paginate: a paginate function that implements code to transform the request for pagination (or iterating through record sets) specific to the endpoint.

    • sort: a sort function that implements code to transform the request for sorting, specific to the endpoint.

    • filter: a filter function. Note: Refer to the next section for details on how to use the transform functions. 

    • query: a query function, to pre-process query parameters available through the uriParameters property.

    • select: a select (fields) function used to build the list of fields to fetch, if the endpoint supports it.

    • body: a body transform function that allows page authors to tweak the body if needed before the fetch call is made.

    • fetchByKeys: transforms function that allows a page author to take a key or Set of keys passed in via the options, and update the request to fetch requested keys.
  • response: An object whose properties also refer to the type of the response transform function.  See Response Transformation Functions for details.

    • paginate: This transform function is called immediately after the REST layer receives a response. It is called with the response so this function can process it and return an object with a group of properties set. The returned object is the primary way ServiceDataProvider obtains information about the paging state of the request:

      • totalSize: <optional> used to inform SDP what the totalSize of the result is.
      • hasMore: <generally requiredc> A boolean that indicates whether there are more records to fetch. Example in business object REST API usecases this would map to the hasMore boolean property commonly returned in the response. See explanation below for behavior of SDP when hasMore is not set.
      • pagingState: <optional> This can be used to store any paging state specific to the paging capability supported by the endpoint. In 1.0.0, this property can be used in the response paginate transform function, to set additional paging state. Which will then be passed 'as is' to the request paginate transform function, for the next fetch call.
    • body: This transform function is called immediately after the REST layer receives a response. It is a hook for authors to transform the response body, and is not guaranteed to be called in any specific order.

    The way this works is an iterating component will get the AsyncIterator from the dataProvider (like ServiceDataProvider) and keep iterating until there is no more data to fetch, or until the component viewPort is filled, or until its current scrollPosition is reached (this might be needed when a selected row is several pages down), whichever comes first. So it's extremely important for SDP to have the above information, to know when to stop iterating.

Missing 'hasMore' property in the paginate

In the event that service implementors may not have configured a paginate transform, we provide the following fallback behavior. If the first fetch request from by the SDP's AsyncIterator, has no 'hasMore' through the paginate response, SDP assumes there are no more records to fetch and iterator is marked as done. This behavior at least allows components to render some data without causing repetitive fetches. Of course this means scrolling through component will not fetch next set, if the endpoint did indeed have more rows to fetch.