Building Mattermost integration with Laravel. Part 1 Setting up an environment.
Most of the popular PHP frameworks are built and used with a concept of ‘fullstack’ in mind. However, they provide us with strong foundations for pure backend, pure OOP development in first place. In the following series of articles I’d like to discuss a way to create Laravel ‘Platform-as-a-Service’ application which will operate between two external APIs by implementing full power of modern PHP framework without any frontend components.
Disclaimer. Please keep in mind that this is not a tutorial or step-by-step guide, but rather an opinionated series of solutions and implementations.
Both of these services have a RESTful API with extensive documentation, which makes it perfect for development purposes.
The goal is simple: provide employees with an ability to manage their projects, tasks and time trackers without leaving a corporate chat application.
A working integration, therefore, must have following functionality:
- Transfer data from one service to another
- Manage (start, stop, pause, resume, delete) time trackers
- Manage authorization and user permissions
In order to keep this discussion comprehensible, the article is divided into three parts:
- Part 1 — Setting up an environment
- Part 2 — Building services
- Part 3 — Integrating separate parts
By the end of first part we should have application skeleton with full development environment suitable for both Mattermost and Redmine APIs.
Second part will take us through Laravel services, service providers and middleware, allowing integration to connect to each external API and manage resources.
Finally, the last part will integrate all created components to cover application’s lifecycle.
In essence, building an integration requires designing a storage, wrapping APIs into application services and connecting these services inside main application. Naturally, it will involve creating a middleware, service providers, utilizing external libraries and so on, but overall path is not very sophisticated.
Before we dive into application’s environment settings, let’s create a draft of integration lifecycle.
- Mattermost instance makes a request to integration app
- Integration app receives request and translates it to Redmine API call
- Redmine instance responses to integration app
- Integration app delivers Redmine response to Mattermost
We can gather all the information required for our environment using this simple approach. In order to make/receive external API calls, we will need authorization credentials. These credentials should be stored somewhere on integration server. It also goes without saying that we will need a database for the application.
A note on Mattermost
Mattermost gives us different mechanisms to develop and run integrations: from paid apps which can modify user interface to simple slash commands. Due to its simplicity, versatility, and ease of implementation, the latter choice is ideal in this situation. Moreover, slash commands might be more attractive for a team of developers because of its CLI-like syntax. So the first lifecycle event will be something like this:
/mySlashCommand do something useful .
A note on Redmine
Although it would be completely fine to build a service for Redmine from scratch, there is a recommended PHP library for this. The library provides ready-to-use API calls wrappers, exception handlers and connectors, so it’s definitely makes sense to use it.
We’ll start with preparing new Laravel project. Since integration is pure backend application, we won’t need most of the pre-configured Laravel Sail services — only main application and database.
Before proceeding to database configurations and migrations, we should modify a default
.env.example file. Both Mattermost and Redmine will have global API tokens, instances URLs and administrator credentials. In this scenario we are creating a self-hosted PaaS application, so there will be no need to store this values in database. Instead, a
.env file will be used.
For now, let’s leave these variables empty (detailed usage is part of a next article). If you’ve just thought that keeping such credentials in
.env file is the most insecure thing you’d ever saw in your life, please refer to Encrypting Environment Files section of Laravel official documentation.
Naturally, we won’t use environment variables directly in code. It’s insecure and inconvenient when it comes to service containers and dependency injection. This is why the last step of setting up an environment is to register these values in application configuration files. To achieve this, append newly created
config/services.php with following arrays.
Since most of the time integration will manage external resources, we don’t need to create complex database structure. Default Laravel migrations will cover most of our needs (namely user management). The only thing we don’t have by this point is time trackers storage, because they are part of integration functionality and not present in Mattermost or Redmine. There’re quite a lot of ways to design a schema for managing such a resource, so I’ll provide just one of many possible solutions.
Notice that unlike standard database design there are no foreign keys here, although there are fields that represent another entities (user and issue). Both of them are part of external resources, so there’s no way to create a constrained fields. We will, however, implement such constrains later in code rather than in database.
Overall logic is quite simple in this solution. Timestamp is saved on Tracker start, any update (such as pause, resume or stop) will save its own timestamp. Total time is then calculated as difference between start-end interval and paused time interval. ‘Finished’ field acts as a soft-delete implementation, making tracker closed to further updates.
At this point all we have left is to run migrations.
In the first part of this publication we prepared a full environment for future development of Mattermost-Redmine integration. With services configuration it is now possible to create service providers and services themselves and to inject these services into application controllers. Additionally, we created a foundation for implementing our own functionality with time tracking due to a database migration.
Thanks for reading! The next part is published here.