Sunday 9 January 2022

ApexTypeImplementor in Spring 22



Note: This feature may be in Beta come Spring 22 - the release notes in Salesforce Help say that it is in beta, the PDF version doesn't mention it. If I get confirmation either way I'll update this post! Remember that Spring 22 is still in preview, so this feature might never see the light of day anyway!

Introduction

The Spring 22 release of Salesforce introduces the ApexTypeImplementor object, which contains information about the classes that implement an interface, either directly or indirectly. You've been able to access Apex class bodies for years, so picking up the direct implementation of an interface has been possible for a while. Figuring out that that the interface is implemented in the inheritance hierarchy gets a bit more tricky. In either case, having the platform take care of it for you takes away a chunk of code, which is always a good thing.

Use Cases

When I first came across this new feature it had a touch of the "solution looking for a problem" about it, but once I had a chance to dig into it in a little more detail, I have a number of scenarios where I'll be able to apply it as soon as it is available/maybe out of beta.

Rule Engine

The most common use case from my perspective is the configurable plug and play engine. I have several rules engines where a class is considered a rule because it implements a specific interface. I'll have a number of classes that implement rule variants, and these rules are enabled and applied in a specific order based on configuration. 

The ApexTypeImplementor doesn't help in terms of which rule classes should be applied, as I still want it to be driven through configuration rather than the code just applying everything it can find in whatever order they come back. Where it does help is to assist the admin who is configuring the rules. Rather than them having to remember the names of classes or look them up and copy/paste from other setup pages, I can create a custom configuration page that offers them only the classes that implement the interface, thus ensuring that the configured classes are available.

Setup Verification 

A less obvious use case is confirmation that a system is setup and configured correctly - i.e. that there is no missing or obviously broken configuration that will cause errors at runtime. Each feature that can be enabled has an associated class that checks the feature is configured correctly (or at least plausibly as it's quite hard to confirm correctness without running a bunch of tests). As long as each of these classes implements the same interface, I can execute code from an admin clicking a button or quick action that finds all of the classes that implement the verification interface. Each of these is instantiated and the method that checks the configuration is executed. This works really well if the additional features are delivered via org dependent or unlocked packages, as the code that implements the feature and the code that checks it are developed in tandem.

Training Confirmation

The third use case that I have is around training, and ensuring that the training has been retained. I have a collection of packages that I can drop into an org that ask the user to carry out some configuration or regular business work, and then check that they carried it out correctly. 

At the moment the packages create configuration entries when they are installed, and those configuration entries are used to generate cards on an application page so that users can access and check the challenges. Because I want all of the challenges that are installed to be available to the user, with the ApexTypeImplementor I can do away with the configuration entries and just show the users details of all the classes that implement the interface. 

Sample App

I've created a sample application in my Spring 22 repository to show ApexTypeImplementor used in anger. I have an interface that a class implements to describe itself to interested parties :

public interface ClassInfoIF 
{
    String getDescription();
    String getAuthor();
}

I'm using a Lightning Web Component to display the details of the classes, which extracts them from a dedicated controller (that also implements the interface!) : 

@AuraEnabled(cacheable=true)
public static List<ClassDetail> GetClassDetails()
{
    List<ClassDetail> classDetails=new List<ClassDetail>();
    List<ApexTypeImplementor> classInfoTypes = 
           [SELECT ApexClassId, ClassName, ClassNamespacePrefix
            FROM ApexTypeImplementor
            WHERE InterfaceName = 'ClassInfoIF' and IsConcrete=true];

    for (ApexTypeImplementor classInfoType : classInfoTypes)
    {
        ClassDetail classDetail=new ClassDetail();
        classDetail.classId=classInfoType.ApexClassId;
        classDetail.fullName='';
        if (null!=classInfoType.ClassNamespacePrefix)
        {
            classDetail.fullName=classInfoType.ClassNamespacePrefix + '.';
        }

        classDetail.fullName+=classInfoType.ClassName;
        ClassInfoIF classInfo = 
                    (ClassInfoIF) Type.forName(classInfoType.ClassNamespacePrefix,
                                            classInfoType.ClassName).newInstance();

        classDetail.description=classInfo.getDescription();
        classDetail.author=classInfo.getAuthor();
        classDetails.add(classDetail);
    }

    return classDetails;
}

The bolded sections above show the query that extracts the classes that implement the interface, and the code that constructs the class as an instance of ClassInfoIF and executes the methods that describe the class. If a new class is added to the org that implements this interface, it is automatically included the next time I access the page.

I've put in a few fake classes too, including one that implements the interface by overriding methods from its superclass to check the indirect side of things works too. Accessing the application page shows the information I'm interested in for each class:


Related

No comments:

Post a Comment