• Get Review Board
  • What's New
  • Products
  • Review Board Code review, image review, and document review
  • Documentation
  • Release Notes
  • Power Pack Enterprise integrations, reports, and enhanced document review
  • Try for 60 Days
  • Purchase
  • RBCommons Review Board as a Service, hosted by us
  • Pricing
  • RBTools Command line tools and Python API for Review Board
  • Documentation
  • Release Notes
  • Review Bot Automated code review, connecting tools you already use
  • Documentation
  • Release Notes
  • RB Gateway Manage Git and Mercurial repositories in your network
  • Documentation
  • Release Notes
  • Learn and Explore
  • What is Code Review?
  • Documentation
  • Frequently Asked Questions
  • Support Options
  • Third-Party Integrations
  • Demo
  • Review Board RBTools Power Pack Review Bot Djblets RB Gateway
    1. Djblets 5.x
    2. Version 5.x
    3. Version 4.x
    4. Version 3.x
    5. Version 2.x
    6. Version 2.0
    7. Version 1.0
    8. Version 0.9
    9. Djblets Documentation
    10. Guides
    11. Integration Guides
    12. Writing Integrations
  • Home
  • Guides
  • Avatar Services Guides
  • Writing Avatar Services
  • Extension Guides
  • Writing Extensions
  • Testing Extensions
  • Feature Checks Guides
  • Introduction to Feature Checks
  • Writing Features
  • Writing Feature Checkers
  • Testing with Feature Checks
  • Integration Guides
  • Supporting Integrations
  • Writing Integrations
  • Privacy Compliance Guides
  • Getting and Checking Consent
  • Working with Personally Identifiable Information
  • Service Integrations
  • reCAPTCHA Guides
  • Using reCAPTCHA
  • Registries Guides
  • Writing Registries
  • Web API Guides
  • Writing Web API Resources
  • Adding OAuth2 Support
  • Module and Class References
  • djblets
  • djblets.deprecation
  • djblets.auth.forms
  • djblets.auth.ratelimit
  • djblets.auth.signals
  • djblets.auth.util
  • djblets.auth.views
  • djblets.avatars.errors
  • djblets.avatars.forms
  • djblets.avatars.registry
  • djblets.avatars.services
  • djblets.avatars.services.base
  • djblets.avatars.services.fallback
  • djblets.avatars.services.file_upload
  • djblets.avatars.services.gravatar
  • djblets.avatars.services.url
  • djblets.avatars.settings
  • djblets.cache.backend
  • djblets.cache.backend_compat
  • djblets.cache.context_processors
  • djblets.cache.errors
  • djblets.cache.forwarding_backend
  • djblets.cache.serials
  • djblets.cache.synchronizer
  • djblets.conditions
  • djblets.conditions.choices
  • djblets.conditions.conditions
  • djblets.conditions.errors
  • djblets.conditions.operators
  • djblets.conditions.values
  • djblets.configforms.forms
  • djblets.configforms.mixins
  • djblets.configforms.pages
  • djblets.configforms.registry
  • djblets.configforms.views
  • djblets.datagrid.grids
  • djblets.db.backends.mysql.base
  • djblets.db.fields
  • djblets.db.fields.base64_field
  • djblets.db.fields.comma_separated_values_field
  • djblets.db.fields.counter_field
  • djblets.db.fields.json_field
  • djblets.db.fields.modification_timestamp_field
  • djblets.db.fields.relation_counter_field
  • djblets.db.managers
  • djblets.db.query
  • djblets.db.query_catcher
  • djblets.db.query_comparator
  • djblets.db.validators
  • djblets.extensions.admin
  • djblets.extensions.errors
  • djblets.extensions.extension
  • djblets.extensions.forms
  • djblets.extensions.hooks
  • djblets.extensions.loaders
  • djblets.extensions.manager
  • djblets.extensions.middleware
  • djblets.extensions.models
  • djblets.extensions.packaging
  • djblets.extensions.resources
  • djblets.extensions.settings
  • djblets.extensions.signals
  • djblets.extensions.staticfiles
  • djblets.extensions.testing
  • djblets.extensions.testing.testcases
  • djblets.extensions.urls
  • djblets.extensions.views
  • djblets.extensions.templatetags.djblets_extensions
  • djblets.features
  • djblets.features.checkers
  • djblets.features.decorators
  • djblets.features.errors
  • djblets.features.feature
  • djblets.features.level
  • djblets.features.registry
  • djblets.features.testing
  • djblets.features.templatetags.features
  • djblets.forms.fields
  • djblets.forms.fieldsets
  • djblets.forms.forms
  • djblets.forms.forms.key_value_form
  • djblets.forms.widgets
  • djblets.gravatars
  • djblets.gravatars.templatetags.gravatars
  • djblets.http.middleware
  • djblets.http.responses
  • djblets.integrations.errors
  • djblets.integrations.forms
  • djblets.integrations.hooks
  • djblets.integrations.integration
  • djblets.integrations.manager
  • djblets.integrations.mixins
  • djblets.integrations.models
  • djblets.integrations.urls
  • djblets.integrations.views
  • djblets.log
  • djblets.log.middleware
  • djblets.log.siteconfig
  • djblets.log.urls
  • djblets.log.views
  • djblets.mail.dmarc
  • djblets.mail.message
  • djblets.mail.testing
  • djblets.mail.utils
  • djblets.markdown
  • djblets.markdown.extensions.escape_html
  • djblets.markdown.extensions.wysiwyg
  • djblets.markdown.extensions.wysiwyg_email
  • djblets.pipeline.compilers.es6
  • djblets.pipeline.compilers.less
  • djblets.pipeline.compilers.mixins
  • djblets.pipeline.compilers.rollup
  • djblets.pipeline.compilers.typescript
  • djblets.pipeline.settings
  • djblets.privacy.consent
  • djblets.privacy.consent.base
  • djblets.privacy.consent.common
  • djblets.privacy.consent.errors
  • djblets.privacy.consent.forms
  • djblets.privacy.consent.hooks
  • djblets.privacy.consent.registry
  • djblets.privacy.consent.tracker
  • djblets.privacy.models
  • djblets.privacy.pii
  • djblets.privacy.templatetags.djblets_privacy
  • djblets.recaptcha.mixins
  • djblets.recaptcha.siteconfig
  • djblets.recaptcha.templatetags.djblets_recaptcha
  • djblets.recaptcha.widgets
  • djblets.registries
  • djblets.registries.errors
  • djblets.registries.importer
  • djblets.registries.mixins
  • djblets.registries.registry
  • djblets.registries.signals
  • djblets.secrets
  • djblets.secrets.crypto
  • djblets.secrets.token_generators
  • djblets.secrets.token_generators.base
  • djblets.secrets.token_generators.legacy_sha1
  • djblets.secrets.token_generators.registry
  • djblets.secrets.token_generators.vendor_checksum
  • djblets.siteconfig
  • djblets.siteconfig.admin
  • djblets.siteconfig.context_processors
  • djblets.siteconfig.django_settings
  • djblets.siteconfig.forms
  • djblets.siteconfig.managers
  • djblets.siteconfig.middleware
  • djblets.siteconfig.models
  • djblets.siteconfig.signals
  • djblets.siteconfig.views
  • djblets.template.caches
  • djblets.template.context
  • djblets.template.loaders.conditional_cached
  • djblets.template.loaders.namespaced_app_dirs
  • djblets.testing.decorators
  • djblets.testing.testcases
  • djblets.testing.testrunners
  • djblets.urls.context_processors
  • djblets.urls.decorators
  • djblets.urls.patterns
  • djblets.urls.resolvers
  • djblets.urls.root
  • djblets.urls.staticfiles
  • djblets.util.compat.django.core.cache
  • djblets.util.compat.python.past
  • djblets.util.contextmanagers
  • djblets.util.dates
  • djblets.util.decorators
  • djblets.util.filesystem
  • djblets.util.functional
  • djblets.util.html
  • djblets.util.http
  • djblets.util.humanize
  • djblets.util.json_utils
  • djblets.util.properties
  • djblets.util.serializers
  • djblets.util.symbols
  • djblets.util.templatetags.djblets_deco
  • djblets.util.templatetags.djblets_email
  • djblets.util.templatetags.djblets_forms
  • djblets.util.templatetags.djblets_images
  • djblets.util.templatetags.djblets_js
  • djblets.util.templatetags.djblets_utils
  • djblets.util.typing
  • djblets.util.views
  • djblets.views.generic.base
  • djblets.views.generic.etag
  • djblets.webapi.auth
  • djblets.webapi.auth.backends
  • djblets.webapi.auth.backends.api_tokens
  • djblets.webapi.auth.backends.base
  • djblets.webapi.auth.backends.basic
  • djblets.webapi.auth.backends.oauth2_tokens
  • djblets.webapi.auth.views
  • djblets.webapi.decorators
  • djblets.webapi.encoders
  • djblets.webapi.errors
  • djblets.webapi.fields
  • djblets.webapi.managers
  • djblets.webapi.models
  • djblets.webapi.oauth2_scopes
  • djblets.webapi.resources
  • djblets.webapi.resources.base
  • djblets.webapi.resources.group
  • djblets.webapi.resources.registry
  • djblets.webapi.resources.root
  • djblets.webapi.resources.user
  • djblets.webapi.resources.mixins.api_tokens
  • djblets.webapi.resources.mixins.forms
  • djblets.webapi.resources.mixins.oauth2_tokens
  • djblets.webapi.resources.mixins.queries
  • djblets.webapi.responses
  • djblets.webapi.signals
  • djblets.webapi.testing
  • djblets.webapi.testing.decorators
  • djblets.webapi.testing.testcases
  • General Index
  • Python Module Index
  • Release Notes
  • Writing Integrations¶

    Overview¶

    Integrations are classes that contain some metadata on the integration (name, icons, description) and perform some initialization (usually listening for signals and reacting to them).

    There’s only ever one instance of an integration, but there may be many configurations, which can contain settings specific to that integration. When an integration is ready to communicate with some service, it can loop through these configurations to determine which apply to the event, and can then use those settings in its communication with the service.

    This will be covered in more detail below.

    Writing Your Integration¶

    Your integration will usually be a subclass of Integration. Each will also have a subclass of IntegrationConfigForm for managing the configuration of this integration.

    Note

    Some applications, such as Review Board, have more specific subclasses you should inherit from. For instance, Review Board uses reviewboard.integrations.Integration and reviewboard.integrations.forms.IntegrationConfigForm.

    If you’re writing integrations for a third-party application, check their documentation first.

    These integrations will provide a human-readable name, short description, default settings, a configuration form class, icon URLs, and will perform initialization.

    A typical integration may look like this:

    from django import forms
    from django.contrib.staticfiles.templatetags.staticfiles import static
    from djblets.integrations.forms import IntegrationConfigForm
    from djblets.integrations.integration import Integration
    
    
    class MyIntegrationConfigForm(IntegrationConfigForm):
        endpoint_url = forms.CharField(label='Endpoint URL', required=True)
        client_id = forms.CharField(label='Client ID', required=True)
    
    
    class MyIntegration(Integration):
        name = 'My Integration'
        description = 'This is my special integration that does stuff.'
    
        default_settings = {
            'endpoint_url': 'https://example.com/endpoint/',
            'client_id': 'abc123',
        }
    
        config_form_cls = MyIntegrationConfigForm
    
        @cached_property
        def icon_static_urls(self):
            return {
                '1x': static('images/my-integration/logo.png'),
                '2x': static('images/my-integration/logo@2x.png'),
            }
    
        def initialize(self):
            # Here's where you'll begin listening to events.
            pass
    

    That’s not too bad, right? Let’s break this down into pieces.

    Configuration Form¶

    Your configuration form is what a user will interact with when configuring an integration. This will come with some default fields for giving the configuration a short description and enabling/disabling the integration.

    You will most likely want to add additional fields. This is just a standard Django form, so you can include anything you want here. Any fields you add will automatically save in the configuration’s settings, using the field name and cleaned value from the form.

    Each field should also have a corresponding default value in the default_settings attribute of your integration.

    You will need to define a Meta class containing a fieldsets attribute. This is used to specify which fields will be visible and in what order. You can also include a helpful description for the user, or a title. See the fieldsets documentation for more information on the format.

    class MyIntegrationConfigForm(IntegrationConfigForm):
        endpoint_url = forms.CharField(label='Endpoint URL', required=True)
        client_id = forms.CharField(label='Client ID', required=True)
    
        class Meta:
            fieldsets = (
                (None, {
                    'description': (
                        'Some useful instructions for the integration. This '
                        'is a good place to tell them what info to gather '
                        'from another service.'
                    ),
                    'fields': ('endpoint_url', 'client_id'),
                }),
            )
    

    Your form can perform validation and can normalize any user-provided data through the standard Django Form validation support.

    Integration Metadata¶

    Your integration class must set some data to identify itself. There’s a handful of options available:

    integration_id:

    A unique identifier for this integration. By default, this is generated for you based on the class name and module path.

    If you set it by hand, make sure it contains some uniquely-identifying information, such as your company name and product. That can be useful if you expect your integration’s class name or module path to change at any point.

    name:

    The name of your integration, which will be shown when listing and configuring integrations.

    description:

    A short description of your integration, which will also be shown when listing integrations.

    config_form_cls:

    The configuration form class that you created above. Djblets will take care of showing this form when configuring the integration.

    default_settings:

    A dictionary of default settings you want for a configuration. You should ideally have a default for every setting you’ll be working with, otherwise you’ll have to be careful when looking up data from the configuration.

    icon_static_urls:

    A dictionary of icon resolution indicators to URLs. This allows you to define icons for your integration, and supports high-DPI icons.

    You’ll usually want to use static(), as shown in the example above. However, if your integration is provided by an extension, you’ll instead want to do:

    @cached_property
    def icon_static_urls(self):
        extension = MyExtension.instance
    
        return {
            '1x': extension.get_static_url('images/logo.png'),
            '2x': extension.get_static_url('images/logo@2x.png'),
        }
    

    Handling Initialization¶

    Your integration is most likely going to need to listen to events (such as Django signals), in order to know when to talk to a service. You can do this by making use of a SignalHook, like so:

    from djblets.extensions.hooks import SignalHook
    from djblets.integrations.integration import Integration
    
    
    class MyIntegration(Integration):
        def initialize(self):
            SignalHook(self, my_signal, self._on_my_signal)
    
        def _on_my_signal(self):
            # Handle things here.
            pass
    

    Integrations can actually make use of any built-in extension hooks, and most third-party hooks.

    Querying Configurations¶

    Once you’ve written your initialization code and began listening for events, you’ll probably want to do something with those events, such as crafting a message to another service.

    Since integrations are designed to work with user-specified configurations, you’ll need to look those up and operate based on them. At a minimum, you’re only going to want to work with configurations that are enabled.

    There’s a handy function, get_configs(), that will do the hard work of looking up the configurations. You can use it like this:

    class MyIntegration(Integration):
        def initialize(self):
            SignalHook(self, my_signal, self._on_my_signal)
    
        def _on_my_signal(self):
            for config in self.get_configs():
                # Do something with this configuration.
                pass
    

    In the body of the for loop, you can check the settings for each configuration and determine if you want to work with it. For example, maybe the configuration has a setting stating whether it should send a message to a given IRC channel only if the event has some particular flag set. You can compare the event’s flag to the setting, and skip the configuration if not set.

    Registering Your Integration¶

    Once you have an integration, you’ll need to register it.

    If your integration is built into your codebase, and not provided by an extension, then you’ll first need to know how your application serves up an instance of IntegrationManager, and then you’ll need to call register_integration_class():

    get_integration_manager().register_integration_class(MyIntegration)
    

    If your integration is provided by an extension, then you’ll want to tie its registration to the extension. The application should provide a subclass of BaseIntegrationHook for you, which you can use like this:

    class MyExtension(Extension):
        def initialize(self):
            IntegrationHook(self, MyIntegration)
    

    And that’s it. Your integration should be ready to go, and should show up in the application’s list of integrations! Now you get to do the fun work of actually integrating with whatever service you’re targeting. That will be left as an exercise to the reader.

    Keep up with the latest Review Board releases, security updates, and helpful information.

    About
    News
    Demo
    RBCommons Hosting
    Integrations
    Happy Users
    Support Options
    Documentation
    FAQ
    User Manual
    RBTools
    Administration Guide
    Power Pack
    Release Notes
    Downloads
    Review Board
    RBTools
    Djblets
    Power Pack
    Package Store
    PGP Signatures
    Contributing
    Bug Tracker
    Submit Patches
    Development Setup
    Wiki
    Follow Us
    Mailing Lists
    Reddit
    Twitter
    Mastodon
    Facebook
    YouTube

    Copyright © 2006-2025 Beanbag, Inc. All rights reserved.

    Terms of Service — Privacy Policy — AI Ethics Policy — Branding

    On this page

    • [Top]
    • Overview
    • Writing Your Integration
      • Configuration Form
      • Integration Metadata
      • Handling Initialization
      • Querying Configurations
      • Registering Your Integration