Modules

From SocialStack
Revision as of 13:00, 5 July 2022 by 2a02:c7f:22be:a500:498:3225:1a16:a72d (talk) (Created page with "Modules in SocialStack are the main way to create small, portable, easy to test units of code. The best way to create a new one is via the generate command, but y...")
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)

Modules in SocialStack are the main way to create small, portable, easy to test units of code. The best way to create a new one is via the generate command, but you can also install public ones. Importantly a module should be self-contained as much as possible with the smallest dependency footprint.

Making C# code modular

As C# is a statically typed language, it is inherently more rigid than languages like Javascript on the frontend. However, partial classes are very useful for this end goal of making extensible units of code. For this reason, every class outputted by the generate command is partial.

Why are partial classes useful? Let's take the User type for example. Modules want to extend this one all the time - a 2 factor authentication plugin may want to store some data in the user objects, modules for adding names and addresses and so on. Because the installed User class is partial, you can add fields to it without editing the original file. By adding fields to the User type, any User reference you end up with - either via the API, admin panel, events etc - will then have that field on it. Because the database storage mechanism is also generic, it will get automatically stored too. Therefore, adding a field to the User type is as simple as adding a file like this:

namespace Api.Users;

public partial class User{
    public string MyCustomField;
}

Secondly, we also have a very fast and flexible events system inside the API. When a user object is created, an event is triggered (specifically that is Events.User.BeforeCreate and AfterCreate), which then any module can subscribe to. Just like with the content type itself, common events are fully automatic but you can also create your own.

Event listeners should be added once at startup. This means you'll either put them in service constructors, or in special EventListener classes which are constructed as a singleton for you.

[EventListener]
public class MyEventListener {
    
    public MyEventListener(){

        Events.User.BeforeCreate.AddEventListener(async (Context context, User user) => {
            if(user == null){
                return null;
            }
            
            // can await something here if needed!
            // Because we're in a beforeCreate, we can edit the object including populating custom fields or validating them.
            // If we have a validation failure, throw a PublicException.
            // throw new PublicException("Incorrect date of birth", "dob/invalid");
            return user;
        });
    }
}