DataProviderExtension
DataProviderExtension lets a plugin supply dropdown, lookup, and query data programmatically. Each provider registers under a globally unique provider key and returns a list of DataItem (value / label / metadata). When some lookup needs to compose data from multiple models, an external system, or a computation the DSL cannot express, a provider encapsulates that logic inside the plugin and the front end references it by key.
Concept
Each provider exposes a unique provider key in namespace:provider-name form (e.g. billing:currencies, hr:departments). The platform matches a DataProviderExtension by getProviderKey() through ExtensionRegistry.getDataProvider(providerKey) and calls fetchData(DataRequest) to retrieve rows.
DataRequest carries the context a provider needs:
tenantId— the current tenantsearchTerm— the user's typed search (for dropdown type-ahead)filters— an additional filter mapoffset/limit— pagination (defaultsoffset=0,limit=100)pluginId/namespace/providerKey/settings— remaining context
A provider returns List<DataItem>; each DataItem is a value (submitted value), a label (display value), and optional metadata (extra info map). The shape is lookup / dropdown oriented — not an arbitrary field-code → value row set.
Interface signature
package com.auraboot.framework.plugin.extension;
import org.pf4j.ExtensionPoint;
import java.util.List;
import java.util.Map;
public interface DataProviderExtension extends ExtensionPoint {
/** Unique provider key, format "namespace:provider-name". */
String getProviderKey();
/** Return data items for the request. */
List<DataItem> fetchData(DataRequest request);
/** Optional: total count matching the request, for pagination (default: size of fetchData). */
default long getCount(DataRequest request) {
return fetchData(request).size();
}
/** Optional: whether this provider handles the key (default compares getProviderKey()). */
default boolean supports(String providerKey) {
return getProviderKey().equals(providerKey);
}
/** Optional: whether results are cacheable (default true). */
default boolean isCacheable() { return true; }
/** Optional: cache TTL in seconds (default 300). */
default int getCacheTtlSeconds() { return 300; }
}DataItem and DataRequest are nested records on the interface:
record DataItem(String value, String label, Map<String, Object> metadata) {
DataItem(String value, String label) { this(value, label, Map.of()); }
}
record DataRequest(
Long tenantId, String pluginId, String namespace, String providerKey,
String searchTerm, Map<String, Object> filters,
int offset, int limit, Map<String, Object> settings
) {
static Builder builder() { /* ... */ }
}Implementation example
A lookup provider that returns a list of currencies:
package com.acme.billing.provider;
import com.auraboot.framework.plugin.extension.DataProviderExtension;
import org.pf4j.Extension;
import java.util.List;
import java.util.Map;
@Extension
public class CurrencyDataProvider implements DataProviderExtension {
@Override
public String getProviderKey() {
return "billing:currencies";
}
@Override
public List<DataItem> fetchData(DataRequest request) {
return List.of(
new DataItem("usd", "US Dollar", Map.of("symbol", "$")),
new DataItem("eur", "Euro", Map.of("symbol", "€")),
new DataItem("cny", "Renminbi", Map.of("symbol", "¥"))
);
}
}For type-ahead, filter on request.searchTerm(); for large lists, paginate by request.offset() / request.limit() and override getCount().
Registration
@Extension annotation only — discovery via META-INF/extensions.idx, collected at startup by ExtensionRegistry into getAllDataProviders(). Provider keys are globally namespaced; always prefix with your own namespace to avoid collisions (billing:currencies is preferable to bare currencies).
Common pitfalls
- Returning
nullitems, ornullvalue/label. Dropdowns render incorrectly; return an emptyListwhen there's no data. - Ignoring
getCount(). For large lookups, override it to return the real total and honouroffset/limitinfetchData(), or pagination breaks. - Bypassing tenant scope. If you query external or shared data, scope explicitly by
request.tenantId(). The platform does not filter your return for you. - Side effects in
fetchData(). The method is called frequently and results may be cached (seeisCacheable()/getCacheTtlSeconds()). Keep it read-only.
Related
- Page Designer — referencing lookups in forms and pages
- Custom Page Block — custom blocks
- DataAccessor — plugin read/write of dynamic entity data