Server Notifications Application Migration from Twilio to CarrierX

If you have read the Migrating from Twilio to CarrierX Quick Start, you could see that both Twilio and CarrierX provide similar means, which allow their users to send voice calls and text messages, and all you need is to change the representation of these instructions in your code.

But when it comes to a real-case migration from Twilio to CarrierX, users might meet some difficulties.

Let’s see such a migration in details and learn how to solve the issues that arise so that your migrated application worked with CarrierX flawlessly.

Getting Application Source Code

We take the Server Notifications application from Twilio as an example. This sample application sends a text message to the server administrator phone number with the details about the server problems. You can download the application source code at GitHub.

Once you download the application, you can see that it has the following structure:

[config]
[twilio_notifications]
[twilio_sample_project]
.coveragerc
.env_example
.gitignore
.travis.yml
LICENSE
README.md
manage.py
requirements.txt

The twilio_notifications folder holds the files we are going to modify. Here is its structure:

[tests]
__init__.py
middleware.py

We need to modify the middleware.py file. All the functions used to send requests for our application are here.

Modifying Files

The middleware.py file contains the following functions and classes:

Let’s see what, where, and how should be modified.

I. load_twilio_config() Function

We modify the load_twilio_config() function like this:

  1. We replace Twilio variables necessary to send requests to the API with CarrierX ones. Instead of using three variables (twilio_account_sid, twilio_auth_token, and twilio_number), we now use only two (carrierx_token and carrierx_phone_number), which values we get from the application environment.

  2. The if condition took the three variables as its arguments, we replace them with the two new.

  3. The return statement also accepted all the three variables. We do not need them now, let’s also replace them with CarrierX ones.

Twilio Python Code

def load_twilio_config():
    twilio_account_sid = os.getenv('TWILIO_ACCOUNT_SID')
    twilio_auth_token = os.getenv('TWILIO_AUTH_TOKEN')
    twilio_number = os.getenv('TWILIO_NUMBER')

    if not all([twilio_account_sid, twilio_auth_token, twilio_number]):
        raise ImproperlyConfigured(NOT_CONFIGURED_MESSAGE)

    return (twilio_number, twilio_account_sid, twilio_auth_token)

Corresponding CarrierX Python Syntax

def load_twilio_config():
    carrierx_token = os.getenv('CARRIERX_TOKEN')
    carrierx_phone_number = os.getenv('CARRIERX_PHONE_NUMBER')

    if not all([carrierx_token, carrierx_phone_number]):
        raise ImproperlyConfigured(NOT_CONFIGURED_MESSAGE)

    return (carrierx_token, carrierx_phone_number)

II. MessageClient Class

The MessageClient class uses Twilio library which forms the request to the API. We are not using it, so we remove the MessageClient class and rearrange this section like this:

  1. The invoking of the load_twilio_config() function in the MessageClient class moves to the send_message() function. We remove the rest of the code for the MessageClient class.

  2. The send_message() function now goes a level higher, accepts two arguments (body and to), and inside it we form the request to the CarrierX Core API with the help of the Send Message method.

Twilio Python Code

class MessageClient:
    def __init__(self):
        (
            twilio_number,
            twilio_account_sid,
            twilio_auth_token,
        ) = load_twilio_config()

        self.twilio_number = twilio_number
        self.twilio_client = Client(twilio_account_sid, twilio_auth_token)

    def send_message(self, body, to):
        self.twilio_client.messages.create(
            body=body,
            to=to,
            from_=self.twilio_number,
        )

Corresponding CarrierX Python Syntax

def send_message(body, to):
    (
        carrierx_token,
        carrierx_phone_number,
    ) = load_twilio_config()

    url = "https://api.carrierx.com/core/v2/sms/messages"
    headers = {'Authorization': f'Bearer {carrierx_token}', 'Content-Type':'application/json'}
    payload = {"from_did": carrierx_phone_number, "to_did": to, "message": body}
    request_details = requests.post(url, data=json.dumps(payload), headers=headers)

For the POST request correct work, we need to import the requests and json Python modules. We add this to the beginning of the middleware.py file:

import requests, json

III. TwilioNotificationsMiddleware Class

We modify the TwilioNotificationsMiddleware class section like this:

  1. Remove the use of the no longer existing MessageClient class.

  2. Edit the send_message() function call according to its changes in the previous section.

