Magento2 - Newsletter API

Magento2 - Newsletter API

December 28, 2020

Introduction #

In this post - I try to share some nice tips about Postman and how it can be used for TDD of Magento API extension.
By default, native Magento Newsletter module doesn’t provide any way to manage or get newsletter subscribers. Luckily, it’s not that big problem, because we can create such endpoints on our own.

Magento2 is super friendly when it comes to create new endpoints and most of the job is already done - what we need to do is just to provide logic for them.

In this post I focus on desiging API and testing it, not the implementation itself.

New API design #

Before actual implementation, it’s good idea to think how our API should look like, what should it return. The easiest way, is just to prepare etc/webapi.xml and define all desired routes there. In this extension we want to have option to:

  • fetch list of subscribers, with some criterias,
  • be able to subscribe new e-mail address,
  • be able to confirm this subscription as well,
  • finally: be able to unsubscribe e-mail address.

We could also add endpoint to delete address totally, but let’s leave it for later.

Based on above points, our webapi.xml could look like this:

<?xml version="1.0" ?>
<routes xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Webapi:etc/webapi.xsd">
	<route method="POST" url="/V1/subscriber">
		<service class="Albedo\NewsletterApi\Api\SubscriberManagementInterface" method="postSubscriber"/>
		<resources>
			<resource ref="Albedo_NewsletterApi::write"/>
		</resources>
	</route>
	<route method="POST" url="/V1/subscriber/:id/confirm">
		<service class="Albedo\NewsletterApi\Api\SubscriberManagementInterface" method="postConfirm"/>
		<resources>
			<resource ref="Albedo_NewsletterApi::write"/>
		</resources>
	</route>
	<route method="POST" url="/V1/subscriber/:id/unsubscribe">
		<service class="Albedo\NewsletterApi\Api\SubscriberManagementInterface" method="postUnsubscribe"/>
		<resources>
			<resource ref="Albedo_NewsletterApi::write"/>
		</resources>
	</route>
	<route method="GET" url="/V1/subscribers">
		<service class="Albedo\NewsletterApi\Api\SubscriberManagementInterface" method="getList"/>
		<resources>
			<resource ref="Albedo_NewsletterApi::read"/>
		</resources>
	</route>
</routes>
Please note, there are two ACL roles used, for write and read permissions.

Test Driven Development - with Postman! #

Now, when we have basic overview how our API should work and what we really want to implement - it’s good to have some tests prepared - which we would run our code against!

For writing tests for API calls I use Postman - this wonderful software is really useful for that. I really suggest to get to know Postman better and use all of its powerful features.

Prepare collection and requests #

Start off with preparing new collection and 4 requests. Be descriptive - use meaningful names and be verbose when writing descriptions. Think about people who will try to understand it - make their life easier by putting as much important information as possible. Prepare environment in Postman for test usage - do not ever hardcode sensitive information into requests! After 4 requests are added, they should look like this:

Test

◀️ It’s important to keep everything organized in collections and folders.

Let’s focus on enpoint responsible for subscribing new e-mail address, as it is first step in the process. Let’s use Postman to create such test: When request for subscribing e-mail address is invoked, with e-mail address passed in request body, we should get subscriber’s data in response (and it should be the same as the one submitted in request body). Click on Tests in Postman and paste this very basic test:

pm.test("Status code is 200", function () {
    pm.response.to.have.status(200);
});

This test will assert if response code is 200.

When we run it, we will get nice red error, that test failed - we got 404 instead of 200 (well - it’s not implemented yet, so no surprises!). Let’s create more test cases for this request - in response - we want to see subscriber’s status and email, so let’s add this:

pm.test("Response contains subscribers email", function () {
   const responseJson = pm.response.json();
    pm.expect(responseJson.subscriber_email).to.eql(pm.environment.get("email"));;
});
Syntax is quite self-explanatory, for more go here.

This test checks if response contains subscriber’s email and it’s the same e-mail as provided in request.

For e-mail, it’s better to create environment variable and use it both in test and in request body.

This is request’s body:

{
    "subscriber": {
        "subscriber_email" : "{{email}}"
    }
}

Let’s keep these two tests. What we need to do now is implementation of this endpoint, so both of them can turn green. I am not going to cover how implementation of this endpoint is done - I think it’s quite straight forward and there are many articles on how to do it, you can always check the code and investigate for yourself - when in doubt you can contact me. What I want to focus on is testing.

So when all interfaces, models and logic is implemented, you hit Send button in Postman to see if your tests turn green - but there is a better way!

Collection runner #

Our new endpoints are strictly connected as they are parts of the process - subscribe -> confirm -> unsubscribe. With this, we can create a testsuite, that will make sure that this process is working as a whole.

