Giter Site home page Giter Site logo

jezzsantos / servicestack.webhooks Goto Github PK

View Code? Open in Web Editor NEW
27.0 8.0 7.0 4.46 MB

Add Webhooks to your ServiceStack services

License: Apache License 2.0

PowerShell 0.23% C# 99.77%
webhook servicestack servicestack-services architecture restful-api servicestack-plugin event-sink raise-events webhook-events servicestack-webhooks

servicestack.webhooks's Introduction

License Build status

NuGet NuGet

Add Webhooks to your ServiceStack services

Release Notes

Overview

This project makes it very easy to expose webhook notifications from your ServiceStack services, and helps you manage your user's subscriptions to those webhooks.

By adding the WebhookFeature to the AppHost of your service, you automatically get all the pieces you need to raise and manage the events raised by your services.

We know that most services are built for scalability and to be hosted in the cloud, so we know that you are going to want to use your own components and technologies that fit in with your own architecture. All you have to do is plug into the WebhookFeature.

For example: In one service, you may want to store the Webhook subscriptions in a MongoDB database, and have an Azure worker role relay the events to subscribers from a (reliable) cloud queue.

In another service, you may want to store subscriptions in Ormlite SQL database, and relay events to subscribers directly from within the same service on a background thread, or throw the event to an AWS lambda to process. Whatever works for you, the choice is yours.

Oh, don't worry, getting started is easy. We got your back with a built-in subscription store and built-in event sink that will get you going seeing how it all works. But eventually you'll want to swap those out for your own pieces that fit your architecture, which is dead easy.

If you cant find the component you want for your architecture (see Plugins), it should be easy for you to build add your own and just plug it in.

Install from NuGet:

Install-Package ServiceStack.Webhooks

Simply add the WebhookFeature in your AppHost.Configure() method:

public override void Configure(Container container)
{
    // Add ValidationFeature and AuthFeature plugins first

    Plugins.Add(new WebhookFeature());
}

See Getting Started for more details.

Raising Events

To raise events from your own services:

  1. Add the IWebhooks dependency to your service
  2. Call: IWebhooks.Publish<TDto>(string eventName, TDto data)

As simple as this:

internal class HelloService : Service
{
    public IWebhooks Webhooks { get; set; }

    public HelloResponse Any(Hello request)
    {
        Webhooks.Publish("hello", new HelloEvent{ Text = "I said hello" });
    }
}

Subscribing to Events

Subscribers to events raised by your services need to create a webhook subscription to those events.

They do this by POSTing something like the following, to your service:

POST /webhooks/subscriptions
{
    "name": "My Webhook",
    "events": ["hello", "goodbye"],
    "config": {
        "url": "http://myserver/api/incoming",
    }
}

Consuming Events

To consume events, a subscriber needs to provide a public HTTP POST endpoint on the internet that would receive the POSTed webhook event.

The URL to that endpoint is defined in the config.url of the subscription (above).

In the case of the "hello" event (raised above), the POSTed event sent to the subscriber's endpoint might look something like this:

POST http://myserver/hello HTTP/1.1
Accept: application/json
User-Agent: ServiceStack .NET Client 4.56
Accept-Encoding: gzip,deflate
X-Webhook-Delivery: 7a6224aad9c8400fb0a70b8a71262400
X-Webhook-Event: hello
Content-Type: application/json
Host: myserver
Content-Length: 26
Expect: 100-continue
Proxy-Connection: Keep-Alive

{
    "Text": "I said hello"
}

To consume this event with a ServiceStack service, the subscriber would standup a public API like the one below, that could receive the 'Hello' event. That might have been raised from another service with a call to Webhooks.Publish("hello", new HelloEvent{ Text = "I said hello" }):

internal class MyService : Service
{
    public void Post(HelloDto request)
    {
        // They said hello!
        var message = request.Text;

       
        // The event name, messaging metadata are included in the headers
        var eventName = Request.Headers["X-Webhook-Event"];
        var deliveryId = Request.Headers["X-Webhook-Delivery"];
        var signature = Request.Headers["X-Hub-Signature"];
    }
}

[Route("/hello", "POST")]
public class HelloDto
{
    public string Text { get; set; }
}

Note: Webhook events can be delivered securely to subscribers using signatures, that proves the authenticity of the sender only. Delivered events are never encrypted, and only signed. See Subscriber Security for more details.

More documentation about how the WebhookFeature works, and how to customize it are available in here

Contribute?

Want to get involved in this project? or want to help improve this capability for your services? just send us a message or pull-request!

servicestack.webhooks's People

Contributors

georgehemmings avatar jezzsantos avatar mythz avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

servicestack.webhooks's Issues

Retry Policy

It would be nice if retry policies could be specified. Currently it's up to each provider to come up with it's own way of handling retries. There are some scenarios that could be handled better if the plugin handled retries rather than the provider.

For example if an endpoint cannot be reached it would be sensible to not attempt to send any other events until the first event succeeds or fails completely (after all available retries are exhausted).

Moving the retry policy into the plugin would allow reuse of common webhook logic. For example try every minute for 10 minutes then every hour for 10 hours. The retries would also be visible via the subscription history. This may help the receiver debug their server app.

I've not thought too much about how this could be implemented generically yet. I'm opening this issue to start the discussion.

Event Delivery History

