Skip to content

Custom REST Handlers

It is possible to extend the default behaviour of the UCC-generated REST handlers.

For example, if your add-on requires an API key to operate and you want to validate this API key during its creation, you might want to use the custom REST handlers to achieve that.

Note: ucc-gen will not override the REST handler code if you use the correct file name under the bin folder.

See the following example of how it can be done. It contains a REST handler for creating an organization, with the organization_id and organization_api_key fields, which are not custom ones, generated by ucc-gen. But, also in the example, there is a custom handler, CustomRestHandler class which has additional steps for configuration creation and edit operations, specifically handleEdit and handleCreate methods:

import import_declare_test

import logging

from splunktaucclib.rest_handler import admin_external, util
from splunktaucclib.rest_handler.admin_external import AdminExternalHandler
from splunktaucclib.rest_handler.endpoint import (
    RestModel,
    SingleModel,
    field,
    validator,
)

util.remove_http_proxy_env_vars()


fields = [
    field.RestField(
        "organization_id",
        required=True,
        encrypted=False,
        default=None,
        validator=validator.AllOf(
            validator.String(
                max_len=50,
                min_len=1,
            ),
            validator.Pattern(
                regex=r"""^\d+$""",
            ),
        ),
    ),
    field.RestField(
        "organization_api_key",
        required=True,
        encrypted=True,
        default=None,
        validator=validator.AllOf(
            validator.String(
                max_len=50,
                min_len=1,
            ),
            validator.Pattern(
                regex=r"""^[a-z0-9]+$""",
            ),
        ),
    ),
]
model = RestModel(fields, name=None)


endpoint = SingleModel(
    "addon_name_organization", model, config_name="organization"
)


def _validate_organization(organization_id, organization_api_key):
    # Some code to validate the API key.
    # Should return nothing if the configuration is valid.
    # Should raise an exception splunktaucclib.rest_handler.error.RestError if the configuration is not valid.
    ...


class CustomRestHandler(AdminExternalHandler):
    def __init__(self, *args, **kwargs):
        AdminExternalHandler.__init__(self, *args, **kwargs)

    def handleList(self, confInfo):
        AdminExternalHandler.handleList(self, confInfo)

    def handleEdit(self, confInfo):
        _validate_organization(
            self.payload.get("organization_id"),
            self.payload.get("organization_api_key"),
        )
        AdminExternalHandler.handleEdit(self, confInfo)

    def handleCreate(self, confInfo):
        _validate_organization(
            self.payload.get("organization_id"),
            self.payload.get("organization_api_key"),
        )
        AdminExternalHandler.handleCreate(self, confInfo)

    def handleRemove(self, confInfo):
        AdminExternalHandler.handleRemove(self, confInfo)


if __name__ == "__main__":
    logging.getLogger().addHandler(logging.NullHandler())
    admin_external.handle(
        endpoint,
        handler=CustomRestHandler,
    )

Native support from UCC for Modular Inputs

UCC 5.18.0 natively supports custom REST handlers for the modular inputs.

One common scenario is to delete a checkpoint after you delete an input in the Inputs page. Otherwise, users may face strange consequences if they create an input with the same name as the input that was deleted, and this newly created input will reuse the old checkpoint, because the names of the inputs are the same. We would like to avoid this situation in the add-on.

This can be done without a need to modify the REST handler code automatically by running ucc-gen.

See the following automatically generated REST handler code for a modular input REST handler:

import import_declare_test

from splunktaucclib.rest_handler.endpoint import (
    field,
    validator,
    RestModel,
    DataInputModel,
)
from splunktaucclib.rest_handler import admin_external, util
from splunktaucclib.rest_handler.admin_external import AdminExternalHandler
import logging

util.remove_http_proxy_env_vars()


fields = [
    field.RestField(
        'interval',
        required=True,
        encrypted=False,
        default=None,
        validator=validator.Pattern(
            regex=r"""^\-[1-9]\d*$|^\d*$""", 
        )
    ),  

    field.RestField(
        'disabled',
        required=False,
        validator=None
    )

]
model = RestModel(fields, name=None)



endpoint = DataInputModel(
    'example_input_one',
    model,
)


if __name__ == '__main__':
    logging.getLogger().addHandler(logging.NullHandler())
    admin_external.handle(
        endpoint,
        handler=AdminExternalHandler,
    )

New file needs to be created in the bin folder of the add-on. Let’s call it splunk_ta_uccexample_delete_checkpoint_rh.py (name can be different).

And put the following content into the file.

import import_declare_test

from splunktaucclib.rest_handler.admin_external import AdminExternalHandler


