The Implicit LearnCard

Easily keep up-to-date!

What is it?

When implementing Control Planes or Methods, you may have noticed an unused _learnCard parameter get added. This is what we call the Implicit LearnCard, and it can be very helpful!

What does it do?

The Implicit LearnCard allows your Plugin's methods to access an up-to-date version of the LearnCard that it has been added to. This means that you can have access to a full LearnCard without having to wrap your Plugin in a function!

Why would you use it?

There are a few use-cases for using the Implicit LearnCard, such as:

  • Calling a method that is implemented in the same plugin

  • Ensuring the most up-to-date method is called

Let's implement a quick plugin that generates names to demonstrate this. The plugin will expose three methods: generateFirstName, generateLastName, and generateFullName. The types for this plugin look like this (using the Plugin type):

src/types.ts
import { Plugin } from '@learncard/core';

export type NamePluginMethods = {
    generateFirstName: () => string;
    generateLastName: () => string;
    generateFullName: () => string;
};

export type NamePluginType = Plugin<'Name', any, NamePluginMethods>;

The implementation for this plugin can have generateFullName easily call generateFirstName and generateLastName without having to define them outside of the function thanks to the Implicit LearnCard:

src/index.ts
import { NamePluginType } from './types';

export const NamePlugin: NamePluginType = {
    name: 'Name',
    methods: {
        generateFirstName: () => 'First', // Not very useful....
        generateLastName: () => 'Last',   // Imagine a huge list of names being chosen at random
        generateFullName: learnCard => 
            `${learnCard.invoke.generateFirstName()} ${learnCard.invoke.generateLastName()}`
    }
};

While this example may be a bit contrived, it does demonstrate a few important benefits of the Implicit LearnCard:

  • We were able to reuse plugin methods without defining them outside the plugin

  • Other plugins are now able to override the functionality of generateFirstName and generateLastName and generateFullName will automatically call the overriden methods!

    • This allows plugins to easily define interfaces for sub-plugins or plugin extensions.

    • This also gives plugins the ability to monkey-patch pieces of another plugin, enhancing or changing that earlier plugin's functionality

When would you not use it?

Privacy

The Implicit LearnCard can be very handy for plugin extensibility and composition. However, there are times you don't want a plugin to be able to be monkey-patched or extended. In such a case, it is a better idea not to use the Implicit LearnCard, and just define the re-usable functions outside the plugin:

src/index.ts
import { NamePluginType } from './types';

export const getNamePlugin = (): NamePluginType => {
    const generateFirstName = () => 'First';
    const generateLastName = () => 'Last';
    
    return {
        name: 'Name',
        methods: {
            generateFirstName, // Not very useful....
            generateLastName,   // Imagine a huge list of names being chosen at random
            generateFullName: learnCard => 
                `${generateFirstName()} ${generateLastName()}`
        }
    };
};

Plugin Extensions

Another reason not to use the Implicit LearnCard is when you specifically want an old version of a method you are overriding. To demonstrate this, let's build a quick Verification Extension

Types

Building a Verification Extension is super easy with the VerifyExtension type coming from the VC Plugin:

src/types.ts
import { Plugin, VerifyExtension } from '@learncard/core';

export type ExtensionPlugin = Plugin<'Extension', any, VerifyExtension>;
src/index.ts
import { LearnCard, VerifyExtension } from '@learncard/core';
import { ExtensionPlugin } from './types';

export const getExtensionPlugin = (
    learnCard: LearnCard<any, any, VerifyExtension>
): ExtensionPlugin => ({
    name: 'Extension',
    methods: {
        verifyCredential: async (_learnCard, credential) => {
            const verificationCheck = await learnCard.invoke.verifyCredential(credential);
            
            verificationCheck.checks.push('Extension! 😄');
            
            return verificationCheck;
        }
    }
})

See Depending on Plugins for more information about how we are depending on a LearnCard with the verifyCredential method here.

The VerifyExtension type defines one method: verifyCredential that takes in a Verifiable Credential and returns a VerificationCheck object. To add our extension, we depend on a LearnCard that already has the verifyCredential function (using the same VerifyExtension type!), then call the old verifyCredential function at the top of our new verifyCredential function.

This pattern allows any number of plugins to add extra verification logic to the verifyCredential function easily!

Last updated