Custom Extension for Azure Functions — Part 2 — Bindings

  1. Define a class that extends from Attribute.
  2. Create a class that extends the IAsyncCollector interface. This interface defines methods to AddAsync and FlushAsync. The system will call the AddAsync function to send data to external resources.
  3. Create a class that implements the IConverter interface. This interface has one method:
    Convert:- The system calls this method to create the AsyncCollector class.
  4. Create a class that implements the interface IExtensionConfigProvider. Similar to Triggers, the system will call the Initialize method. In this method, we bind the attribute class using the AddBindingRule method and bind to the Collector using the AddToCollector method.
  1. The system calls the Configure method passing the IWebJobsBuilder object. We add the extension using the AddExtension method using the class that implements the IExtensionConfigProvider interface.
  2. The system calls the Initialise method of the IExtensionConfigProvider passing ExtensionConfigContext as a parameter. Our implementation of the Initialize method adds the add the binding rule using the AddBindingRule method of the ExtensionConfigContext, which returns a BindingRule object. We call the BindToCollector to add our binding, passing the Converter as a parameter.
  3. The system calls the Convert method of the ICoverter. Our implementation creates an instance of AsyncCollector class and return.
  4. The systems call AddAsync function to send data to external services.
Microsoft.Azure.WebJobs.Extensions 
MyNatsClient
namespace WebJobs.Extension.Nats
{
/// <summary>
/// <c>Attribute</c> class for Trigger
/// </summary>
[AttributeUsage(AttributeTargets.Parameter)]
[Binding]
public class NatsAttribute: Attribute
{
// <summary>
// Connection string in the form of nats://<username>:<password>@<host>:<port>
// </summary>
public string Connection { get; set; }
// Channel string
public string Channel { get; set; }

// <siummary>
// Helper method to get connection string from environment variables
// </summary>
internal string GetConnectionString()
{
return Environment.GetEnvironmentVariable(Connection);
}
}
}
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Azure.WebJobs;

namespace WebJobs.Extension.Nats.Bindings
{
/// <summary>
/// Async Collector class. Responsible for publishing to a NATS channel
/// </summary>
/// <typeparam name="T">Data Type of value</typeparam>
public class NatsAsyncCollector<T>: IAsyncCollector<T>
{
/// <summary>
/// NatsBindingContext instance
/// </summary>
private readonly NatsBindingContext _context;

/// <summary>
/// Constructor
/// </summary>
/// <param name="context">NatsBindingContext instance</param>
public NatsAsyncCollector(NatsBindingContext context)
{
_context = context;
}

/// <summary>
/// Publish message to a NATS chanel
/// </summary>
/// <param name="message">Message to be published</param>
/// <param name="cancellationToken">A Cancellation Token</param>
/// <returns>A Task that completes when the message us published</returns>
public Task AddAsync(T message, CancellationToken cancellationToken = default)
{
return _context.client.Publish(_context.attribute.Channel, message.ToString());
}

/// <summary>
/// Flush any pending publish
/// </summary>
/// <param name="cancellationToken">A Cancellation token/param>
/// <returns></returns>
public Task FlushAsync(CancellationToken cancellationToken = default)
{
return Task.CompletedTask;
}
}
}

Create a sample to test the NATS Binding

using Microsoft.AspNetCore.Http;
using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Extensions.Http;
using Microsoft.Extensions.Logging;
using WebJobs.Extension.Nats;

namespace Bindings.Sample
{
public static class NatsBindingsSample
{
[FunctionName("NatsBindingsSample")]
public static void Run(
[HttpTrigger(AuthorizationLevel.Anonymous, "get", "post", Route = null)] HttpRequest req,
[Nats(Connection = "NatsConnection", Channel = "SampleChannelOut")] out string message,
ILogger log)
{
string msg = req.Query["message"];

log.LogInformation("C# HTTP trigger function processed a request.");
log.LogInformation($"Received message {msg}");

message = msg;
}
}
}
#!/usr/bin/env node
/* jslint node: true */
'use strict';

var nats = require('nats').connect("nats://<username>:<password>@localhost:4222");

nats.on('error', function(e) {
console.log('Error [' + nats.options.url + ']: ' + e);
process.exit();
});

nats.on('close', function() {
console.log('CLOSED');
process.exit();
});

var subject = "SampleChannelOut1"

if (!subject) {
console.log('Usage: node-sub <subject>');
process.exit();
}

console.log('Listening on [' + subject + ']');

nats.subscribe(subject, function(msg) {
console.log('Received "' + msg + '"');
});

--

--

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store
Krishnaraj Varma

Krishnaraj Varma

64 Followers

A Software Architect from Kerala, India, Open Source, Cloud Native enthusiast. Likes Golang, Rust, C/C++, Kubernetes, Kafka, etc.