Story: "As a subscriber I want to see a transaction history of my subscription.
(i.e. What notification succeeded and what failed, when, and what StatusCode and StatusDescription from my service was returned.)"

If we provided a SubscriptionService.UpdateHistory() API, then relays could call that API and deliver the results of notifying subscribers with events. The history could be attached to the SubscriptionService.GetSubscription() API.

I think the update would need to come in batches of subscribers for a single event.

Then subscribers will have a history of all events delivered and their success, much like we see on github. https://developer.github.com/webhooks/testing/#listing-recent-deliveries

HMAC Authenticity Not Supported

We have not yet completed the implementation of HMAC authenticity for subscriptions that pass a secret key.

We need the HMAC signature to be calculated over the whole request, and updated in the request headers.
in: https://github.com/jezzsantos/ServiceStack.Webhooks/blob/master/src/Webhooks.Relays/Clients/EventServiceClient.cs#L135

If no support for creating the signature in ServiceStack, then we could look to bouncycastle or some other library for doing that for us.

ServiceStack.Webhooks.Signed

Need a (additional) signed version of the nuget packages:

  • ServiceStack.Webhooks
  • ServiceStack.Webhooks.Interfaces
  • ServiceStack.Webhooks.OrmLite

i.e.:

  • ServiceStack.Webhooks.Signed
  • ServiceStack.Webhooks.Interfaces.Signed
  • ServiceStack.Webhooks.OrmLite.Signed

using a *.snk file in the solution

AppHostWebhookEventSink runs on same thread as IWebhooks.Publish()

Because AppHostWebhookEventSink runs on the same thread as IWebhooks.Publish() all events raised by my service operations are going to be relayed to all subscribers before my service operation can process the incoming request. This means that my service operation could either:

  • timeout (i.e. waiting for subscribers endpoints to respond) or
  • timeout waiting to retry (default 3 times) sending to subscriber endpoints, or
  • at the very least slow down my service endpoint waiting until all subscribers are notified.

It is a perf killer!

RedisSubscriptionStore?

Now that we have the CacheClientSubscriptionStore we can simply create a Redis version of it, for storing subscriptions, and offer that to people as an alternative store.

We could create a 'ServiceStack.Webhooks.Redis' nuget from a 'Webhooks.Redis' project in the solution ,and ship that nuget separately.

Filter Events for user / role

Hey @jezzsantos

Have you got a plan for handling the filtering of events? For example in GitHub if you are subscribed to PullRequestEvent you would only want to receive these for the repos you are interested in.

ServiceStack Latest v5.9 giving issue with ServiceStack.Webhooks v3.0.1

I am trying to use Webhook plugin with ServiceStack v5.9 but with latest ServiceStack.Webhooks v3.0.1 giving me issue, please refer error below:

{ "responseStatus": { "errorCode": "MissingMethodException", "message": "Method not found: 'Int32 ServiceStack.OrmLite.OrmLiteWriteExpressionsApi.Delete(System.Data.IDbConnection, System.Linq.Expressions.Expression1<System.Func2<!!0,Boolean>>)'.", "stackTrace": "[DeleteSubscription: 6/15/2020 12:42:28 PM]:\n[REQUEST: {id:c42c28e2-f432-40d1-b370-49a5a8506035}]\r\nSystem.MissingMethodException: Method not found: 'Int32 ServiceStack.OrmLite.OrmLiteWriteExpressionsApi.Delete(System.Data.IDbConnection, System.Linq.Expressions.Expression1<System.Func2<!!0,Boolean>>)'.\r\n at ServiceStack.Webhooks.OrmLite.OrmLiteSubscriptionStore.Delete(String subscriptionId)\r\n at ServiceStack.Webhooks.ServiceInterface.SubscriptionService.Delete(DeleteSubscription request)\r\n at lambda_method(Closure , Object , Object )\r\n at ServiceStack.Host.ServiceRunner1.d__15.MoveNext() in C:\BuildAgent\work\3481147c480f4a2f\src\ServiceStack\Host\ServiceRunner.cs:line 133\r\n",
"errors": []
}
}`

Upon contacting ServiceStack it is advice to raise issue with you to provide new version that targets the latest v5.9.

Reference: https://stackoverflow.com/questions/62388956/servicestack-webhook-servicestack-webhooks-ormlite-subscription-store-plugin-i

HmacAuthProvider.Secret should be easier to lookup

Right now, HmacAuthProvider.Secret is a simple property.

There will be cases where this needs to be read from configuration using IAppSettings for a global value.

There may be other cases where the developer will need to fetch a different secret for different events, and so a delegate can be used to lookup the secret from any resource based upon the eventname.

Recommend, we define a HmacAuthProvider.ctor() overload that reads a global value of 'hmac.Secret' from configuration.

Recommend we replace the simple property getter with a delegate that passes in the IRequest, and eventName of the event to be verified. That way the developer can use Resolve to get any service they might need (including IAppSettings) should they need to get different secrets for different events

Content-Type (Other than application/json) Not Supported

We currently don't support relaying events to subscribers with any other Content-Type than application/json. You will get a 400 is you subscribe with any other Content-Type. See SubscriptionConfigValidator

Possible other types we could/should support would be at least: application/x-www-form-urlencoded as well as application/json

That's because right now we are using a JsonServiceClient (wrapped inside the ServiceClient class) to do our relay of events. That would have to change. For example, using the HttpClient instead.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.