Skip to main content

Posts tagged with '.net'

This is a repost that originally appeared on the Couchbase Blog: Geospatial Search with ASP.NET Core, Aurelia, and Google Maps.

Geospatial search is now fully supported in Couchbase Server 5.5. Check out the Couchbase Server 5.5 announcement, and download the developer build for free right now.

In this post, I’m going to demonstrate the geospatial search capabilities of Couchbase Full Text Search by creating a web-based UI that performs searches. Whenever I think of geospatial searches, I think about Yelp, which is great at helping me find restaurants in a specific area.

So I’m going to have a little fun and create a very bare-bones version of Yelp, but only for hotels.

If you want to follow along, the full source code is available on Github.

Getting set up

Here are the steps I took to create a new project before I started writing code.

  1. At the command line: dotnet new aurelia. This assumes that you have .NET Core installed. Note that Geospatial Search is not a .NET-only feature: you can use it with the other Couchbase SDKs like Node.js, Java, etc. It also assumes that you’ve installed a SPA template for Aurelia. You can also go with Angular or React if you’d like, but I really like Aurelia, and I think you should give it a chance.

  2. The above command will create a shell of an ASP.NET Core project. In this blog post, I’m not going to use Razor. I’m just using ASP.NET as a backend for REST API endpoints.

  3. npm install aurelia-google-maps. You don’t have to use this, but the aurelia-google-maps plugin will make it easy for me to interact with Google Maps in my app.

  4. I opened this project in Visual Studio 2017. I added Couchbase.Extensions.DependencyInjection with NuGet. You don’t have to use this extension but it makes things easier.

  5. I installed Couchbase Server 5.5, including the Full Text Search service. I setup the travel-sample bucket. I created a user "matt" with full access to that bucket.

Create a Geospatial Index

Before building the ASP.NET backend, we need to create a geospatial index in Couchbase Server. Once you log in, click "Search" on the menu (it’s under "Workbench"). Click "Add Index" to get started.

Create Geospatial index

I named my index "mygeoindex". I selected travel-sample as the bucket to index.

In "Type Mappings", I uncheck the default. I add a new type mapping with a type name of "hotel". Every hotel document in "travel-sample" has a type with a value of "hotel". Check the "only index specified fields" box.

I’m going to add two child fields. One is "geo", which contains the geospatial coordinates inside a hotel document. Make sure to select "geopoint" as the type. The other is "name", which will be the name of the hotel. I choose to "store" each of these: it will make the index larger, but I can avoid a secondary lookup if I store the information in the index.

Important Note: There is a bug (NCBC-1651) in the current release of the .NET SDK that will cause an error if you try to read from a geopoint field. In the code samples, I’ve created a workaround: I don’t actually get the geo & name fields from the search index. I instead use the document key returned by search to make a secondary "get" call and get the full document. Keep in mind this is still a technique you may want to consider if you want to keep the size of your index down. This bug has already been fixed and will be in a future release. Such is the peril of being on the cutting edge!

That’s all there is to it. Click "Create Index". Watch the "indexing progress" on the next screen until it gets to 100% (it should not take very long, assuming you remembered to uncheck "default").

ASP.NET Core REST Endpoints

Next, let’s move over to ASP.NET. I’ll create two endpoints. One endpoint will demonstrate the bounding box search method, and the other will demonstrate the distance search method.

I’ll need a Couchbase bucket object to execute the queries. Follow the examples in my blog post about dependency injection or check out the source code on Github if you’ve never done this before.

Bounding Box

A "bounding box" search means that you define a box on a map, and you want to search for points of interest that are inside of that box. You only need two points to define a box: the top right corner coordinates and the bottom left corner coordinates. (Coordinates are latitude and longitude).

public class BoxSearch
{
    public double LatitudeTopLeft { get; set; }
    public double LongitudeTopLeft { get; set; }
    public double LatitudeBottomRight { get; set; }
    public double LongitudeBottomRight { get; set; }
}

To create a bounding box geospatial query, use the GeoBoundingBoxQuery class available in the .NET SDK. I’ll do this inside of a POST method with the above BoxSearch class as a parameter.

        [Route("api/Box")]
        [HttpPost]
        public IActionResult Box([FromBody] BoxSearch box)
        {
            var query = new GeoBoundingBoxQuery();
            query.TopLeft(box.LongitudeTopLeft, box.LatitudeTopLeft);
            query.BottomRight(box.LongitudeBottomRight, box.LatitudeBottomRight);
            var searchParams = new SearchParams()
                // .Fields("geo", "name") // omitting because of bug NCBC-1651
                .Limit(10)
                .Timeout(TimeSpan.FromMilliseconds(10000));
            var searchQuery = new SearchQuery
            {
                Query = query,
                Index = "mygeoindex",
                SearchParams = searchParams
            };
            var results = _bucket.Query(searchQuery);

// ... snip ...

All I need to return from this endpoint is a list of the results: each hotel’s coordinates and the hotel’s name & location. I created a GeoSearchResult class for this.

public class GeoSearchResult
{
    public double Latitude { get; set; }
    public double Longitude { get; set; }
    public InfoWindow InfoWindow { get; set; }
}

public class InfoWindow
{
    public string Content { get; set; }
}

I’ve constructed this class to match the Google Maps plugin that I’ll be using later.

Finally, I’ll use this class to return some results from the endpoint.

// ... snip ...

            var list = new List<GeoSearchResult>();
            foreach (var hit in results.Hits)
            {
                // *** this part shouldn't be necessary
                // the geo and name should come with the search results
                // but there's an SDK bug NCBC-1651
                var doc = _bucket.Get<dynamic>(hit.Id).Value;
                // ****************
                list.Add(new GeoSearchResult
                {
                    Latitude = doc.geo.lat,
                    Longitude = doc.geo.lon,
                    InfoWindow = new InfoWindow
                    {
                        Content = doc.name + "<br />" +
                            doc.city + ", " +
                            doc.state + " " +
                            doc.country
                    }
                });
            }
            return Ok(list);
        }

