Skip to content

Updating 130 Pages of Developer Documentation using HubDB

Late last year, we began a project to improve the security of apps connected to HubSpot. We did this by splitting the single `contacts` scope into more granular scopes, having separate permissions for each CRM object, and separate read and write scopes for those objects.

This presented a challenge for our legacy API documentation. Our new Open API-based documentation allowed us to easily update all of our v3 CRM APIs by regenerating the endpoint documentation, but our legacy API site would have required us to manually update each page. HubDB gave us a way to automate these updates, allowing us to quickly provide accurate documentation for the new granular scopes, without manually editing each separate page.

Editing our legacy documentation

The legacy documentation site uses a single module for the method details box for the endpoint.

HTTP Method Details

The module has settings for the different details of the endpoint, including the HTTP method, content-type, response format, and the scope required for the endpoint. The existing module presented a few challenges for granular scopes, though.

HTTP Method Detailsscreen2

The existing module only supported selecting a single scope. This wasn't an issue with the contacts scope, since it covered many different objects in the HubSpot CRM, but this wasn't going to work for many of the endpoints that supported multiple objects, such as the endpoints for managing associations.

The second issue had to do with making the updates to the pages. The contacts scope was one of the most widely used scopes –there were over 100 pages that needed to be updated. While all of the pages used the same module, there were several different templates in use, which made automating the updates to individual pages difficult.

We needed a solution that would allow us to update a large number of pages while also setting multiple scopes on a single page. We decided to use HubDB to do this. 

HubDB is a feature of the HubSpot CMS that allows you to create tables to store data in rows, columns, and cells–much like a spreadsheet. The data in these tables can be easily integrated into HubSpot pages.

Automating documentation updates

The first step was to update the code for the details module so that the module could use a HubDB row for the scope instead of using the scope selected in the page settings. If the content_id of the page was in the HubDB table, then the module would use the scopes from the HubDB table. If the page was not in the table, however, then it would use the scope set in the page settings. Additionally, the code would need to be able to display multiple scopes from the HubDB table.

To make sure we could test this first, we set it up so that it would only use the HubDB code if a query parameter was included.

{% if request.query_dict.scope_debugging == "true" %} {% set scopes_list = hubdb_table_rows(5347503, "page_id=%s"|format(content_id)) %} {% if scopes_list|length > 0 %} <p class="key"> <a href="/docs/methods/oauth2/initiate-oauth-integration#scopes"> Required Scope </a> :</p> <p class="value"> {% for scope in scopes_list.objects[0].scope_list %} {{ scope.name }} <br> {% endfor %} </p> {% else %} {% if widget.required_scope != '' %} <p class="key">Required Scope:</p> <p class="value"> <a href="/docs/methods/oauth2/initiate-oauth-integration#scopes">{{ widget.required_scope }}</a> </p> {% endif %} {% endif %} {% else %} {% if widget.required_scope != '' %} <p class="key">Required Scope:</p> <p class="value"> <a href="/docs/methods/oauth2/initiate-oauth-integration#scopes">{{ widget.required_scope }}</a> </p> {% endif %} {% endif %}

We then created a HubDB table with two columns. The first column was used as the key for the row, using a number column for the content_id of the page. The second column was a multi-select, used for the scopes for the page. We had already created a spreadsheet containing the page details (including the page ID) and the scopes as we prepared to make the updates to the individual pages, so creating the HubDB table was as easy as uploading this spreadsheet.

HubDB Table - Scope List-1

After testing using the query parameter, we updated the code for the details module to remove the testing query parameter so that all pages would use the HubDB entry–if it exists–while any page that isn't in the HubDB table would use the scope set in the page settings. This way, any pages imported into the HubDB table would automatically update and other pages would continue to use whatever individual scope they were already using.

{% set scopes_list = hubdb_table_rows(5347503, "page_id="~content_id|default("0")) %} {% if scopes_list|length > 0 %} <p class="key"><a href="/docs/methods/oauth2/initiate-oauth-integration#scopes">Required Scope</a>:</p> <p class="value"> {%for scope in scopes_list.objects[0].scope_list %} {{ scope.name }}<br> {% endfor %} </p> {% else %} {% if widget.required_scope != '' %} <p class="key">Required Scope:</p><p class="value"><a href="/docs/methods/oauth2/initiate-oauth-integration#scopes">{{ widget.required_scope }}</a></p> {% endif %} {% endif %}

Better documentation sooner

This solution allowed us to update the documentation for 130 endpoints with a quick HubDB upload. It also paved the way for future updates, as it will allow us to make bulk updates to pages for any future scope updates. It also provides a path to making other changes to the method details by adding another column.

Using the page content_id allows you to map HubDB rows to the content of any page on your site. Next time you need to make uniform changes to a large number of existing pages, consider using HubDB and HubL instead of manually updating each individual page.