Contextual Material

In March of this year, I was working on a Workflow in n8n, and needed to search a Mattermost team for available channels. When I tried to use the existing Mattermost for that n8n supports, I discovered the function that covers the Channel Search API method was missing.

I was able to cobble together a HTTP Request node by duplicating my Mattermost credentials as HTTP credentials and tailoring the request against the Mattermost API's requirements as a work around. But when I was done, I felt contributing to n8n's Mattermost node would be a rewarding entry point into developing in the ecosystem.

I could not find an engineering or business argument against adding to the Mattermost node, or, against filling-out a Node's capabilities on n8n's public channels. There were a few places I checked for expression of these opinions from n8n members:

Picture: When in Rome, plug and chug? Source: craiyon.com

I was concerned that the risk appetite of n8n - The Business may prevent them from more fully filling out a node's capabilities against a given service's API. What satisfied my concerns were a professionally ran and friendly community in the Pull Requests that happily accepted contributions to nodes. So, I set off ready to cut some code.

I did not give myself much time to work on this, and intended to finish it quickly. I am mostly a PHP developer, and have not stayed fully up-to-date with many of the developments in the JavaScript/NodeJS/ECMA ecosystem. However, I knew colloquially from listening to my peers that the way many of these frameworks work is by defining modules in specific file paths in the project, and filling in specific files with the right interface. The "Plug and chug" analogy comes to mind, but this time we are chugging code. This is advantageous for someone trying to work fast and format the work according to to the maintainer's conventions and expectations.

I begin to observe the code relevant to the Mattermost module.

I used the trusty ack-grep and discovered where Mattermost was mentioned in the code.

Output from <code>ack -i mattermost</code> in the root of the n8n project directory Picture: Output from ack -i mattermost in the root of the n8n project directory

This lead me to the directory packages/nodes-base/nodes/Mattermost. After reading a lot of code in there, I also found packages/nodes-base/nodes/Mattermost/v1/actions/Interfaces.ts which mentioned the actions and capabilities I had found in the existing node as MattermostMap. Digging further into the original directory, I found the packages/nodes-base/nodes/Mattermost/v1/actions/channel directory.

I based the Search capability on the example that the Create capability set. Create is responsible for creating Channels in the Mattermost Node, and both of these methods in the Mattermost API make POST HTTP requests to the target Mattermost environment.

Narrowing all this down, we are left with the following files to consider:

  • packages/nodes-base/nodes/Mattermost/v1/actions/channel/create/description.ts
  • packages/nodes-base/nodes/Mattermost/v1/actions/channel/create/execute.ts
  • packages/nodes-base/nodes/Mattermost/v1/actions/channel/create/index.ts

And I copied them into a new directory: packages/nodes-base/nodes/Mattermost/v1/actions/channel/search I appended the ​​channel key in the MattermostMap variable Interfaces.ts with the search capability.

packages/nodes-base/nodes/Mattermost/v1/actions/channel/search/description.ts is the ChannelProperties data structure that defines the User-Interface form-field options for a particular capability. We can find the Search API requirements from the Mattermost API documentation.

The Search API method has two required parameters. A team ID in the URL part of the request, and a search term in the request body. Fortunately, the Create capability also requires the Team ID. All that is needed to clean up is the references to ’create’ to ’search’.

The search term is a required free-form text field. The closest example Create has is the “Name” option. References to ’create’ are changed to ’search’.

There are probably other more appropriate options for other parameters like displayName, placeholder, default, and description. I tried to keep the choices I made as generic as possible, but I do not have a good reference for why I made the choices I made for these parameters. In retrospect, I would opt to copy the text directly from the Mattermost API documentation.

All of this work takes me to the first commit in the pull request, aeb0cf1. This commit includes some mistakes, including referential errors to components that are within the Create capability’s scope. These mistakes are part of the risk we assume when we profile and stage components the way we did.

Picture: Robot detective will investigate your code Source: craiyon.com

The way we mitigate against these kinds of mistakes is by testing. I had difficulty running n8n’s tests initially. I was blocked on testing early in my investigation of the environment.

I believed I had some problems installing a dependency the dev environment required. However, I was actually blocked on installing a dependency the environment required during build time. These are the kinds of risks we assume when we work in new environments and make too many assumptions on the current environment from prior experience in other environments.

I committed the work and transferred it to a different development environment. But, I soon felt I was working a little too late for a Sunday, and decided to punt until I could try again. Hoping I would still be interested. I posted the PR with the branch’s intention, and a status message of where I had left off in trying to validate the work met the intention.

The next day I was still thinking about my experience, and I had been searching the web with follow-up questions. I stubbed my toes on n8n’s excellent official documentation pretty quickly, and had answers to build and test n8n’s environment the way the maintainer’s intended.

Having a more “tactile” experience inspecting the n8n UI led to the realization that there were more missing boilerplate components. These components include the newly written data structures and definitions for Search into the scope of the components under the Mattermost Channel API resources. Components like packages/nodes-base/nodes/Mattermost/v1/actions/channel/index.ts feel very much like a Controller in the MVC paradigm. Given TypeScript’s objectives with object orientation, this may be true.

I submitted my changes to the PR, and included a note of what I had done and the principle reason I thought I had pushed my work into acceptability range. After all, it was working on my machine.

Several weeks later the n8n team was able to pick up my branch. They brought it up-to-date with the main n8n branch, and extended the functionality to include API result pagination.

It was released in version 0.169.0.

Questions? code@mashio.net