diff --git a/docs/SUMMARY.md b/docs/SUMMARY.md index dc66b04..1012052 100644 --- a/docs/SUMMARY.md +++ b/docs/SUMMARY.md @@ -17,7 +17,7 @@ * [Data Layers](notifications/webhook-subscriptions-management/webhook\_subscription\_data\_layers/README.md) * [Entity Framework Layer](notifications/webhook-subscriptions-management/webhook\_subscription\_data\_layers/webhook\_subscription\_ef.md) * [The WebhookNotifier Service](notifications/webhook\_notifier.md) - * [Producing Custom Data Factories](notifications/custom\_datafactory.md) + * [Webhook Factories](notifications/webhook-factories.md) * [Filtering Webhook Subscriptions](notifications/webhook\_subscription\_filters.md) * [Receivers](receivers/README.md) * [Webhook Receivers](receivers/custom\_receiver.md) diff --git a/docs/notifications/webhook-factories.md b/docs/notifications/webhook-factories.md new file mode 100644 index 0000000..515a2ca --- /dev/null +++ b/docs/notifications/webhook-factories.md @@ -0,0 +1,107 @@ +# Webhook Factories + +In notification scenarios triggered by an event, the webhooks to be notified must be built and delivered to subscribers, and this construction process might need to _transform_ the original data carried by those triggering events into a new object. + +In fact, sometimes events don't carry all the information that has to be transmitted to the receivers, for several reasons (eg. _privacy_, _design_, _external context_, etc.), and this requires an additional intervention for the integration of that information (eg. _resolving an entity from the database_, _appending the environment variables of the notifier_, etc.). + +_Note_: This is not a mandatory passage in the notification process, and can be skipped if the data carried by the event represents the information to be transferred to the receivers. + +### Event Information + +The overall contract of an event as recognized by the system is defined by the `EventInfo` structure, that is composed of the following fields: + +
FieldTypeDescription
IdstringThe identifier of the event
SourcestringThe source of the event (eg. github)
SubjectstringThe subject of the event (eg. issue)
TypestringThe type of the event (eg. created)
TimeStampDateTimeOffsetThe exact time the event has occurred in the source system
DataobjectThe payload that contains the actual data of the event
+ +This design respects a general contract of the domain-driven design, informing of the event's nature and the payload that contains the actual data. + +#### Cloud Events + +An example of the implementation of this contract is the [Cloud Events](https://cloudevents.io/) specification, that defines a standard for the representation of events in a cloud-native environment. + +Anyway, despite the adherence to the overall design, the Deveel Webhooks framework is not tied to this specification, but it can be used to implement it, an it requires the EventInfo structure to be provided in order to be able to send notifications. + +### Event Data + +As mentioned before, the event data is not always in the format that is expected by the target system that will be notified, and the resons to implement a further passage of transformation are various: + +* The event data might contain sensitive information, that should not be exposed to the target system +* The event data might contain information that is not relevant to the target system +* The event data might contain information that is not available in the event, but must be retrieved from external sources (eg._database entries_, _environment variables_, etc.) + +### Transforming the EventInfo to a Webhook: the `IWebhookFactory` interface + +The notifier service uses instances of the `IWebhookFactory` interface to transform a triggering event, into a webhook object that is expected by the subscribing applications, using a subscription-scoped transformation logic. + +Transformations are specific to your use cases and they can be specific to a given event condition (eg. _only a specific type of event with a given value in its 'data' component triggers the transformation_). + +To implement this logic in the application, you must first create a new class that inherits from the `IWebhookFactory` contract. + +```csharp +public class MyWebhookFactory : IWebhookFactory { + private readonly IUserResolver resolver; + + public MyWebhookFactory(IUserResolver resolver) { + this.resolver = resolver; + } + + public Task CreateAsync(IWebhookSubscription subscription, EventInfo eventInfo, CancellationToken cancellationToken) { + // Resolve the event data + var userId = eventInfo.Data.GetString("userId"); + var user = await resolver.GetUserAsync(userId, cancellationToken); + + var webhookType = $"user.{eventInfo.Type}"; + + // Transform the event data + return new MyWebhook { + Type = webhookType, + User = new UserInfo { + Id = user.Id, + Email = user.Email, + FirstName = user.FirstName, + LastName = user.LastName + } + }; + } +} +``` + +### Registering the Webhook Factory + +To enable the implementation by your custom Webhook Factory, you can register it during the configuration of the application. + +```csharp +using System; + +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; + +using Deveel.Webhooks; + +namespace Example { + public class Startup { + public Startup(IConfiguration config) { + Configuration = config; + } + + public IConfiguration Configuration { get; } + + public void Configure(IServiceCollection services) { + // ... add any other service you need ... + + // this call adds the basic services for sending of webhooks + services.AddWebhookNotifier(webhooks => { + // This call registers your custom webhook factory as a + // singleton service by default, but other overloads + // allow controlling the lifetime + webhooks.UseWebhookFactory(); + }); + } + } +} +``` + +### The Default WebhookFactory + +When registering the Webhook Notifier service, by default the framework will register a default implementation of the `IWebhookFactory` interface, if the type of `TWebhook` is derived from the `Webhook` class: this factory will attempt to create instances of the webhook type using the default constructor, and then it will try to map the properties of the webhook object to the properties of the event data and the subscription data. + +This approach is useful when the webhook object is a simple POCO that can be easily mapped to the event data, and it doesn't require any further transformation logic, but it's not suitable for more complex scenarios. diff --git a/docs/notifications/webhook_notifier.md b/docs/notifications/webhook_notifier.md index 80974f3..29c13dd 100644 --- a/docs/notifications/webhook_notifier.md +++ b/docs/notifications/webhook_notifier.md @@ -1,6 +1,6 @@ # The WebhookNotifier Service -As described in the [previous chapter](README.md), when registering the Webhook Notifier service, a number of services are registered in the container, that are used to manage the subscriptions and to deliver the webhooks to the receiving end-point. +As described in the [previous chapter](./), when registering the Webhook Notifier service, a number of services are registered in the container, that are used to manage the subscriptions and to deliver the webhooks to the receiving end-point. An default implementation of the `IWebhookNotifier` service is also registered by the framework, which is the `WebhookNotifier` class. @@ -14,12 +14,7 @@ This implementation executes the following steps: ### Service Dependencies -| Service | Description | -|---------|-------------| -| `IWebhookSubscriptionResolver` | Resolves the subscriptions to an event. It is generally provided externally, since it's generally tied to the Webhook Subscription service. Anyway, a default implementation of this service is registered, that is a wrapper around any registered `IWebhookSubscriptionRepository` in the container. | -| `IWebhookFactory` | Transforms the event into a webhook object of the type supported by the service, and that will be then notified. | -| `IWebhookFilterEvaluator` | The service used to evaluate the filters of the subscriptions. When none is provided at the registration, and webhook subscriptions define any filter, the notification to those subscribers will fail. See [this chapter](webhook_subscription_filters.md) for more information | -| `IWebhookDeliveryResultLogger` | When available in the context of the application, logs the results of the delivery of the webhooks | +
ServiceDescription
IWebhookSubscriptionResolver<TWebhook>Resolves the subscriptions to an event. It is generally provided externally, since it's generally tied to the Webhook Subscription service. Anyway, a default implementation of this service is registered, that is a wrapper around any registered IWebhookSubscriptionRepository<TSubscription> in the container.
IWebhookFactory<TWebhook>Transforms the event into a webhook object of the type supported by the service, and that will be then notified.
IWebhookFilterEvaluator<TWebhook>The service used to evaluate the filters of the subscriptions. When none is provided at the registration, and webhook subscriptions define any filter, the notification to those subscribers will fail. See this chapter for more information
IWebhookDeliveryResultLogger<TWebhook>When available in the context of the application, logs the results of the delivery of the webhooks
## Custom Webhook Notifiers @@ -34,4 +29,4 @@ services.AddWebhookNotifier() .UseNotifier(); ``` -All other default services will still be registered, and you can still use them in your custom implementation, by injecting them in the constructor of your class. \ No newline at end of file +All other default services will still be registered, and you can still use them in your custom implementation, by injecting them in the constructor of your class.