Package: phantom-action-handler
phantom-action-handlerUtilities for simplifying the development of Phantom appsAuthorDavid Finn:dfinn@splunk.comRequirementsSplunk>PhantomPython 3.6 or higherInstallationThis library should generally be specified as a Phantom app pip dependency, and
therefore not require manual installation.If manual installation on the Phantom instance is desirable:phenvpip3installphantom-action-handlerDescriptionThephantom_dev.action_handlermodule greatly simplifies the implementation
of aphantom.base_connector.BaseConnectorsubclass, which is the basis for
Phantom app development.Edge cases andQuickstartGiven the following app JSON describing a hypotheticalecho messageaction:{..."pip_dependencies":{"pypi":[...{"module":"phantom-action-handler"},...],...},..."actions":[{"action":"echo message","identifier":"echo_message","description":"Return the input message","verbose":"","type":"test","read_only":true,"parameters":{"message":{"description":"The message to be echoed","data_type":"string","contains":["text"],"required":true,"primary":true},},"output":[{"data_path":"action_result.data.*","data_type":"string","contains":["text"]},],"versions":"EQ(*)"},],...}The correspondingphantom.base_connector.BaseConnectorcould be implemented
as follows:fromphantom.base_connectorimportBaseConnectorfromphantom_dev.action_handlerimport(ActionHandler,HandlerMixin,main_connector)@main_connectorclassMyConnector(HandlerMixin,BaseConnector):@ActionHandlerdefecho_message(self,message,context=None):yieldmessageAll methods of theBaseConnectorclass are available for use in the action
handler logic implementation.DetailsIn the above example, use of theActionHandlerdecorator wraps the decoratedecho_messagemethod in the logic required for error handling and results
reporting.
Theparamdictionary is automatically unpacked as keyword arguments to
handler method, allowing for quick and explicit argument validation and
intuitive access to action parameters.paramcontains acontextentry
(except for thetest connectivityaction) and the parameters described in the
app JSON.Handler methods such asecho_messageare expected to return iterables of
results data.
The items from this iterable are added as data objects to theActionResult.
Implementing handler methods as generators is highly convenient, as this allows
custom logic to be run any time before or after data is yielded, but methods
can also be implemented as normal functions that return iterable objects.TheHandlerMixinsuperclass automatically delegates incoming actions to the
correct method based on the action identifier.Themain_connectorclass decorator simply calls the class'smainmethod if
the class is defined in the__main__module, reproducing the testing
functionality provided by autogenerated app wizard code.SummariesTo add summary information to the result, theActionHandler.summarydecorator can be used:...@main_connectorclassMyConnector(HandlerMixin,BaseConnector):@ActionHandlerdefecho_message(self,message,context=None):yieldmessage@echo_message.summarydefsummarise_echo_message(self,results):message,=resultsreturn{'message':message}This will insert the result of the summary method as the action result summary
object.Signaling FailureFailure is signaled through raising exceptions.
If the handler executes without raising an exception, the action is treated as
a success.To implement anecho failaction that does the same thing asecho message,
but always fails after producing results (assuming the correct entries are
added to the app JSON):...@main_connectorclassMyConnector(HandlerMixin,BaseConnector):@ActionHandlerdefecho_message(self,message,context=None):yieldmessage@ActionHandlerdefecho_fail(self,**param):# Demonstration of re-packing param; this will be the same as the# original param dictionary, which we can then unpack for the call# to echo_messageyield fromself.echo_message(**param)raiseRuntimeError('Failed on purpose')# The same summary method can be decorated multiple times for different# handlers to duplicate functionality@echo_fail.summary@echo_message.summarydefsummarise_echo_message(self,results):message,=resultsreturn{'message':message}Actions with no results...{"action":"test connectivity","identifier":"test_connectivity","description":"Validate the asset configuration for connectivity using supplied configuration","verbose":"","type":"test","read_only":true,"parameters":{},"output":[],"versions":"EQ(*)"},...test connectivityis an example of an action which produces no results.
The handler method needs only to return an empty iterable, which is easily
accomplished by returning an empty collection rather than implementing a
generator:...@main_connectorclassMyConnector(HandlerMixin,BaseConnector):@ActionHandlerdefecho_message(self,message,context=None):yieldmessage@ActionHandlerdefecho_fail(self,**param):# Demonstration of re-packing param; this will be the same as the# original param dictionary, which we can then unpack for the call# to echo_messageyield fromself.echo_message(**param)raiseRuntimeError('Failed on purpose')# The same summary method can be decorated multiple times for different# handlers to duplicate functionality@echo_fail.summary@echo_message.summarydefsummarise_echo_message(self,results):message,=resultsreturn{'message':message}@ActionHandlerdeftest_connectivity(self):# The test connectivity action is a special case that does not# receive a param dictionary at all, so there are no arguments to# unpack (not even context)test_value='SOME TEST MESSAGE'results=[]try:forresultinself.echo_fail(test_value):results.append(result)exceptRuntimeError:passelse:raiseRuntimeError('echo fail failed to fail')message,=resultsifmessage!=test_value:raiseValueError('echo fail failed to echo')return[]It would also be possible to achieve this with areturnstatement before ayieldstatement in a generator, or by failing before any results are yielded.