asyncapi / saunter

Saunter is a code-first AsyncAPI documentation generator for dotnet.
https://www.asyncapi.com/
MIT License
201 stars 56 forks source link

Add HTTP Bindings #30

Closed m-wild closed 3 years ago

m-wild commented 3 years ago

HTTP is a popular format for async messaging (webooks). We should provide

https://github.com/asyncapi/bindings/tree/master/http

Bindings are currently usable by creating a filter which adds the binding. e.g.

services.AddAsyncApiSchemaGeneration(options =>
{
    options.ChannelItemFilters.Add(new MyAmqpChannelFilter());
}

class MyAmqpChannelFilter : IChannelItemFilter
{
    public void Apply(ChannelItem channelItem, ChannelItemFilterContext context)
    {
        channelItem.Bindings.Amqp = new AmqpChannelBinding
        {
            Is = AmqpChannelBindingIs.RoutingKey,
            Exchange = new AmqpChannelBindingExchange
            {
                Name = "streetlights",
                Type = "topic",
            }
        };
    }
}
HUTCHHUTCHHUTCH commented 3 years ago

I have had a bit of a look at this and would like to have a go at doing this for hacktoberfest.

HUTCHHUTCHHUTCH commented 3 years ago

Sorry I have lots of questions as I am unsure of how I am supposed to test that I have implemented the HTTP bindings currently. I don't know whether I have missed something fundamental in the documentation or some of the stuff just hasn't been implemented yet.

I'm unclear of How you want to use the HTTP bindings? is it like the filters above (there currently isn't a http channel binding in the async API documents)? for example:

in the startup: options.OperationFilters.Add(new HttpOperationFilter());

public class HttpOperationFilter : OperationFilter
    {
        public void Apply(Operation operation, OperationFilterContext context)
        {
            operation.Bindings = new Dictionary<string, OperationBindings>() {
                {
                    "foo", new OperationBindings {
                        Http = new HttpOperationBinding {
                            Type = "foo",
                            Method = "foo",
                            Query = "foo",
                            BindingVersion = "foo"
                            }
                        }
                    }
             };
        }
    }

currently there only seems to be a filter for channel bindings, operations, and documents? do you want the message binding to be used in the same way?

Also for the Message binding, I can't seem to see how you use them in the system? do I need to add in an apply filter like the PublishOperation and Channel attributes in the Document generation.cs?

how do you want the data to be displayed when you call the documentation endpoint (http://localhost:5000/asyncapi/asyncapi.json)? an example is given below?

          "test": {
            "http": {
              "type": "foo",
              "method": "foo",
              "query": "foo",
              "bindingVersion": "foo"
            }
          }
        },
m-wild commented 3 years ago

I don't know whether I have missed something fundamental in the documentation or some of the stuff just hasn't been implemented yet.

Definitely the latter - the library was built first to suit my (rather narrow) needs, and I'm now trying to expand it to be more fully-featured.

You're on the right track, adding bindings is currently only supported via filters. So adding an OperationBinding would be done via an OperationFilter.

If you look through the different *Bindings implementations (MessageBindings, ChannelBindings, OperationBindings) they are currently all different... I think it would be best to bring them in line with the ChannelBindings implementation like so:

public class ChannelItem
{
    ...

    [JsonPropertyName("bindings")]
    public ChannelBindings Bindings { get; set; }
}

public class ChannelBindings
{
    [JsonPropertyName("amqp")]
    public AmqpChannelBinding Amqp { get; set; }
}

For example, OperationBindings should be like:

public class Operation
{
    ...

    // Remove this current implementation
    // [JsonPropertyName("bindings")]
    // public IDictionary<string, OperationBindings> Bindings { get; set; } = new Dictionary<string, OperationBindings>();

    // Add this new implementation
    [JsonPropertyName("bindings")]
    public OperationBindings Bindings { get; set; }
}

public class OperationBindings
{
    [JsonPropertyName("amqp")]
    public AmqpOperationBinding Amqp { get; set; }

    [JsonPropertyName("http")]
    public HttpOperationBinding Http { get; set; }
}

As for the rendered output, it should match the YAML in the example documentation

channels:
  /employees:
    subscribe:
      bindings:
        http:
          type: request
          method: GET
          query:
            type: object
            required:
              - companyId
            properties:
              companyId:
                type: number
                minimum: 1
                description: The Id of the company.
            additionalProperties: false
          bindingVersion: '0.1.0'

But in JSON format like:

{
  "channels": {
    "/employees": {
      "subscribe": {
        "bindings": {
          "http": {
            "type": "request",
            "method": "GET",
            "query": {
              "type": "object",
              "required": [
                "companyId"
              ],
              "properties": {
                "companyId": {
                  "type": "number",
                  "minimum": 1,
                  "description": "The Id of the company."
                }
              },
              "additionalProperties": false
            },
            "bindingVersion": "0.1.0"
          }
        }
      }
    }
  }
}
HUTCHHUTCHHUTCH commented 3 years ago

@tehmantra Thanks for getting back to me, I've had a go and there is a pr in for it, let me know if there are any changes you want made etc.

m-wild commented 3 years ago

Looks good, thanks!

m-wild commented 3 years ago

Fixed in #57