Defining events
Triggers are an important part of any workflow orchestrator. Blackbird allows custom events to be defined as triggers. These events usually correspond to webhooks in applications but they can also be callback URLs or they work using polling.
Webhooks
Just like with actions, we use the WebhookList
attribute to point Blackbirds towards our webhooks. You can split your webhooks into multiple files by providing multiple classes implementing the WebhookList
. The Webhook
attribute has to be added to each webhook handling method. Similarly to actions, they also take a name and description.
Some external systems can request additional data, for example, credentials, URL to send the event payload, etc., before sending the event payload. Therefore, we allow you to control the response to the system and if a flight needs to be started with specific credentials.
The Blackbird platform transfers all request parameters to the WebhookRequest
object. This includes the HTTP method, additional headers, etc. You can use this to decide how to handle this request.
In the WebhookResponse
class that you return, you can signal to Blackbird if this incoming request should trigger a bird or not. If you don’t want to trigger a bird set ReceivedWebhookRequestType
to WebhookRequestType.Preflight
.
You can also control what message will be send back to the calling service by providing a HttpResponseMessage
. If null
is provided then Blackbird sends a 204 no content
response by default.
All the properties passed to the Result
class implementation will be available in the bird editor. All Display
attributes are possible here as well.
💡 Note: The name of your webhook method cannot be changed, Blackbird would interpret it as a deleted and newly created event.
Automatic subscription and unsubscription
To define automatic subscription and unsubscription to webhooks you can implement an instance of IWebhookEventHandler
and attach it to the webhook as the second argument (see the example above typeof(ArticlePublishedHandler)
).
The webhook event handler has two methods: SubscribeAsync
and UnsubscribeAsync
. There are triggered when a bird is published and unpublished/deleted respectively. Both should implement the API calls that create/delete the webhooks.
An example implementation from the Zendesk app is shown below:
💡 Tip: you can use the Bird ID from the invocation context to generate unique keys for each subscription if required.
Handling checkpoint edge cases
Events can be created at the top of the bird to act as the trigger. However, they can also be used in the middle of a bird as a checkpoint. A common scenario for a checkpoint would be to wait for a status to be changed to X. Therein lies a problem: what if the status was already changed to X before the subscription to the webhook was created?
In order to deal with this edge case we also allow you to implement IAfterSubscriptionWebhookEventHandler<T>
on a Webhook handler class. This interface wants you to implement the OnWebhookSubscribedAsync
method. This method is called the moment the subscription is made. You can use this method to already trigger the first event immediatly. In the case of checkpoints, if the event is called the webhook will unsubscribe afterwards, therefore resolving the edge case.
Here is the implementation of this interface taken from the Phrase TMS app:
💡 Note: this event will only be triggered on subscription if the EXACT conditions are met: a specific project ID was provided and the status of that project is exactly that of the provided status.
Additional webhook inputs
You can use the [WebhookParameter]
attribute to add (optional) input values to your webhook event. F.e. if you want to allow your user to specify their event more narrowly.
These input parameters may or may not be used in the actual subscription method. This can happen if your endpoint or the body of the subscription request takes certain extra parameters for the input. If an input parameter is actually used to create the subscription we recommend that you use [WebhookParameter(true)]
. This optional boolean value tells Blackbird that the description depends on this input. If this input value is now changed, the bird will automatically resubscribe.
Callbacks
Blackbird can also handle applications that work with callbacks instead of webhooks. Typically these URLs have to be manually configured. Implementation of this is quite simple and it works exactly the same as webhooks. The only difference is that if you don’t define a IWebhookEventHandler
in the webhook attribute, Blackbird will consider it a callback. In this case Blackbird will provide the user with a URL in the UI when they publish their bird.
The following method has no webhook handler (compare this to the webhooks defined above!)
This translates to:
Tip: one can use the callback functionality to create “callable” birds as if Blackbird had its own API.
💡 Note: If you create different birds with the same event and the same connection, then all of these birds will have the same URL. Blackbird has the assumption that it is still the same event that is being triggered and this allows us to optimize internally.
💡 Note: If you suspend a bird, or if you change the event and republish the bird, the URL will change and would have to be reconfigured where the URL is applied.
Because callbacks require quite a bit of developer skill to use, we recommend that you use polling instead of callbacks whenever you’re developing apps that are intended for a broad audience.
Polling
Besides webhooks and callbacks, Blackbird’s core can also take care of different polling scenarios. Instead of implementing a WebhookList
with Webhook
attributed methods, you can implement a PollingEventList
with PollingEvent
attributed methods.
A polling event always takes a PollingEventRequest<T>
as its first parameter (where T
is a memory implementation). It can be followed with any number of PollingEventParameter
attributed arguments that work similar to actions and webhooks. The return type of this method should always be of PollingEventResponse<T, U>
where T
is the memory implementation and U
is the response that will be send as the output of the event in the bird.
A possible implementation of PollingEventResponse<T, U>
can look like this:
When a polling event is ‘activated’ (either by publishing a bird or when a flight arrives at a polling checkpoints) the polling event method will be called. If the returned object indicates that the event should be triggered FlyBird
should be set to true
. The Result
will be the values that are passed to the bird.
While active, the polling event will be periodically called. This period is configurable by the user in the bird editor.
The memory implementation can be any class the user desires. This way, you can store any data into memory by setting it in the return value. When the polling event is called again, the memory can be retrieved from the PollingEventRequest<T>
. With this mechanism, the developer can choose to implement e.g. a timestamp to indicate when the last poll was in order to filter a query for new items, store existing items in an array and compare it to new items, or store a certain property in a field and compare it to a current property to see if this property has changed.
A full example implementation:
💡 Note: you can know if the polling event is on its first call if the memory is null.
💡 Note: polling events are always called immediatly when activated. You can use this fact to create a baseline memory for future comparison, or to immediatly trigger a flight (or checkpoint continuance) if all conditions are already met.