A "distance" search is another way to perform geospatial queries. This time, instead of a box, it will be more like a circle. You supply a single coordinate, and a distance. The distance will be the radius from that point.

public class PointSearch
{
    public double Latitude { get; set; }
    public double Longitude { get; set; }
    public int Distance { get; set; }
    // miles is being assumed as the unit
    public string DistanceWithUnits => Distance + "mi";
}

I’m defaulting it to miles, but certainly you can use kilometers instead, or present the option in the UI.

The endpoint will be very similar to the bounding box endpoint, except that it uses GeoDistanceQuery.

[Route("api/Point")]
[HttpPost]
public IActionResult Point([FromBody] PointSearch point)
{
    var query = new GeoDistanceQuery();
    query.Latitude(point.Latitude);
    query.Longitude(point.Longitude);
    query.Distance(point.DistanceWithUnits);
    var searchParams = new SearchParams()
        // .Fields("geo", "name") // omitting because of bug NCBC-1651
        .Limit(10)
        .Timeout(TimeSpan.FromMilliseconds(10000));
    var searchQuery = new SearchQuery
    {
        Query = query,
        Index = "mygeoindex",
        SearchParams = searchParams
    };
    var results = _bucket.Query(searchQuery);

    var list = new List<GeoSearchResult>();
    foreach (var hit in results.Hits)
    {
        // *** this part shouldn't be necessary
        // the geo and name should come with the search results
        // but there's an SDK bug NCBC-1651
        var doc = _bucket.Get<dynamic>(hit.Id).Value;
        // ****************
        list.Add(new GeoSearchResult
        {
            Latitude = doc.geo.lat,
            Longitude = doc.geo.lon,
            InfoWindow = new InfoWindow
            {
                Content = doc.name + "<br />" +
                          doc.city + ", " +
                          doc.state + " " +
                          doc.country
            }
        });
    }
    return Ok(list);
}

At this point, you can start testing these endpoint with Postman or Fiddler if you’d like. But it will be so much nice to see this on a map.

Auerlia and Google Maps

In Aurelia, I’ve created two components: geosearchbox and geosearchpoint.

Auerlia components

Each of them will have a Google Maps component that the user can interact with. These maps will be centered on San Francisco, because that’s where a lot of the hotels in "travel-sample" are located.

Bounding Box search component

