Proper way of finding Resource using NamedResourceHandler

Hello, I’m building a custom alarm notification profile and I’m trying to search for the backing record for it. I have an extension point extending from AlarmNotificationProfileExtensionPoint, and a record NotificationConfig that backs it. I’ve been able to save this record in the resources/core/com.inductive.alarm-notif/alarm-notification/profile folder, and I’ve set up a NamedResourceHandler on my NotificationConfig.META so I can listen to changes and lookup the configs elsewhere in my module. When I try calling NamedResourceHandler.find(myProfileName), it returns as empty.

My suspicion is that because I’m providing the wrong ResourceType into my config’s META, so it isn’t looking in the right place:

public static final ResourceTypeMeta<NotificationConfig> META = ResourceTypeMeta.newBuilder(NotificationConfig.class)
        .resourceType(new ResourceType(MODULE_ID, "config"))
        .categoryName("Config")
        .defaultConfig(DEFAULT)
        .build();

Am I supposed to use the AlarmNotificationProfileConfig.RESOURCE_TYPE as the ResourceType? Using that results in a similar error as I came across here:

When I apply the solution from that post, I run into a different error, so I figured I might as well ask what the proper way to do it is.

You shouldn't be creating a ResourceTypeMeta at all for the settings record of an extension point.

See ignition-sdk-examples/slack-alarm-notification at ignition-8.3 · inductiveautomation/ignition-sdk-examples · GitHub, which is doing what you're trying to do by the sound of it (custom notification profile type with config/settings record).

When you're implementing an extension point, the 'manager' of that extension point owns the NamedResourceHandler. You would only need to instantiate or create your own if you have either your own extension point (very unusual in modules) or your own configuration resource class that does not participate in the extension point system.

Imagine an alarm notification profile that has "service accounts", but your service accounts are not 1:1 with notification profiles; then you would define a service account resource type + meta + named resource handler, but you would not create your own handler for the alarm notification extension point.

Thank you both for chiming in. The reason I was looking into the NamedResourceHandler was to try and replicate some of the IRecordListener behaviour from 8.1. We had a record listener for the notification profile settings in our old notification profiles listening for changes, though looking at it now, it might have been redundant. Does the notification profile restart after changes are made to the underlying config record?

The other reason was that I was looking for a lookup to replace the persistence interface query pattern. Should I be using gwContext.getConfigurationManager().getResource(new ResourcePath(AlarmNotificationProfileConfig.RESOURCE_TYPE, configName)) instead?

Generally speaking yes, though ultimately this is up to where the NamedResourceHandler is actually created/used.

Well, this depends entirely on what you're actually trying to do.
The most direct analogue is going to the configuration manager directly, yes.

I’m specifically trying to look up the config for the notification profile for use not in the notification profile, but elsewhere in my code. I just realized I need to fiddle with the Resource.getDataStream and JsonStreamParser if I got the getResource method. Is there a simpler way I could get at those configs?

You need an instance of AlarmNotificationManager.

Couple of ways to skin that particular cat, but here's what I'd recommend, somewhere in your module that properly depends on the alarm notification module as a (required) dependency:

    GatewayModuleHook hook = context.getModuleManager()
        .getModule("com.inductiveautomation.alarm-notification").getHook();
    if (hook instanceof AlarmNotificationContext alarmContext) {
      alarmContext.getAlarmNotificationManager().getProfile("some-profile").getStatus();
    }

Thanks for that Paul. I was able to cast the profile to my class and added a getter for my config. In a somewhat related tangent, my extension point for my notification profile are throwing startup errors. I have a custom named resource for account config records, and each notification profile has a reference to an account. This record is tracked by a named resource handler, and when my extension point’s createNewProfile is called, I have some code to look up the account config and feed it to my constructor. What seems to be happening is the alarm notification module starts up the notification profiles before my own module’s startup can occur, so that when it tries to look up the account config, it returns an empty optional.

Am I going to have to use the config manager to look up my account config or is there something I’m missing?

Another somewhat related question: in the older version of this module, we relied on an IRecordListener in the notification profile to listen for changes in the account settings. When the account settings were changed, some online account settings would get updated, with the relevant context/data being all in the notification profile at that time. Is the only way to do this in 8.3 using the named resource handler’s onResourceUpdated and looking up all notification profiles using that account?

Extension point lifecycle is complicated, and I believe does happen prior to your module's hook for reasons I don't remember offhand. I would generally recommend encoding some lazy pattern where you defer access to the account config until it's actually needed, instead of fetching it eagerly at runtime. There might be something better, but that's usually the easiest option.

As for record listening, it's not quite as direct but you can always add listeners for resource collections being added (addListener on ResourceCollectionManager), or once you have a particular collection, addResourceListener.

We're speaking very vaguely, though - if you can outline what you are ultimately trying to do (more concretely) I can potentially offer more specific advice on what I'd recommend/what our APIs are going to guide you towards.