First, let’s create tests for each request - where we define - what is expected result of each request. Obviously, each request is expected to return 200 by default, so we can easily copy and paste this testcase snippet to each request. For subscribe request, we want to make sure that we receive an email address in response, and it’s the same as the one we had sent before.

Make sure to operate on environment variables instead of hardocded values - this way tests will for everyone! Important - if want to test whole process - including confirmation of new subscriber, we need to save confirmation_code from subscribe request so we can use it in next tested request. Finally, our test script for this request can look like this:

pm.test("Status code is 200", function () {
    pm.response.to.have.status(200);
});

pm.test("Response contains subscribers email", function () {
    const responseJson = pm.response.json();
    pm.expect(responseJson.subscriber_email).to.eql(pm.environment.get("email"));

    pm.environment.set("confirmation_code", responseJson.subscriber_confirm_code);
    pm.environment.set("subscriber_id", responseJson.subscriber_id);
});

For second request (confirm) - we want to again test if we get e-mail back. Also - confirmation code will change now, so we need to save it again for next request:

pm.test("Status code is 200", function () {
    pm.response.to.have.status(200);

});

pm.test("Response contains subscribers email", function () {
   const responseJson = pm.response.json();
   pm.expect(responseJson.subscriber_email).to.eql(pm.environment.get("email"));;

   pm.environment.set("confirmation_code", responseJson.subscriber_confirm_code);
});

For last request (unsubscribe) - we want to check if status is set to 3 - this is ID that Magento uses for unsubscribed users.

pm.test("Status code is 200", function () {
    pm.response.to.have.status(200);
});

pm.test("Response contains subscribers email", function () {
   const responseJson = pm.response.json();
   pm.expect(responseJson.subscriber_email).to.eql(pm.environment.get("email"));;
});

pm.test("Unsubscribed email has status unsubscribed", function () {
    const responseJson = pm.response.json();
    pm.expect(responseJson.subscriber_status).to.eql(3);
});

When we have tests for all requests written, we can launch it in Postman’s Collection Runner. Select collection, order requests, select environment and… let the magic happen:

Collection Runner

All green! You can set iterations number to 10, to see if repeated tests will work just fine and see this nice summary.

Tests summary

Running tests in CLI #

When we have testsuite created and working nicely in Postman app, we can think about more versatile way of running these tests.

Postman team introduced newman - ~command line Collection Runner for Postman~ - this is amazing utility that allows you to include API testing into your CI pipelines! Installation and usage is super easy, just go with a classic:

$ npm install -g newman

Export your Postman collection and just run:

newman run magento-newsletter-endpoint.json

This will fail, because in exported collection we don’t have hardcoded token or host - we keep them as environment variables. Now, we need to export environment from Postman and save it as, for example - testsystem.json. Now, we need to tell newman to use this environment for running tests:

newman run magento-newsletter-endpoint.json -e testsystem.json

It will execute each request from your collection and will execute tests. After all - you will get detailed report. Nice!

Newman results

Documentation #

Remember, when I wrote to be as descriptive as possible when creating requests? To put as many information as possible - here’s why. Postman can create nice-looking documentation pages for your API, including examples for any programming language. It is also possible to share it online with just one click.

You can find API reference here:

What’s really cool is that when you add or change something - it will be synced with your online documentation. You can also define examples for each request - and I strongly recommend to do so, because everything will get easier to understand. Premium plan will give option to use your own domain for your documentation.

What’s more? #

These are main features of Postman which I use frequently. When creating API endpoints, I prefer to test them with Postman on the go - this gives me more insight on how it really performs. However, Postman offers even more features:

  • Setup monitoring for API - you can setup monitoring in Postman, which will periodically run collection’s requests to make sure it’s working properly. You can receive alerts when something goes wrong. There is also a web dashboard when you can see results of your monitoring. Monitoring
  • Setup mock server for API - you can setup mock server which will serve same responses as examples defined for each request. This makes it easier to test, when you have no access to real endpoints (for example, when it’s not implemented yet)
  • Share collection for everyone - for example my collection can be seen here - so whoever needs to play with the API I can share this URL and everything will get imported into his Postman, making it much easier to play around with API. It can also be shared within private teams, but you need premium plan for that.

Conclusion #

In this post I wanted to share some experience with working with Postman. Postman is this kind of software, that works straight away and fullfils most of your needs. There is no need to go deeper with it, because everything just works - and it needs some time to investigate and check all available features to discover some nice and useful features that come handy when developing API-related features. I hope you liked it and I managed to encourage you to get familiar with less known Postman features.

Resources #

🍺 If you liked this article you might consider buying me a beer? ;)