Plugins
Plugins provide a way to extend Pathom behavior. This page explains how Pathom plugins work and how you can write your extensions.
The wrapping model of plugins
Pathom extensions work by wrapping
code points in Pathom. This means you can code
around the extended points in Pathom.
Each extension point has an identifier keyword.
Let's take for example the :com.wsscode.pathom3.format.eql/wrap-map-select-entry
extension. This adds a hook to the result masking operation, which occurs at the
end of p.eql/process
to filter out the results according to the query.
note
It's a Pathom convention to prefix all plugin wrapping attributes with wrap-
.
This hook, in specific, can control how the value is placed at the resulting map.
Let's use this hook to make an extension that protects certain attributes from getting in the output map:
important
The data from those attributes may still leak in the meta for stats, to make it really secure you need to prevent regular users from seeing the stats.
Now let's talk in more detail about the things we used in this example.
Define the wrapper fn
A more generic way to think about wrapper extensions is with the following structure:
Define the plugin
Then, a plugin consists of one or more of those wrapping functions. Any map can be a
plugin, to make it a plugin, you must add the key ::p.plugin/id
with a symbol to
identify this plugin. Then each other key is an extension wrapper.
The latter form is interesting when you are generating a plugin programmatically.
For example, we could make our plugin to protect attributes more configurable by using making a function that returns the plugin:
Adding plugins
To use a plugin, you must add it to the environment using the p.plugin/register
function.
This function works similar to pci/register
and also accepts vectors
of plugins.
In the case of plugins, there is also p.plugin/register-before
and
p.plugin/register-after
. Which gives you more control over the plugin execution order.
In the next section, we will talk more about plugin order.
Plugin order
To understand the order in which plugins execute, it helps to keep the following image in your head:
Here is the same description as code:
Plugins on top will enter first and leave last.
tip
You can view the plugin order in the attribute ::p.plugin/plugin-order
attribute in
your environment.
A plugin can halt the execution of the posterior plugins (and the original operation itself). This happens when the plugin returns some data without calling the original function given on the wrapper.
For example, if plugin-b
halts execution (like our protected attributes plugin does)
the stack stops there and goes back up. The following image illustrates this case:
Consider this when deciding the order of plugins.
important
When you use p.plugin/register
, Pathom creates specific lists for each plugin type. This way, it only has to iterate over plugins of that extension type.
Extension points
Here you can see all the extension points available in Pathom.
Runner Extensions
Extensions available for the runner process of Pathom at com.wsscode.pathom3.connect.runner
namespace.
::pcr/wrap-resolve
Wrap the call of a resolver. Note in the case of a batch resolver, input is going to be a sequence.
::pcr/wrap-mutate
Wrap the call of a mutation.
::p.error/wrap-attribute-error
Wrap the error data, allowing you to modify it.
::pcr/wrap-resolver-error
Wrap the operation that attaches an error to a node.
::pcr/wrap-batch-resolver-error
Wrap batch errors
::pcr/wrap-mutation-error
Use this to get notified when mutation errors happen.
::pcr/wrap-merge-attribute
Wrap the operation of merging new data in the entity. This is an important point. You
can control how sub-processes occur here. The source original is an assoc
like operation.
::pcr/wrap-entity-ready!
This runs only once per entity, if you want to do any kind of post-process that you expect one entity to be ready.
::pcr/wrap-run-graph!
Wrap the operation of running a graph. Note for a given transaction, there may be many graphs to run. Each entity (each map in the result) is potentially an entity and may have its own graph.
Also note this may run multiple times for the same entity, for each "batch hit" it does.
::pcr/wrap-root-run-graph!
Like the previous, but only runs at the root graph.
EQL Output
::pf.eql/wrap-map-select-entry
This is the one we used for the initial demo. Controls how outputs get masked out in the post-processing of the EQL output.
::p.eql/wrap-process-ast
This wraps a call to process-ast
in the EQL interface.
When the query is triggered using process
, you also get the query as ::pcr/root-query
in the environment.
If you don't see this value it means process-ast
was called directly. To get the query
in this case use eql/ast->query
to generate the query from the AST.