How to identify read-only Historian providers?

Some historian providers like the DB Table Historian (WideDbHistorian) or the Simulator Historian (SimulatorHistorian) do not accept history writes.

In the Tag Actor, in Java, valid historians for storage are identified as follows:

public void configureTagModel(MutableConfigurationPropertyModel model) {
    TagHistoryProps.register(model);
    List<String> names = this.manager.getHistorianNames().stream()
        .filter(name -> this.manager.getStorageEngine(name).isPresent())
        .toList();
    model.setAllowedValues(TagHistoryProps.HistoryProvider, names);
}

One of our modules has a gateway config page (Web UI) where you can select a historian, etc. However, I can’t find any information that distinguishes a read-only historian provider from another.
Some of them expose a storage metric, but it doesn’t seem reliable to base this on that property, since some historians might not manage/report this metric.

See this route:

http://localhost:8088/data/api/v1/resources/list/com.inductiveautomation.historian/historian-provider

Another route returns the list of historian types, but all historians report canCreate => true.

http://localhost:8088/data/api/v1/resources/type/com.inductiveautomation.historian/historian-provider

Does anyone know if this information is exposed natively via one of the OpenAPI routes (without having to re-implement this logic)?

That is what the filter is doing. For each history provider name, it tests if the same-named storage engine exists.

That’s not really what I was asking.
I already understand what the filter is doing on the Java side.
My question was mainly whether any of the OpenAPI routes expose this information explicitly (read-only vs write-capable), so that I don’t have to re-implement the same logic in our module.

From what I’ve seen so far, it doesn’t look like this information is exposed, but I wanted to double-check in case I’m overlooking something.

1 Like

Here’s what I ended up implementing:

  1. I copied the same mechanism used by the Tag Actor into a utility class.
public class HistorianUtils {
    public static Object getTagHistoryProviders(RequestContext requestContext, HttpServletResponse httpServletResponse){
        JsonArray histories = new JsonArray();

        HistorianManagerImpl m = HistorianGatewayHook.get(SyncManager.gatewayContext).getHistorianManager();
        List<String> names = m.getHistorianNames().stream()
                .filter(name -> m.getStorageEngine(name).isPresent())
                .toList();
        names.forEach(histories::add);

        return histories;
    }
}
  1. I exposed a route that uses this utility.
@Override
public void mountRouteHandlers(RouteGroup routes) {
    routes.newRoute("/api/v1/xxxx/tag-history-providers")
            .type(RouteGroup.TYPE_JSON)
            .method(HttpMethod.GET)
            .requirePermission(PermissionType.READ)
            .handler(HistorianUtils::getTagHistoryProviders)
            .openApi(api -> api
                    .tag("xxxx")
                    .summary("Tag history providers")
                    .description("Returns the list of tag history providers available on this gateway."))
            .mount();
}

Route : http://localhost:8088/data/sofrel/api/v1/xxxx/tag-history-providers

1 Like

Yeah, the /api/v1/resources/ endpoints are common to all resource types that register a meta. So the canCreate here is just saying that new instances of the type can be created; not the same as the question you're asking.

There's only one bespoke route created by the historian module at the moment, and it's just used to list remote history providers. I think your workaround is the way to go.

1 Like