Twilio Python Code

class TwilioNotificationsMiddleware:
    def __init__(self, get_response):
        self.administrators = load_admins_file()
        self.client = MessageClient()
        self.get_response = get_response

    def __call__(self, request):
        return self.get_response(request)

    def process_exception(self, request, exception):
        message_to_send = MESSAGE.format(exception)

        for admin in self.administrators:
            self.client.send_message(message_to_send, admin['phone_number'])

        return None

Corresponding CarrierX Python Syntax

class TwilioNotificationsMiddleware:
    def __init__(self, get_response):
        self.administrators = load_admins_file()
        self.get_response = get_response

    def __call__(self, request):
        return self.get_response(request)

    def process_exception(self, request, exception):
        message_to_send = MESSAGE.format(exception)

        for admin in self.administrators:
            send_message(message_to_send, admin['phone_number'])

        return None

IV. Configuration Files

As you could notice, we used the CARRIERX_TOKEN and CARRIERX_PHONE_NUMBER variables in our migrated code. They are stored in the .env file in the application route. Change their values with the ones from CarrierX. These values can be taken in your portal account profile. Refer to the Security Token and Rent Phone Number Quick Starts for more information on where to get the token and the phone number.

Add these variables with their values to the .env file:

export CARRIERX_TOKEN=5ebc03d6-8b2b-44ad-bf65-72d4f1491dda
export CARRIERX_PHONE_NUMBER=15162065515

Do not forget to change the phone number you are going to send the message to in the administrators.json file.

Finishing Migration

Now that we modified the routes, we can safely remove the Twilio libraries import declaration from the beginning of the middleware.py file:

from twilio.rest import Client
import requests, json
import os

from django.conf import settings
from django.core.exceptions import ImproperlyConfigured
from dotenv import load_dotenv

dotenv_path = settings.PROJECT_PATH / '.env'
load_dotenv(dotenv_path=dotenv_path)

MESSAGE = """[This is a test] ALERT! It appears the server is having issues.
Exception: {0}"""

NOT_CONFIGURED_MESSAGE = (
    "Required enviroment variables "
    "CARRIERX_TOKEN or CARRIERX_PHONE_NUMBER missing."
)

def load_admins_file():
    admins_json_path = settings.PROJECT_PATH / 'config' / 'administrators.json'
    return json.loads(admins_json_path.read_text())

def load_twilio_config():
    carrierx_token = os.getenv('CARRIERX_TOKEN')
    carrierx_phone_number = os.getenv('CARRIERX_PHONE_NUMBER')

    if not all([carrierx_token, carrierx_phone_number]):
        raise ImproperlyConfigured(NOT_CONFIGURED_MESSAGE)

    return (carrierx_token, carrierx_phone_number)

def send_message(body, to):
    (
        carrierx_token,
        carrierx_phone_number,
    ) = load_twilio_config()

    url = "https://api.carrierx.com/core/v2/sms/messages"
    headers = {'Authorization': f'Bearer {carrierx_token}', 'Content-Type':'application/json'}
    payload = {"from_did": carrierx_phone_number, "to_did": to, "message": body}
    request_details = requests.post(url, data=json.dumps(payload), headers=headers)

class TwilioNotificationsMiddleware:
    def __init__(self, get_response):
        self.administrators = load_admins_file()
        self.get_response = get_response

    def __call__(self, request):
        return self.get_response(request)

    def process_exception(self, request, exception):
        message_to_send = MESSAGE.format(exception)

        for admin in self.administrators:
            send_message(message_to_send, admin['phone_number'])

        return None

You can also remove the importing of Twilio modules from the requirements.txt file.

Follow the instructions from the application GitHub page to run the application. Then open the application error page in your browser and the application will send a server error message to the phone number you specified in the administrators.json file.

Further Reading

You have successfully migrated the Server Notifications application from Twilio to CarrierX!

Refer to the following page to learn more about CarrierX API:

Use our Migrating from Twilio to CarrierX Quick Start to learn more about other difficulties you can meet while migrating from Twilio to CarrierX and the ways to solve these issues.

Read other instructions on real-case migrations from Twilio to CarrierX here:

Refer to our other quick starts for instructions on how to work with CarrierX: