I'll share the entirety of the extension point, it doesn't have much else in it.
From what I can tell, the ExtensionPoint interface's default implementation of settingsType() uses defaultSettings() to figure out the class. So I need to have at least one of those methods implemented for things to work. (That is why they're both there).
Historian Settings
package redacted.gateway;
import com.inductiveautomation.historian.gateway.api.config.HistorianSettings;
import com.inductiveautomation.ignition.gateway.dataroutes.openapi.annotations.*;
import com.inductiveautomation.ignition.gateway.web.nav.FormFieldType;
public record REDACTEDSettings(
@FormCategory("Connectivity")
@FormField(FormFieldType.REFERENCE)
@Label("Database Connection")
@Required
@Description("The database connection to use")
@FormReferenceType("ignition/database-connection")
String databaseConnection,
@FormCategory("Advanced")
@Label("Poll Rate")
@FormField(FormFieldType.NUMBER)
@DefaultValue("60000")
@Required
@Description("The rate (in milliseconds) at which the historian should query the datasource to check for updates. Set the the value to anything less than or equal to 0 to only poll the values on the historian's startup.")
int pollRate
) implements HistorianSettings {
public REDACTEDSettings copyWithDatabaseConnection(String newDatabaseConnection) {
return new REDACTEDSettings(newDatabaseConnection, this.pollRate);
}
}
ExtensionPoint
package redacted.gateway;
import com.inductiveautomation.historian.gateway.api.Historian;
import com.inductiveautomation.historian.gateway.api.HistorianExtensionPoint;
import com.inductiveautomation.historian.gateway.api.HistorianProvider;
import com.inductiveautomation.historian.gateway.api.config.HistorianSettings;
import com.inductiveautomation.ignition.gateway.config.*;
import com.inductiveautomation.ignition.gateway.dataroutes.openapi.SchemaUtil;
import com.inductiveautomation.ignition.gateway.datasource.DatasourceManager;
import com.inductiveautomation.ignition.gateway.model.GatewayContext;
import com.inductiveautomation.ignition.gateway.web.nav.WebUiComponent;
import com.inductiveautomation.ignition.gateway.web.nav.ExtensionPointResourceForm;
import java.util.Optional;
public class REDACTEDExtensionPoint extends HistorianExtensionPoint<REDACTEDSettings> {
public static final String TYPE_ID = "redacted.REDACTED";
public REDACTEDExtensionPoint() {
super(TYPE_ID, "REDACTED.Meta.Name", "REDACTED.Meta.Desc");
}
@Override
public Historian createHistorianProvider(GatewayContext gatewayContext, DecodedResource<ExtensionPointConfig<HistorianProvider, HistorianSettings>> decodedResource) throws Exception {
REDACTEDSettings settings = this.getSettings(decodedResource.config()).get();
addReferenceProperty("databaseConnection",
ref -> ref
.targetType(DatasourceManager.DATABASE_CONNECTION_RESOURCE)
.value(REDACTEDSettings::databaseConnection)
.onUpdate(REDACTEDSettings::copyWithDatabaseConnection)
);
return new REDACTED(gatewayContext, decodedResource.name(), settings);
}
@Override
public Optional<Class<REDACTEDSettings>> settingsType() {
return Optional.of(REDACTEDSettings.class);
}
@Override
public Optional<REDACTEDSettings> defaultSettings() {
return Optional.of(new REDACTEDSettings("", 60000));
}
@Override
public boolean canCreate() {
return true;
}
@Override
public Optional<WebUiComponent> getWebUiComponent(ComponentType type) {
return Optional.of(new ExtensionPointResourceForm(
RESOURCE_TYPE,
"Historian",
TYPE_ID,
SchemaUtil.fromType(HistorianProvider.class),
SchemaUtil.fromType(REDACTEDSettings.class)
));
}
}
getExtensionPoints in module hook
@Override
public List<? extends ExtensionPoint<?>> getExtensionPoints() {
return List.of(new REDACTEDExtensionPoint());
}
The above code works for creating a historian, but breaks with Extension Point Form Not Found when trying to edit it.
Looking at the SDK examples, some extension points implement the defaults in a constructor of the record class, in addition to implementing defaultSettings() and the @Default annotations. This is the case with: User Source Profile, and Secret Provider. I tried this briefly as well, but no luck.
Stepping through with a debugger, reveals some interesting things. On the historians page, in the list of extension points, addComponent and editComponent are nulls for all the IA historians, but instances of ExtensionPointResourceForm for my custom one:
Screenshot
However, doing the same for the Secret Provider example reveals that all the extension points have ExtensionPointResourceForm instances (not just the example module):
Screenshot
I'm not sure if that explains anything, but because of these inconsistencies compiling the example modules and comparing them to my own has not been as helpful as I hoped.
In the screenshot, you can see that the default settings have been created with the correct values and (as far as I can tell) in the correct format:
Screenshot