The google-map` component has a map-click.delegate that will will fire whenever the users clicks on the map. In geosearchbox.html:

<google-map
    if.bind="markers"
    map-click.delegate="clickMap($event)"
    latitude="37.780986253433895"
    longitude="-122.45291600632277"
    zoom="12"
    markers.bind="markers">
</google-map>

markers is simply an array containing coordinates of search results that should appear on the map. Initially it will be empty.

When the user first clicks the map, this will set the first coordinate (top left) in the form. In geosearchbox.ts:

public clickMap(event : any) {
    var latLng = event.detail.latLng,
        lat = latLng.lat(),
        lng = latLng.lng();

    // only update top left if it hasn't been set yet
    // or if bottom right is already set
    if (!this.longitudeTopLeft || this.longitudeBottomRight) {
        this.longitudeTopLeft = lng;
        this.latitudeTopLeft = lat;
        this.longitudeBottomRight = null;
        this.latitudeBottomRight = null;
    } else {
        this.longitudeBottomRight = lng;
        this.latitudeBottomRight = lat;
    }
}

Then, click another spot on the map. This will set the second coordinate (bottom right).

My implementation is very bare bones. No fancy graphics and no validation of the second coordinate being to the bottom right of the first. The fields on a form will simply be populated with the latitude and longitude. In geosearchbox.html:

<p>
    Bounding box search:
    <br />
    Latitude (top left):
        <input type="text" value="${ latitudeTopLeft }" />
    Longitude (top left):
        <input type="text" value="${ longitudeTopLeft }" />
    <br />
    Latitude (bottom right):
        <input type="text" value="${ latitudeBottomRight }" />
    Longitude (bottom right):
        <input type="text" value="${ longitudeBottomRight }" />
    <br />
    <input
        if.bind="latitudeTopLeft && latitudeBottomRight"
        click.trigger="searchClick()"
        type="button"
        name="search"
        value="Search" />
</p>

Once you’ve selected two coordinates, a search button will appear. Click that to post these coordinates to the endpoint created earlier, and it will trigger the searchClick() method as seen in geosearchbox.ts:

public searchClick() {
    let boxSearch = {
        latitudeTopLeft: this.latitudeTopLeft,
        longitudeTopLeft: this.longitudeTopLeft,
        latitudeBottomRight: this.latitudeBottomRight,
        longitudeBottomRight: this.longitudeBottomRight
    };

    console.log("POSTing to api/Box: " + JSON.stringify(boxSearch));

    this.http.fetch('api/Box', { method: "POST", body: json(boxSearch) })
        .then(result => result.json() as Promise<any[]>)
        .then(data => {
            this.markers = data;
        });
}

When Aurelia, Google Maps, ASP.NET Core, and Couchbase all work together, it looks like this:

Geospatial bounding box

Distance Search

Implementing the "distance" geostatial query will be similar to the bounding box UI. This time, you only need to click a single point on the map. But, you will need to type in a distance (in miles).

The google-map component will look identical. The clickMap function is different:

public clickMap(event: any) {
    var latLng = event.detail.latLng,
        lat = latLng.lat(),
        lng = latLng.lng();

    this.longitude = lng;
    this.latitude = lat;
}

Specify a distance (in miles), and then click 'search' to make a POST request to the endpoint we wrote earlier.

geosearchbox.html:

    <p>
        Distance search:
        <br />
        Latitude: <input type="text" value="${ latitude }" />
        Longitude: <input type="text" value="${ longitude }" />
        <br />
        Distance (miles): <input type="text" value="${ distance }" />
        <br />
        <input if.bind="latitude" click.trigger="searchClick()" type="button" name="search" value="Search" />
    </p>

geosearchbox.ts:

    public searchClick() {
        let pointSearch = {
            latitude: this.latitude,
            longitude: this.longitude,
            distance: this.distance
        };

        console.log("POSTing to api/Point: " + JSON.stringify(pointSearch));

        this.http.fetch('api/Point', { method: "POST", body: json(pointSearch) })
            .then(result => result.json() as Promise<any[]>)
            .then(data => {
                this.markers = data;
            });
    }
}

Below is a clip of the search in motion. Note how the results change as I move the coordinate around.

Geospatial distance point search query

Summary

With Couchbase’s built-in geospatial indexing and search feature, all the math and the searching is delegated to the Couchbase Data Platform. So you can focus on building a killer UI (better than mine anyway) and rock-solid business logic.

Be sure to check out the documentation for a complete overview of the geospatial capabilities of Couchbase.

If you need help or have questions, please check out the Couchbase Server forums, and if you have any questions about the Couchbase .NET SDK, check out the .NET SDK forums.

If you’d like to get in touch with me, please leave a comment or find me on Twitter @mgroves.

Thanks to tremendous response, I’ve got a ton of great guests lined up.

Reminders:

  • Be sure to check out my sponsor: Smartsheet! Thanks to them, I’m able to afford some new equipment, more hosting, and the JavaScript game show prizes!

  • If you’re enjoying the new JavaScript game show, please send in your suggestions. I’m running out of ideas, and you and your ideas could be featured on this show. I need real and made-up suggestions! The sillier the names, the better.

Subscribe now!

Here’s what’s coming in March:

  • David Giard is returning to talk more cognitive services

  • Chase Aucoin on the Microservices Manifesto

  • Andrea Cremese returns to talk developer retention

  • Jeffrey Miller returns to talk about authoring a children’s book

  • Craig Stuntz returns to talk .NET IL

Subscribe now with your podcatcher of choice!

Want to be on the next episode? You can! All you need is the willingness to talk about something technical.

This is a repost that originally appeared on the Couchbase Blog: Alexa Skills with Azure Functions and Couchbase.

Alexa Skills are the "apps" that you can build to run on Amazon devices like the Echo, Echo Dot, etc. In this blog post, you’ll learn how to build an Alexa skill using serverless Azure Functions and a Couchbase backend running on Azure. This post builds on a lot of blog posts I’ve written about Azure Functions, Serverless, and Couchbase on Azure in the past:

What kind of Alexa skills am I building?

I work as a Developer Advocate, which means I often spend time at sponsor booths at developer events. I love doing this: I get to tell people how great Couchbase is, and I often get feedback from developers about what problems they’re trying to solve with Couchbase.

If there’s one thing I don’t like about working a booth, though, it’s repetition. I often get asked the same set of questions hundreds of times per events:

  • What is Couchbase? (distributed NoSQL document database with a memory first architecture)

  • How is Couchbase different than MongoDB? (they are both document databases, but Couchbase has major feature and architectural differences)

  • Is Couchbase the same thing as CouchDB? (No.)

I’m not complaining, mind you. It’s just that it’s hard to be enthusiastic when answering the question for the 100th time as the conference is about to close down.

But you know who is always enthusiastic? Alexa! So, if I bring my Echo Dot to the next event, maybe she can help me:

  • What is Couchbase? - Alexa will say a random interesting fact about Couchbase

  • How is Couchbase different than MongoDB? Alexa will say a random architectural or feature difference.

  • Is Couchbase the same thing as CouchDB? Alexa will say "no".

If these Alexa skills turn out to be helpful, I can expand the skills later to answer more complex questions.

If you want to follow along with this post and create your own Alexa skills, the full source code is available on Github.

Design

Alexa skills are registered with Amazon. For the most part, they make simple HTTP requests to the endpoint that you designate and expect a certain JSON response. Azure Functions can process HTTP requests. The Azure Functions can make queries out to a database full of responses, and can also keep track of how many times each response has been given.

Below is a high-level architectural diagram of my minimum viable Alexa skills project:

Architecture diagram from you

Data storage and design

The skill is going to ultimately query some data from Couchbase Server. I’ll start with 2 different kinds of documents. (If these Alexa skills turn out to be useful, I’ll add more complex stuff later).

Document design

Each document represents a possible response. Each will have 3 fields:

  • type - This will be either "mongodbcomparison" or "whatiscouchbase".

  • number - The number of times this response has been used (starting at 0).

  • text - The text that I want the Alexa skills to say.

The document key design of these documents is not important (at least not yet), since I’ll be using only N1QL (SQL for JSON) queries to retrieve them. However, I’ve decided to create keys like "mongo::2" and "couchbase::5".

To start, I will store this data in a single Couchbase node on a low cost Azure VM. A single node with a small amount of data should be able to handle even heavy booth traffic no problem. But if, for instance, I were to install these as kiosks in airports around the world I will definitely need to scale up my Couchbase cluster. Couchbase and Azure makes this easy.

Query design

To get a random document, I need to run a N1QL query:

SELECT m.*, META(m).id
FROM boothduty m
WHERE m.type = 'mongodbcomparison'
ORDER BY UUID()
LIMIT 1;

UUID is functioning as a random number generator. That’s not really what it’s for, but it’s "good enough". If I really needed true randomness, I could make a curl request in N1QL to random.org’s API.

To run that query, I need to create an index for the 'type' field:

CREATE INDEX ix_type ON boothduty(type);

Azure Functions

To create an Azure Function, I used an existing .NET library called AlexaSkills.NET, which makes it very easy to write the code you need to create Alexa skills.

After creating my Azure Functions solution, I added it with NuGet.

Using AlexaSkills.NET

Next, I created a "speechlet" class. I chose to make my speechlet asynchronous, but a synchronous option exists as well. There are four methods that need to be created. I only really need two of them for the skill at this point.

    public class BoothDutySpeechlet : SpeechletBase, ISpeechletWithContextAsync
    {
        public async Task<SpeechletResponse> OnIntentAsync(IntentRequest intentRequest, Session session, Context context)
        {
            try
            {
                var intentName = intentRequest.Intent.Name;
                var intentProcessor = IntentProcessor.Create(intentName);
                return await intentProcessor.Execute(intentRequest);
            }
            catch (Exception ex)
            {
                var resp = new SpeechletResponse();
                resp.ShouldEndSession = false;
                resp.OutputSpeech = new PlainTextOutputSpeech() { Text = ex.Message };
                return await Task.FromResult(resp);
            }
        }

        public Task<SpeechletResponse> OnLaunchAsync(LaunchRequest launchRequest, Session session, Context context)
        {
            var resp = new SpeechletResponse();
            resp.ShouldEndSession = false;
            resp.OutputSpeech = new PlainTextOutputSpeech() { Text = "Welcome to the Couchbase booth. Ask me about Couchbase." };
            return Task.FromResult(resp);
        }

        public Task OnSessionStartedAsync(SessionStartedRequest sessionStartedRequest, Session session, Context context)
        {
            return Task.Delay(0); // nothing to do (yet)
        }

        public Task OnSessionEndedAsync(SessionEndedRequest sessionEndedRequest, Session session, Context context)
        {
            return Task.Delay(0); // nothing to do (yet)
        }

        // I only need to use this when I'm testing locally
//        public override bool OnRequestValidation(SpeechletRequestValidationResult result, DateTime referenceTimeUtc,
//            SpeechletRequestEnvelope requestEnvelope)
//        {
//            return true;
//        }
    }

The OnLaunchAsync is the first thing that an Echo user will reach. The user will say something like "Alexa, open Matt’s booth helper", and this code will respond with some basic instructions.

The OnIntentAsync is where most of the Alexa skills request will be processed. I’m using a factory/strategy code pattern here to instantiate a different object depending on which intent is being invoked (more on "intents" later).

public static IIntentProcessor Create(string intentName = "FallbackIntent")
{
    switch (intentName)
    {
        case "MongodbComparisonIntent":
            return new MongoDbComparisonIntentProcessor(CouchbaseBucket.GetBucket());
        case "WhatIsCouchbaseIntent":
            return new WhatIsCouchbaseIntentProcessor(CouchbaseBucket.GetBucket());
        case "CouchDbIntent":
            return new CouchDbIntentProcessor();
        case "FallbackIntent":
            return new FallbackIntentProcessor();
        default:
            return new FallbackIntentProcessor();
    }
}

Connecting to Couchbase

CouchbaseBucket.GetBucket() is using Lazy behind the scenes as outlined in my earlier blog post on Azure Functions.

So, whenever a 'What is Couchbase' intent comes in, a WhatIsCouchbaseIntentProcessor is instantiated and executed.

public class WhatIsCouchbaseIntentProcessor : BaseIntentProcessor
{
    private readonly IBucket _bucket;

    public WhatIsCouchbaseIntentProcessor(IBucket bucket)
    {
        _bucket = bucket;
    }

    public override async Task<SpeechletResponse> Execute(IntentRequest intentRequest)
    {
        // get random fact from bucket
        var n1ql = @"select m.*, meta(m).id
                        from boothduty m
                        where m.type = 'whatiscouchbase'
                        order by `number`, uuid()
                        limit 1;";
        var query = QueryRequest.Create(n1ql);
        var result = await _bucket.QueryAsync<BoothFact>(query);
        if (result == null || !result.Rows.Any())
            return await CreateErrorResponseAsync();
        var fact = result.First();

        // increment fact count
        await _bucket.MutateIn<dynamic>(fact.Id)
            .Counter("number", 1)
            .ExecuteAsync();

        // return text of fact
        return await CreatePlainTextSpeechletReponseAsync(fact.Text);
    }
}

Note the use of the N1QL query that was mentioned earlier (slightly tweaked so that facts with lower numbers will be given priority). This code is also using the Couchbase subdocument API to increment the "number" field by 1.

You can view the full code of the other intent processors on Github, but they are very similar (just with slightly different N1QL).

Connecting to Azure Functions

Finally, once my speechlet is ready, it’s easy to wire up to an Azure Function.

public static class BoothDuty
{
    [FunctionName("BoothDuty")]
    public static async Task<HttpResponseMessage> Run([HttpTrigger(AuthorizationLevel.Anonymous, "get", "post", Route = null)]HttpRequestMessage req, TraceWriter log)
    {
        var speechlet = new BoothDutySpeechlet();
        return await speechlet.GetResponseAsync(req);
    }
}

You can now test this locally with Postman, or with the Alexa interface once you deploy to azure.

Creating the Alexa skills

I won’t go through the whole process, since there’s plenty of documentation on how to setup Alexa skills. I think I have more work to do before my skill is officially certified, but it’s good enough for beta testing.

Once you have the Azure Functions URL, you’ll use that with Alexa. Alexa requires skills to use HTTPS, but fortunately Azure Functions come with HTTPS on a azurewebsites.net subdomain. Here’s a screenshot:

Alexa skills HTTPS configuration with Azure Functions

I mentioned "intents" earlier. These are various types of actions that Alexa skills can process, along with their inputs. Think of these like function signatures. Currently, I have designed 3 intents, and I have no parameters on these (yet). So my intent schema is a very simple piece of JSON:

Alexa skills intent schema

For each intent, you can create "utterances" that map to the intents. These are the phrases that an Echo user will speak, and which intent they correspond to.

Alexa skills sample utterances

I’ve tried to think of all the different variations. But if I really wanted this to work more generally, I would setup parameters so that a user could ask the question "What is the difference between Couchbase and {x}".

Echo Dot in action

I did not publish this on the Alexa store. I did deploy it as a "beta test", so if you want to try it out, I’d be happy to send you an invitation to get it.

Here’s a video of my trying it out on my Echo Dot (which was a speaker gift last year from the fine people at DevNexus):

Will this actually work at a noisy booth? Well, let’s just say I’m not ready to bring an easy chair and pillow to the booth just yet. But it’s a fun way to demonstrate the power of Couchbase as an engagement database.

Summary

Alexa skills are a great place to use serverless architecture like Azure Functions. The skills will be used intermittently, and Azure Functions will only bill you for the time they are executed.

Couchbase Server again makes a great database for such an app. It can start out small to handle a single booth, but it can scale easily to accommodate larger demand.

Have a question about Couchbase? Visit the Couchbase forums.

Have a question for me? Find me on Twitter @mgroves.

Be sure to check out all the great documentation from Microsoft on Azure Functions, and the documentation on the Alexa Skills .NET library.

Swashbuckle is a handy library to easily bring Swagger support to your ASP.NET Core (or ASP.NET) application. It is especially handy when developing an HTTP based API. It creates a form of interactive documentation based on the OpenAPI Specification.

Before diving into Swashbuckle: Merry Christmas! This blog is being posted on December 25th, 2017. It’s the final post of the very first C# Advent Calendar. Please check out the other 24 posts in the series! This event has gone so well, that I’m already planning on doing it again in 2018. Thank you, again, to everyone who participated (whether you are a writer or you’ve just been following along).

The full source code used in this example is available on Github.

ASP.NET Core HTTP API

I’m going to assume some level of familiarity with ASP.NET Core and creating a REST API. Here’s an example of a GET and a POST. These endpoints are reading/writing from a JSON text file (in a way that is probably not thread-safe and definitely not efficient, but it’s fine for this example).

public class ValuesController : Controller
{
    [HttpGet]
    [Route("api/teams")]
    public IActionResult GetTeams()
    {
        var jsonFile = System.IO.File.ReadAllText("jsonFile.json");
        var teams = JsonConvert.DeserializeObject<List<Team>>(jsonFile);
        return Ok(teams);
    }

    [HttpPost]
    [Route("api/team")]
    public IActionResult PostTeam([FromBody]Team team)
    {
        var jsonFile = System.IO.File.ReadAllText("jsonFile.json");
        var teams = JsonConvert.DeserializeObject<List<Team>>(jsonFile);
        teams.Add(team);
        System.IO.File.WriteAllText("jsonFile.json",JsonConvert.SerializeObject(teams));
        return Ok(team);
    }

    // etc...

To try out the GET endpoint, the simplest thing I can do is open a browser and view the results. But to try out the POST endpoint, I need something else. I could install Postman or Fiddler (and you should). Here’s how that would look.

Postman

Postman is great for interacting with endpoints, but Postman alone doesn’t really tell us anything about the endpoint or the system as a whole. This is where Swagger comes in.

Swagger

Swagger is a standard way to provide specifications for endpoints. Usually, that specification is automatically generated and then used to generate an interactive UI.

We could write the Swagger spec out by hand, but fortunately ASP.NET Core provides enough information to generate a spec for us. Look at the PostTeam action above. Just from reading that we know:

  • It expects a POST

  • The URL for it is /api/team

  • There’s a Team class that we can look at to see what kind of body is expected

From that, we could construct a Swagger spec like the following (I used JSON, you can also use YAML).

{
	"swagger": "2.0",
	"info": { "version": "v1", "title": "Sports API" },
	"basePath": "/",
	"paths": {
		"/api/team": {
			"post": {
				"consumes": ["application/json"],
				"parameters": [{
					"name": "team",
					"in": "body",
					"required": false,
					"schema": { "$ref": "#/definitions/Team" }
				}]
			}
		}
	},
	"definitions": {
		"Team": {
			"type": "object",
			"properties": {
				"name": { "type": "string" },
				"stadiumName": { "type": "string" },
				"sport": { "type": "string" }
			}
		}
	}
}

But why on earth would you want to type that out? Let’s bring in a .NET library to do the job. Install Swashbuckle.AspNetCore with NuGet (there’s a different package if you want to do this with ASP.NET).

You’ll need to add a few things to Startup.cs:

In the ConfigureServices method:

services.AddSwaggerGen(c =>
{
    c.SwaggerDoc("v1", new Info { Title = "Sports API", Version = "v1"});
});

In the Configure method:

app.UseSwagger();

Aside: With ASP.NET, NuGet actually does all this setup work for you.

Once you’ve done this, you can open a URL like http://localhost:9119/swagger/v1/swagger.json and see the generated JSON spec.

Swagger spec in JSON

Swagger UI with Swashbuckle

That spec is nice, but it would be even nicer if we could use the spec to generate a UI.

Back in the Configure method, add this:

app.UseSwaggerUI(c =>
{
    c.SwaggerEndpoint("/swagger/v1/swagger.json", "Sports API v1");
});

Now, open your site and go to /swagger:

Swagger UI generated by Swashbuckle

Some cool things to notice:

  • Expand/collapse by clicking the URL of an endpoint (note that you must use Route attributes for Swashbuckle to work with ASP.NET Core).

  • "Try it out!" buttons. You can execute GET/POST right from the browser

  • The "parameter" of the POST method. Not only can you paste in some content, but you get an example value that acts like a template (just click it).

Giving some swagger to your Swagger

Swagger and Swashbuckle have done a lot with just a little bit. It can do even more if we add a little more information in the code.

  • Response: The ProducesResponseType attribute will let Swagger know what the response will look like (this is especially useful if you are using IActionResult and/or an endpoint could return different types in different situations).

  • Comments: If you are using XML comments, you can have these included with the Swagger output.

services.AddSwaggerGen(c =>
{
    c.SwaggerDoc("v1", new Info { Title = "Sports API", Version = "v1" });
    var filePath = Path.Combine(PlatformServices.Default.Application.ApplicationBasePath, "swashbuckle-example.xml");
    c.IncludeXmlComments(filePath);
});

(Also make sure you XML Documentation output for your project enabled)

Here’s an example of a GetTeams method with both XML comments and ProducesResponseType:

/// <summary>
/// Gets all the teams stored in the file
/// </summary>
/// <remarks>Baseball is the best sport</remarks>
/// <response code="200">List returned succesfully</response>
/// <response code="500">Something went wrong</response>
[HttpGet]
[Route("api/teams2")]
[ProducesResponseType(typeof(Team), 200)]
public IActionResult GetTeams2()
{
    var jsonFile = System.IO.File.ReadAllText("jsonFile.json");
    var teams = JsonConvert.DeserializeObject<List<Team>>(jsonFile);
    return Ok(teams);
}
  • Customize your info: there’s more to the Info class than just Title and Version. You can specify a license, contact, etc.

services.AddSwaggerGen(c =>
{
    c.SwaggerDoc("v1", new Info
    {
        Title = "Sports API",
        Version = "v1",
        Description = "An API to list and add sports teams",
        TermsOfService = "This is just an example, not for production!",
        Contact = new Contact
        {
            Name = "Matthew Groves",
            Url = "https://crosscuttingconcerns.com"
        },
        License = new License
        {
            Name = "Apache 2.0",
            Url = "http://www.apache.org/licenses/LICENSE-2.0.html"
        }
    });
    var filePath = Path.Combine(PlatformServices.Default.Application.ApplicationBasePath, "swashbuckle-example.xml");
    c.IncludeXmlComments(filePath);
});

Here’s a screenshot of the UI that has all three of the above enhancements: response type, XML comments, and more customized info.

Swagger with more swagger

Summary

Working on HTTP-based APIs? Bring Swashbuckle and Swagger into your life!

More resources:

Thanks again for reading the 2017 C# Advent!

This is a repost that originally appeared on the Couchbase Blog: Chatbot on Azure and Couchbase for Viber.

A chatbot can be a novel way to interact with users. After writing a post introducing the basics of serverless, and also writing a post on writing Azure Functions, I decided I would try to build something a little more practical than a "hello, world".

Using a serverless architecture for a chatbot makes sense. Chatbot usage may be sporadic. Usage may peak and drop at various times of the day. By using serverless, you’ll only be paying for the resources and time that you need.

If you want to follow along, all the source code for this blog post is available on Github.

Viber Chatbot

I could have chosen a lot of different platforms to create a chatbot for: Facebook Messenger, Skype, WhatsApp, and more. But I decided to go with Viber.

In the United States, Viber doesn’t seem to have a huge following, but I’ve been using it a lot. It’s a very handy way to chat with my wife, send pictures, funny GIFs, and so on. I find it to be more reliable and faster than SMS, especially for pictures. I wish everyone in my family was using it! It’s also a nice side effect that Viber is a Couchbase customer. They switched from MongoDb to support their growing data needs.

Also, Viber’s REST API is simple and well documented. Between the use of serverless architecture and Viber’s API, I couldn’t believe how fast I went from 0 to chatbot.

Setup

First, You’ll need to start by creating a bot in Viber (you’ll need a Viber account at some point). Viber will give you an API key that looks something like 30a6470a1c67d66f-4207550bd0f024fa-c4cacb89afc04094. You’ll use this in the HTTP headers to authenticate to the Viber API.

Next, create a new Azure Functions solution. I’ve previously blogged about Azure Functions with a followup on Lazy Initialization.

I decided to use C# to write my Azure Functions. Unfortunately, there is no .NET SDK for Viber (as far as I know), so I’ll have to use the REST API directly. Not a big deal, I just used RestSharp. But if you prefer NodeJS or Python, Viber has got you covered with SDKs for those languages.

Before you start coding, you’ll need to setup a Webhook. This is simply a way of telling Viber where to send incoming messages. You’ll only need to do this at the beginning. I did this by first deploying a barebones Azure Function that returns a 200. I used Postman to set the initial webhook.

Chatbot webhook with Postman

Finally, I setup a Couchbase cluster on Azure. Getting started with Couchbase and Azure is easy and free. (You can even use the "Test Drive" button to get 3 hours of Couchbase Server without expending any Azure credit). I created a single user called "viberchatbot", a bucket called "ViberChatBot", and I loaded the "travel-sample" bucket.

Azure Function

For this application, I wanted to create a chatbot with a little more substance than "Hello, world" and I also wanted to have a little fun. Here are the commands I want my chatbot to understand:

  • If I say "hi" (or hello, etc), it will respond with "Howdy!"

  • If I ask for "metrics", it will tell me how many messages it’s processed so far.

  • If I mention "twitter", it will make a recommendation about who to follow.

  • If I ask for flights from CMH to ATL (or other airports) it will tell me how many flights there are today (I will use the travel-sample bucket for this data).

  • If I say "help", it will give me a list of the above commands.

I decided not to use any natural language processing or parsing libraries. I’m just going to use simple if/else statements and some basic string matching. If you are planning to create a robust chatbot with rich capabilities, I definitely recommend checking out libraries and tools like LUIS, wit.ai, NLTK and others.

Chatbot code

I started by creating a few C# classes to represent the structure of the data that Viber will be sending to my serverless endpoint.

Viber classes

This is not an exhaustive representation of Viber’s capabilities by far, but it’s enough to start receiving basic text messages.

public class ViberIncoming
{
    public string Event { get; set; }
    public long Timestamp { get; set; }
    public ViberSender Sender { get; set; }
    public ViberMessage Message { get; set; }
}

public class ViberSender
{
    public string Id { get; set; }
    public string Name { get; set; }
}

public class ViberMessage
{
    public string Text { get; set; }
    public string Type { get; set; }
}

Next, the Azure function will convert the raw HTTP request into a ViberIncoming object.

[FunctionName("Chatbot")]
public static async Task<HttpResponseMessage> Run(
    [HttpTrigger(AuthorizationLevel.Anonymous, "get", "post", Route = null)]HttpRequestMessage req,
    TraceWriter log)
{
    var incoming = req.Content.ReadAsAsync<ViberIncoming>().Result;

    var viber = new ViberProcessor(Bucket.Value);
    viber.Process(incoming);

    // return "OK" each time
    // this is most important for the initial Viber webhook setup
    return req.CreateResponse(HttpStatusCode.OK);
}

After this, I created a ViberProcessor class with a Process method that receives this object.

public void Process(ViberIncoming incoming)
{
    if (incoming?.Message?.Type == "text")
    {
        LogIncoming(incoming);
        ProcessMessage(incoming);
    }
}

Processing Viber messages

LogIncoming creates a record (in Couchbase) so that I know everything about each request that comes in.

ProcessMessage will analyze the text of the message and figure out what to do in response. You can check out the complete code on Github, but here’s a brief snippet to give you the idea:

// if the message contains "hi", "hello", etc say "howdy"
else if (HelloStrings.Any(incoming.Message.Text.ToLower().Contains))
    SendTextMessage("Howdy!", incoming.Sender.Id);
// if message contains "?" then link to the forums
else if (incoming.Message.Text.Contains("?"))
    SendTextMessage("If you have a Couchbase question, please ask on the forums! http://forums.couchbase.com", incoming.Sender.Id);
else
    SendTextMessage("I'm sorry, I don't understand you. Type 'help' for help!", incoming.Sender.Id);

Getting metrics

One of things my chatbot listens for is "metrics". When you ask it for metrics, it will give you a count of the incoming messages that it’s processed. Since I’m logging every request to Couchbase, querying for metrics is easily done with a N1QL query.

private string GetMetrics()
{
    var n1ql = @"select value count(*) as totalIncoming
                from ViberChatBot b
                where meta(b).id like 'incoming::%';";
    var query = QueryRequest.Create(n1ql);
    var response = _bucket.Query<int>(query);
    if (response.Success)
        return $"I have received {response.Rows.First()} incoming messages so far!";
    return "Sorry, I'm having trouble getting metrics right now.";
}

Sending a message back

The chatbot needs to communicate back to the person who’s talking to it. As I said earlier, there is no Viber .NET SDK, so I have to create a REST call "manually". This is easy enough with RestSharp:

private void SendTextMessage(string message, string senderId)
{
    var client = new RestClient("https://chatapi.viber.com/pa/send_message");
    var request = new RestRequest(RestSharp.Method.POST);
    request.AddJsonBody(new
    {
        receiver = senderId,    // receiver	(Unique Viber user id, required)
        type = "text",          // type	(Message type, required) Available message types: text, picture, etc
        text = message
    });
    request.AddHeader("X-Viber-Auth-Token", ViberKey);
    var response = client.Execute(request);

    // log to Couchbase
    _bucket.Insert("resp::" + Guid.NewGuid(), response.Content);
}

Note that I’m also logging each response from Viber to Couchbase. This could be very useful information for later analysis and/or troubleshooting. If Viber decides to change the structure and content of their response, the data in Couchbase is all stored as flexible JSON data. You will not get surprise errors or missing data at this ingestion point.

Summary

That’s all the basics. Check out the source code for the complete set of actions/operations that the chatbot can do. To test out the bot, I used my Viber app for Android on my phone (and my wife’s, to make sure it worked when I went public).

Conversation with chatbot

Beware: by the time you read this, the chatbot I created will likely be taken offline. Anyone else who creates a "Couchbase Bot" is not me!

Here’s a recap of the benefits of this approach to creating a chatbot:

  • The serverless approach is a good way to control costs of a chatbot. Whether it’s Viber or some other messaging platform, there is potential for sporadic and cyclic use.

  • Viber’s REST API utilizes JSON, which makes Couchbase a natural fit for tracking/storing/querying.

  • Couchbase’s ease of scaling and partnerships with Microsoft (and Amazon and Google) make it a great choice for a chatbot backend.

This was really fun, and I could definitely get carried away playing with this new chatbot. It could analyze images, tell jokes, look up all kinds of information, sell products and services, or any number of useful operations.

I would love to hear what you’re doing with chatbots! Please leave a comment or contact me on Twitter @mgroves.

Matthew D. Groves

About the Author

Matthew D. Groves lives in Central Ohio. He works remotely, loves to code, and is a Microsoft MVP.

Latest Comments

Twitter