Skip to main content

Utilities to simplify Phantom app development

Project description

phantom-action-handler

Utilities for simplifying the development of Phantom apps

Author

David Finn: dfinn@splunk.com

Requirements

  • Splunk>Phantom
  • Python 3.6 or higher

Installation

This 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:

phenv pip3 install phantom-action-handler

Description

The phantom_dev.action_handler module greatly simplifies the implementation of a phantom.base_connector.BaseConnector subclass, which is the basis for Phantom app development.

Edge cases and

Quickstart

Given the following app JSON describing a hypothetical echo message action:

{
	...
	"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 corresponding phantom.base_connector.BaseConnector could be implemented as follows:

from phantom.base_connector import BaseConnector
from phantom_dev.action_handler import (
	ActionHandler, HandlerMixin, main_connector)


@main_connector
class MyConnector(HandlerMixin, BaseConnector):
	@ActionHandler
	def echo_message(self, message, context=None):
		yield message

All methods of the BaseConnector class are available for use in the action handler logic implementation.

Details

In the above example, use of the ActionHandler decorator wraps the decorated echo_message method in the logic required for error handling and results reporting. The param dictionary is automatically unpacked as keyword arguments to handler method, allowing for quick and explicit argument validation and intuitive access to action parameters. param contains a context entry (except for the test connectivity action) and the parameters described in the app JSON.

Handler methods such as echo_message are expected to return iterables of results data. The items from this iterable are added as data objects to the ActionResult. 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.

The HandlerMixin superclass automatically delegates incoming actions to the correct method based on the action identifier.

The main_connector class decorator simply calls the class's main method if the class is defined in the __main__ module, reproducing the testing functionality provided by autogenerated app wizard code.

Summaries

To add summary information to the result, the ActionHandler.summary decorator can be used:

...
@main_connector
class MyConnector(HandlerMixin, BaseConnector):
	@ActionHandler
	def echo_message(self, message, context=None):
		yield message

	@echo_message.summary
	def summarise_echo_message(self, results):
		message, = results
		return {'message': message}

This will insert the result of the summary method as the action result summary object.

Signaling Failure

Failure is signaled through raising exceptions. If the handler executes without raising an exception, the action is treated as a success.

To implement an echo fail action that does the same thing as echo message, but always fails after producing results (assuming the correct entries are added to the app JSON):

...
@main_connector
class MyConnector(HandlerMixin, BaseConnector):
	@ActionHandler
	def echo_message(self, message, context=None):
		yield message

	@ActionHandler
	def echo_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_message
		yield from self.echo_message(**param)
		raise RuntimeError('Failed on purpose')

	# The same summary method can be decorated multiple times for different
	# handlers to duplicate functionality
	@echo_fail.summary
	@echo_message.summary
	def summarise_echo_message(self, results):
		message, = results
		return {'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 connectivity is 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_connector
class MyConnector(HandlerMixin, BaseConnector):
	@ActionHandler
	def echo_message(self, message, context=None):
		yield message

	@ActionHandler
	def echo_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_message
		yield from self.echo_message(**param)
		raise RuntimeError('Failed on purpose')

	# The same summary method can be decorated multiple times for different
	# handlers to duplicate functionality
	@echo_fail.summary
	@echo_message.summary
	def summarise_echo_message(self, results):
		message, = results
		return {'message': message}

	@ActionHandler
	def test_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:
			for result in self.echo_fail(test_value):
				results.append(result)
		except RuntimeError:
			pass
		else:
			raise RuntimeError('echo fail failed to fail')

		message, = results
		if message != test_value:
			raise ValueError('echo fail failed to echo')

		return []

It would also be possible to achieve this with a return statement before a yield statement in a generator, or by failing before any results are yielded.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

phantom-action-handler-0.0.1a9.tar.gz (6.6 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

phantom_action_handler-0.0.1a9-py3-none-any.whl (12.0 kB view details)

Uploaded Python 3

File details

Details for the file phantom-action-handler-0.0.1a9.tar.gz.

File metadata

  • Download URL: phantom-action-handler-0.0.1a9.tar.gz
  • Upload date:
  • Size: 6.6 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/3.2.0 pkginfo/1.6.0 requests/2.24.0 setuptools/44.0.0 requests-toolbelt/0.9.1 tqdm/4.50.2 CPython/3.8.5

File hashes

Hashes for phantom-action-handler-0.0.1a9.tar.gz
Algorithm Hash digest
SHA256 d8dd931f12844cbf96b06b7b95460f72e4dd30032a9e76d446fbedd1e2390711
MD5 8e87631ddb6b9f01f3be7fe23c4b4b2e
BLAKE2b-256 6f9cf6d3212b57460ffd26c7392e7d07c5f31e5f32c75d28addb87b89f69f325

See more details on using hashes here.

File details

Details for the file phantom_action_handler-0.0.1a9-py3-none-any.whl.

File metadata

  • Download URL: phantom_action_handler-0.0.1a9-py3-none-any.whl
  • Upload date:
  • Size: 12.0 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/3.2.0 pkginfo/1.6.0 requests/2.24.0 setuptools/44.0.0 requests-toolbelt/0.9.1 tqdm/4.50.2 CPython/3.8.5

File hashes

Hashes for phantom_action_handler-0.0.1a9-py3-none-any.whl
Algorithm Hash digest
SHA256 2aef04c580abd75991c841e8f2e35bea5c91b470bccfb9d54df970c3d6999df1
MD5 872d1363416e0d2e443463816708785f
BLAKE2b-256 241b9ca6a3042c27a23a354dc67260eb54ae2eb3a6b704606256eca1dceb23f5

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page