class CustomRestHandlerDeleteCheckpoint(AdminExternalHandler):
    def __init__(self, *args, **kwargs):
        AdminExternalHandler.__init__(self, *args, **kwargs)

    def handleList(self, confInfo):
        AdminExternalHandler.handleList(self, confInfo)

    def handleEdit(self, confInfo):
        AdminExternalHandler.handleEdit(self, confInfo)

    def handleCreate(self, confInfo):
        AdminExternalHandler.handleCreate(self, confInfo)

    def handleRemove(self, confInfo):
        # Add your code here to delete the checkpoint!
        AdminExternalHandler.handleRemove(self, confInfo)

Then, in globalConfig file you need to change the behaviour of the UCC to reuse the REST handler that was just created.

{
    "name": "example_input_one",
    "restHandlerModule": "splunk_ta_uccexample_delete_checkpoint_rh",  <----- new field
    "restHandlerClass": "CustomRestHandlerDeleteCheckpoint",  <----- new field
    "entity": [
        "..."
    ],
    "title": "Example Input One"
}

After ucc-gen command is executed again, the generated REST handler for this input will be changed to the following.

import import_declare_test

from splunktaucclib.rest_handler.endpoint import (
    field,
    validator,
    RestModel,
    DataInputModel,
)
from splunktaucclib.rest_handler import admin_external, util
from splunk_ta_uccexample_delete_checkpoint_rh import CustomRestHandlerDeleteCheckpoint  # <----- changed
import logging

util.remove_http_proxy_env_vars()


fields = [
    field.RestField(
        'interval',
        required=True,
        encrypted=False,
        default=None,
        validator=validator.Pattern(
            regex=r"""^\-[1-9]\d*$|^\d*$""", 
        )
    ),  

    field.RestField(
        'disabled',
        required=False,
        validator=None
    )

]
model = RestModel(fields, name=None)



endpoint = DataInputModel(
    'example_input_one',
    model,
)


if __name__ == '__main__':
    logging.getLogger().addHandler(logging.NullHandler())
    admin_external.handle(
        endpoint,
        handler=CustomRestHandlerDeleteCheckpoint,  # <----- changed
    )

Native support from UCC for Configuration Page

UCC 5.41.0 natively supports custom REST handlers for the configuration page

As mentioned at the top, when the API key provided by a user in your add-on and it needs to be validated, you can now directly mention the name of the file that contains the validating code and the class name of it. Hence, your validation script now becomes something as below:

import import_declare_test
from splunktaucclib.rest_handler.admin_external import AdminExternalHandler
# any other imports required for validation

def _validate_organization(organization_id, organization_api_key):
    # Some code to validate the API key.
    # Should return nothing if the configuration is valid.
    # Should raise an exception splunktaucclib.rest_handler.error.RestError if the configuration is not valid.
    ...


class CustomAccountValidator(AdminExternalHandler):
    def __init__(self, *args, **kwargs):
        AdminExternalHandler.__init__(self, *args, **kwargs)

    def handleList(self, confInfo):
        AdminExternalHandler.handleList(self, confInfo)

    def handleEdit(self, confInfo):
        _validate_organization(
            self.payload.get("organization_id"),
            self.payload.get("organization_api_key"),
        )
        AdminExternalHandler.handleEdit(self, confInfo)

    def handleCreate(self, confInfo):
        _validate_organization(
            self.payload.get("organization_id"),
            self.payload.get("organization_api_key"),
        )
        AdminExternalHandler.handleCreate(self, confInfo)

    def handleRemove(self, confInfo):
        AdminExternalHandler.handleRemove(self, confInfo)

Place the above content in a file in the bin folder of the add-on. Let’s call it splunk_ta_uccexample_account_validator_rh.py (name can be different).

Then, in globalConfig file you need to change the behaviour of the UCC to reuse the REST handler that was just created.

{
    "configuration": {
        "tabs": [
            {
                "name": "account",
                "title": "Account",
                "restHandlerModule": "splunk_ta_uccexample_account_validator_rh",  <----- new field
                "restHandlerClass": "CustomAccountValidator",  <----- new field
                "table": {
                    "..." : "..."
                },
                "entity": [
                    "..."
                ]
            }
        ]
    }
}

After ucc-gen command is executed again, the generated REST handler for this configuration page will be updated to the following.

import import_declare_test

from splunktaucclib.rest_handler.endpoint import (
    field,
    validator,
    RestModel,
    SingleModel,
)
from splunktaucclib.rest_handler import admin_external, util
from splunk_ta_uccexample_account_validator_rh import CustomAccountValidator  # <----- changed
import logging

util.remove_http_proxy_env_vars()


fields = [
    # all the fields and its validators mentioned in globalConfig in the configuration page
    # that are auto generated by UCC framework
]
model = RestModel(fields, name=None)


endpoint = SingleModel(
    "addon_name_account", model, config_name="account"
)


if __name__ == '__main__':
    logging.getLogger().addHandler(logging.NullHandler())
    admin_external.handle(
        endpoint,
        handler=CustomAccountValidator,  # <----- changed
    )