Writing Plugins¶
For a working example of a folder plugin, check out Jirafs-Pandoc’s Github Repository.
Setuptools Entrypoint¶
Add a setuptools entrypoint to your plugin’s
setup.py
:entry_points={ 'jirafs_plugins': [ "my_plugin_name = module.path:ClassName" ] }
Write a subclass of
jirafs.plugin.Plugin
implementing one or more methods using the interface described in Folder Plugin API.
Folder Plugin API¶
The following properties must be defined:
MIN_VERSION
: The string version number representing the minimum version of Jirafs that this plugin will work with.MAX_VERSION
: The string version number representing the first version at which your plugin would not be guaranteed to becompatible. Note that this means that your Jirafs version must be below this number, and that users running a version of Jirafs matching this will see an error message. Note: Jirafs uses semantic versioning, so you should set this value to the next major version about the highest version you’ve tested.
Pre/Post Command Methods¶
All commands (including user-installed commands) can have plugins altering
their behavior by defining pre_*COMMAND*
and post_*COMMAND*
methods.
For the below, please replace *COMMAND*
with the command your plugin
would like to alter the behavior of.
pre_*COMMAND*(**kwargs)
:- Executed before handling
*COMMAND*
. Receives (as**kwargs
) all parameters that will be passed-in to the underlying command. - You may alter the parameters that will be passed-in to the underlying
command by returning a new or altered
**kwargs
dictionary. - Return
None
or the original**kwargs
dictionary to pass original arguments to the command without alteration.
- Executed before handling
post_*COMMAND*(returned)
:- Executed after handling
*COMMAND*
. Receives as an argument the result returned by the underlying command.
- Executed after handling
Note
Although the return values of commands are not in the scope of this
specification, many commands return a jirafs.utils.PostStatusResponse
instance.
Such an instance is a named tuple containing two properties:
- (bool)
new
: Whether the command’s action had an effect on the underlying git repository. - (string)
hash
: The hash of the relevant repository branch’s head commit following the action.
Properties¶
The plugin will have the following properties and methods at its disposal:
self.ticketfolder
: An instance ofjirafs.ticketfolder.TicketFolder
representing the jira issue that this plugin is currently operating upon.self.get_configuration()
: Returns a dictionary of configuration settings for this plugin.self.metadata
: Returns a dictionary containing metadata stored for this plugin. This dictionary is modifyable, and will be preserved between plugin executions.
Methods¶
execute_macro(data: str, attrs: Dict, config: Dict) -> str
: REQUIRED Your macro function. It will receive a series of parameters:data
: The content of your macro.attrs
: Any attributes of your macro.config
: Jirafs config parameters.
and is expected to return a string of text that your macro will be replaced when when sending content to Jira.
execute_macro_reversal(data: str) -> str
: If provided, will be expected to perform the reversal ofexecute_macro
above. It will receive as parameters the full text of each field.cleanup()
: Perform any cleanup following macro processing for a ticket folder. If you need more-granular control, you can define methods forcleanup_pre_process()
andcleanup_post_process()
if you need to segment your cleanup process between before and after running macro processing methods.
Macro Plugins¶
Macro plugins are special kinds of folder plugins that are instead subclasses of jirafs.plugin.MacroPlugin
but same setuptools entrypoints apply as are described in Setuptools Entrypoint.
Macros can be executed using either a block element format; for example:
<jirafs:my-macro>
Some content
</jirafs:my-macro>
Note
See Reserved Attributes for more information about attributes and
the special src
attribute.
or as a void element:
<jirafs:my-macro src="some_file_to_read_as_content.ext" />
Note
The trailing slash at the end of your macro is important!
Your execute_macro
method is expected to return text that should be sent
to Jira instead of your macro. Note that the method signature remains
identical to that of a block element macro, but instead of receiving
the content of the block, you will receive None
.
Reserved Attributes¶
src
: All macro plugins can be provided in either a block or void elements. When using a block element version of your macro, you provide content directly within the content of your tag. If you would like the content to be imported from a file instead, you can provide the path to the file to import via thesrc
attribute.
Attributes¶
Both block and void elements can receive any number of attributes; they’re specified following the same conventions you might use for providing an HTML tag with attributes; for example:
<jirafs:flag-image country_code=”US” size=300 alternate=True /> {flag-image:country_code=US|size=300|alternate}
country_code
:US
(string)size
:300.0
(float)alternate
:True
(boolean)
Example Macro Plugin¶
The following plugin isn’t exactly useful, but it does demonstrate the basic functionality of a plugin:
class Plugin(MacroPlugin):
TAG_NAME = 'upper-cased'
def execute_macro(self, data, prefix='', **kwargs):
return prefix + data.upper()
When you enter the following text into a Jira ticket field:
<jirafs:upper-cased prefix="Hello, ">my name is Adam.</jirafs:upper-cased>
the following content will be sent to Jira instead:
Hello, MY NAME IS ADAM.
Warning
Note that it’s always a good idea to make sure your execute_macro
method has a final parameter of **kwargs
! In future versions of
Jirafs, we may add more keyword arguments that will be sent automatically.
Automatically-Reversed Macro Plugins¶
It’s not a ton of fun to have to handle reversing your own macros; so
if your macro’s content will produce unique content for provided input,
you can use the AutomaticReversalMacroPlugin
as your base class
instead of MacroPlugin
. If you do so, your macro will automatically
be reversed when returning content from Jira by scanning the content
received from Jira and replacing any output generated by your macro
during the most recent run with the macro content that generated that
output.
In general, you won’t need to make any special modifications, but there are useful methods for overriding in special circumstances:
should_rerender(data: str, cache_entry: Dict, config: Dict) -> bool
: Control whether this given input content (data
) should be re-rendered. By default,should_rerender
returnsTrue
only ifcache_entry
is empty. Values available in thecache_entry
dictionary include:filenames
: A list of filenames generated by your macro while during processing of this input text.attrs
: Macro attributes set for your macro when running for this input text.replacement
: The replacement text generated by your macro for this input text.is_temp
: Whether or not this macro result was the result of generating content for your current working directory (is_temp==False
), or if it was the result of processing historical content for identifying changes (is_temp==True
).
See Methods for other methods that may be necessary for your macro.
Examples¶
See one of the following repositories for an example of this type of macro:
Image Macros¶
A particularly powerful Macro type is the “Image Macro”. Use of a macro
of this type will allow you to automatically generate and embed images
in your Jira content by passing your macro’s contents through a tool
like Graphviz’ dot
or plantuml
.
In the case of this type of macro, you need to define just one method:
get_extension_and_image_data(data: str, attrs: Dict) -> Tuple[str, bytes]
: For a given input text (data
) and macro attributes (attrs
), return a 2-tuple of the file extension to use for the file to be created, and the bytes of that file.
See Methods for other methods that may be necessary for your macro.
Note
Unlike most subclasses of MacroPlugin
, you should not define
your own execute_macro
method!
Examples¶
See one of the following repositories for an example of this type of macro:
Note
Image Macros are automatically reversed.