Many developers ask about how to create components for the Forge, an open source repository of code for the OutSystems platform.
Recently I had to look into the several integrations available for Pushwoosh and noticed there weren't any components available as a "library" for Reactive Web and Mobile applications. So why not use this example and create a walkthrough on building it?
As a side note: In our community, the first thing that comes to mind when Pushwoosh is mentioned is sending notifications to your app. However, Pushwoosh is so much more than that. It is a complete marketing platform for your app.
What Is a Good Component?
Of course there is a complete guide to create components available, and I invite you to take a look at it.
But if I had to put it in one sentence, I would say a good component makes the developer’s life easier. In essence, it should:
- Be agnostic of any application context;
- Be reusable by several applications;
- Be a piece of the infrastructure of your factory without causing any conflicts.
Why a Library?
A Library is a module type that helps you lay the foundations of your application. Libraries have a direct fit in the Foundation Layer of the Architecture Canvas architecture tool and allow you to reduce the number of dependencies in a given environment.
The Library module type was designed with the following principles in mind:
- Libraries are stateless. They do not store information in the database, and they do not have direct access to Entities.
- The elements of a library are only deployed in the context of consumer applications. They are not deployed as standalone applications, reducing the load in the Platform Server's Application Server.
See more details in our documentation here.
Let's get started!
Setting Up the App
There are a few key components to start working with Pushwoosh. We are interested in the Web SDK 3.0. You can check the documentation here.
- The service worker (pushwoosh-service-worker.js)
- The initialization script
- The worker registration script
- The manifest file
All of the above create issues on the platform, either because these files contain dynamic configurations that can only be inserted in static files or, because of the sequence the platform loads files, the configurations kick in out of order.
In the end, this is what I've settled for:
Loading the pushwoosh-web-notifications.js into your scripts area has pros and cons. You could download it from the CDN when the app starts, but including it on your app will save you from some unexpected upgrades, which could break something on your code. The disadvantage is that if some annoying bug is fixed on the SDK, you have to manually reload it.
Place the pushwoosh-service-worker.js under resources on your main app and change the deploy action to Deploy to Target Directory so that the runtime path ends up on the root of your app. This is necessary because the scope of a service worker can be reduced but not increased. If the service worker ends up on a lower folder, its scope is limited to that folder.
If you are using a PWA, please note that browsers do not allow two different Service Workers to be registered at the same time, and a service worker will exist for your PWA. However, you can always import to the original service worker. Check out the documentation "Adding custom service worker".
In the end, your app should contain only the service worker deployed to the target directory.
Building the Library
We start by registering the events to point to the block client actions so that when the event is triggered within the Pushwoosh object, it can be propagated on the block.
This is done in several steps:
- Create an event for the block;
- Create a block client action to trigger the event;
- Reference that action on the event registration.
So from the original documentation, this
and the evtSubscribe looks like this
On the example, the EventRegistration looks like this
Some events pass simple information, for example, the onHideNotificationPermissionDialog
outputs, 'default', 'granted' or 'denied'. We should capture this information and send it on the platform event. For this particular case we can even create a static entity with this information and convert the output to the static identifier.
For the evtPushReceive:
For example, the Pushwoosh.getParams()
can be implemented like this:
I like to have a catch because if something goes wrong inside the promise, it is very difficult to debug due to its async nature.
The complete public client action with the error handler:
The rest of the methods follow the same pattern.
- User Register/Unregister: Allows you to register a user of your app on the pushwoosh platform. You can use something as simple as the user id or any other reference you may like.
- Tags: On the Pushwoosh platform, you can create tags and populate them as you see fit. This will allow you to contextualize the user experience and create statistics from the pushwoosh platform.
- Events: You can also create application events and fire them on your app as needed. The pushwoosh platform will then track these events for each user.
The REST API
Pushwoosh also provides an extensive REST API for your application to interact with the platform.
There are several methods to interact with this API, create, delete, get details, create targeted, get push history, get results, and cancel messages. They all use POST.
The authentication is very straightforward, an auth (API token) and application code parameters are expected on the request body. Some methods only use the auth parameter.
However, some of the JSON on the requests are not OutSystems friendly, for example, on the content section of the createMessage request:
It should be replaced by a list of key/value pairs.
This means we are going to use ardoJSON to tweak these.
ardoJSON is an older library (or, I should say, mature) created primarily for serializing and deserializing JSON into and from OutSystems structures at a time (v 9.x) where the platform didn't do this. However, it is still very valuable since it includes 2 methods, the Objectify and Listify, which handle this use case.
These methods allow you to automatically translate to and from a dynamic JSON object for e.g:
to a key/value pair, e.g.
So on the onAfterRequest of the API, we call the Listify to translate from the key/value pair into the object:
And, on the onBeforeRequest of the API, we call the Objectify to translate the JSON object to a name/value pair:
Finally, we abstract (and eventually simplify) the use of the API for the developer. By creating a specific structure to expose the methods in OutSystems and then translate into the structures used by the API itself.
So, compare between using the direct API with its gazillion parameter to the OutSystems method:
Where Is It?
As usual, there is a component available on the forge, although this is still work in progress and will not work on your PWA app… yet.