Tuesday, October 22, 2013

Geotopia: additional features and sending emails

In the last blog I explained how to use Windows Azure Active Directory and Windows Azure Caching Service. This blog post will dive a bit deeper in these concepts but also adds SendGrid to the solution in order to send emails to users with their temporary password.

The WebAPI Controller will perform the following steps:

1. Create a user in WAAD by using Microsoft.WindowsAzure.ActiveDirectory.GraphHelper. The following snippets achieves this goal. It also creates some temporary password based on a Guid.

Note: your tenant ID can be found on the Windows Azure portal. Go to your application in the directory screen on the portal. Click View Endpoints and you will see a list of endpoints. When you have a look at your OAuth 2.0 token endpoint you will see the URL in following shape:

https://login.windows.net//oauth2/token?api-version=1.0

//add to to Windows Azure Active Directory
            string clientId = CloudConfigurationManager.GetSetting("ClientId").ToString();
            string password = CloudConfigurationManager.GetSetting("ClientPassword").ToString();
            // get a token using the helper
            AADJWTToken token = DirectoryDataServiceAuthorizationHelper.GetAuthorizationToken("", clientId, password);
            // initialize a graphService instance using the token acquired from previous step
            DirectoryDataService graphService = new DirectoryDataService("", token);

            User newWAADUser = new Microsoft.WindowsAzure.ActiveDirectory.User();
            newWAADUser.accountEnabled = true;
            newWAADUser.displayName = user.UserName;
            newWAADUser.mailNickname = user.UserName;
            PasswordProfile pwdProfile = new PasswordProfile();
            pwdProfile.forceChangePasswordNextLogin = true;
            pwdProfile.password = Guid.NewGuid().ToString("N").Substring(1, 10) + "!";
            newWAADUser.userPrincipalName = user.UserName + "@geotopia.onmicrosoft.com";
            newWAADUser.passwordProfile = pwdProfile;
            graphService.AddTousers(newWAADUser);
            var response = graphService.SaveChanges();

I registered "FirstUser" as being the user with an emailaddress I own. As you can see in the next figure, the user is added to the Windows Azure Active Directory.


2. An email is sent to the user with his/her temporary password which is generated in step 1. For sending emails, I use the Windows Azure add-on SendGrid which can be easily configured.

//send email to user by using SendGrid
            SendGrid myMessage = SendGrid.GetInstance();
            myMessage.AddTo(user.EmailAddress);
            myMessage.From = new MailAddress("info@geotopia.com", "Geotopia Administrator");
            myMessage.Subject = "Attention: Your temporary password for Geotopia";
            myMessage.Text = "Your username on Geotopia is:" + user.UserName + "\n\r";
            myMessage.Text += "Temporary password:" + pwdProfile.password + "\n\r";
            myMessage.Text += "\n\r";
            myMessage.Text += "The first time you sign in with your temporary password, you need to change it.";

            // Create credentials, specifying your user name and password.
            var credentials = new NetworkCredential("", "");

            // Create an SMTP transport for sending email.
            var transportSMTP = SMTP.GetInstance(credentials);

            // Send the email.
            transportSMTP.Deliver(myMessage);

After this, I get an email!


3. The user is added to the neo4j graph db. This snippet is already shown in the previous blog post.
4. Add the user to the Windows Azure Cache. This snippets is also shown in the previous blog post.

So, with everything in place now I finalize the search window on geotopia to look for users and start following them. I will blog about this feature in the next few days....

Happy coding!


Monday, October 21, 2013

Geotopia: searching and adding users (Windows Azure Cache Service)

Now that we are able to sign in and add geotopic, the next step would be to actually find users and follow them. The first version of Geotopia will allow everybody to follow everybody, a ring of authorization will be added in the future (users need to allow you to follow them after all).

For fast access and fast search, I decided to use the Windows Azure Cache Service preview to store a table of available users. An entry in the cache is created for everyone that signed up.

First of all, I created a a new cache on the Windows Azure portal.


Azure now has a fully dedicated cache for me up and running. Every created cache has a default cache that is used when no additional information is provided.

Next step is to get the necessary libraries and add them to my webrole. Use the Nuget package manager to get them and search for Windows Azure Caching. This will add the right assemblies and modifies the web.config. It adds a configsection to the configuration file (dataCacheClients).

Now with the cache in place and up and running, I can start adding entries to the cache when somebody signs up and make him/her available in the search screen.

Later on, we can also cache Page Output (performance!) and Session State (scalability).

I also created a controller/view combination that allows users to signup with simply their username and an emailaddress. The temporarily password will be sent to this email account.

Download the Windows Azure AD  Graph Helper at http://code.msdn.microsoft.com/Windows-Azure-AD-Graph-API-a8c72e18 and add it to your solution. Reference it from the project that needs to query the graph and create users.

Summary
The UserController performs the following tasks:
1. Add the registered user to Windows Azure Active Directory by using the Graph Helper
2. Adds the user to the neo4j graph db to enable it to post geotopics
3. Add the user to the Windows Azure Cache to make the user findable.

This snippet does it all.

            string clientId = CloudConfigurationManager.GetSetting("ClientId").ToString();
            string password = CloudConfigurationManager.GetSetting("ClientPassword").ToString();
            // get a token using the helper
            AADJWTToken token = DirectoryDataServiceAuthorizationHelper.GetAuthorizationToken("", clientId, password);
            // initialize a graphService instance using the token acquired from previous step
            DirectoryDataService graphService = new DirectoryDataService("", token);
            //add to Neo4j graph
            GeotopiaUser user1 = new GeotopiaUser(user.UserName, user.UserName, user.UserName + "@geotopia.onmicrosoft.com");

            var geoUser1 = client.Create(user1,
                new IRelationshipAllowingParticipantNode[0],
                new[]
                            {
                                new IndexEntry("GeotopiaUser")
                                {
                                    { "Id", user1.id.ToString() }
                                }
                            });
            //add to cache
            object result = cache.Get(user.UserName);
            if (result == null)
            {
                // "Item" not in cache. Obtain it from specified data source
                // and add it.
                cache.Add(user.UserName, user);
            }
            else
            {
                Trace.WriteLine(String.Format("User already exists : {0}", user.UserName));
                // "Item" is in cache, cast result to correct type.
            }

The modal dialog on the Geotopia canvas searches the cache every time a keydown is notices and displays the users that meet the query input of the dialog.

Monday, October 14, 2013

Geotopia, SignalR and Notification Hub

The next extension of Geotopia is to notify users of updates. Everyone who is following 'me', should get a notification of a topic I posted. Install SignalR by using the package manager console or the Manage nuget packages screen when you right-click the asp.net project. E.g. in the package manage console, run:

Install-Package Microsoft.AspNet.SignalR -Version 1.1.3.

You can also use newer versions of SignalR.

In the webapi controller that handles the creation of Geotopics, I also update all the clients with this newly created Geotopic.

The SignalR hub is lazy loaded by the Geotopic webapi controller.

protected readonly Lazy AdminHub = 
            new Lazy(() => GlobalHost.ConnectionManager.GetHubContext());


The webapi method Post is slightly modified and calls the posted method on the clients.

[System.Web.Http.Authorize]
        public void Post(Geotopic value)
        {    

            //user must be logged in.
            var userNode = client.QueryIndex("GeotopiaUser", IndexFor.Node, String.Format("Id:{0}", User.Identity.Name));

            //now create a topic
            Geotopic newTopic = new Geotopic(value.latitude, value.longitude, value.message);

            var secondtopic = client.Create(newTopic,
                new IRelationshipAllowingParticipantNode[0],
                new[]
                            {
                                new IndexEntry("Geotopic")
                                {
                                     { "Id", newTopic.id.ToString() }
                                }
                            });
            NodeReference reference = userNode.First().Reference;

            client.CreateRelationship(secondtopic, new PostedBy(reference));
            //now SignalR it to all my followers....
            AdminHub.Value.Clients.All.posted(value);
   }

A piece of Javascript makes all of the above happen. It connects to the geotopichub and responds to the "posted" call from the webapi controller.



To show a nice popup on the Geotopiascreen I use Toastr. Get this by: install-package Toastr.

When I post a topic, the Toastr package displays a nice toast:



The SignalR addition enables webclients to be updated on the fly. For future release of mobile clients of Geotopia, I also use the Notification Hub of Windows Azure to enable notifications on mobile devices. To enable this, I create a service bus namespace and a notification hub.


Now I have a notificationhub available. A mechanism to easily update my mobile clients. On the backend side (my webrole), install the service bus package.

Install-Package WindowsAzure.ServiceBus

add use it in your designated class. In my case, I use it in my TopicsController.cs webapi module.

using Microsoft.ServiceBus.Notifications;

I add the following lines to the Post method of my TopicsController:
  NotificationHubClient myNotificationHub = NotificationHubClient.
        CreateClientFromConnectionString("Endpoint=sb://.servicebus.windows.net/;SharedAccessKeyName=DefaultListenSharedAccessSignature;SharedAccessKey=;YOUR_KEY", 
            "YOUR_HUB");
 string toast = "" +
                "" +
                    "" +
                        "A toast from geotopia!" +
                    "
" +                "
";
 Task result = myNotificationHub.SendMpnsNativeNotificationAsync(toast);

In the result we can see the outcome of the notification call.

For now, everybody receives notifications of topics being posted. The next blog post will demonstrate how to make sure only my followers get my topics by using SignalR groups and adding tags for the Notification Hub.

Friday, October 11, 2013

Creating a topic on the Geotopia Canvas

Next step in realizing Geotopia is enabling logged in users to click on the map and add some information for that specific location on the map.

I enable this by:
- use jquery ui to show  a dialog on the screen that takes a piece of text
- use $.ajax to post the data to my webapi controller
- use Neo4j client to create a node containing the piece of text, the location and the user who posted it
- create a PostedBy relationship between the user and the newly created geotopic

Clicking on the map somewhere, displays this model dialog from jquery UI.




Clicking post will do the following to open mydialog and add some data to the dialog (the location as well).

  $("#MyDialog").data('location', location).dialog("open");

Here is where i get my location in the dialog function:
 var source = {
                    'latitude': location.latitude,
                    'longitude': location.longitude,
                 
                    'message': $('#topicmessage').val()
                }
This source is used to send it to my webapi controller.

 $.ajax({
            type: 'POST',
            url: '../api/Topics',
            data: source
          });

This causes my webapi controller to fire off

//user must be logged in.
//i take the username which is indexed in neo4j to get a reference to the right user node.

var userNode = client.QueryIndex("GeotopiaUser", IndexFor.Node, String.Format("Id:{0}", User.Identity.Name));

            //now create a topic
            Geotopic newTopic = new Geotopic(value.latitude, value.longitude, value.Topic);

            var secondtopic = client.Create(newTopic,
                new IRelationshipAllowingParticipantNode[0],
                new[]
                            {
                                new IndexEntry("Geotopic")
                                {
                                     { "Id", newTopic.id.ToString() }
                                }
                            });
            NodeReference reference = userNode.First().Reference;

            client.CreateRelationship(secondtopic, new PostedBy(reference));
            //now SignalR it to all my followers....

The node graph now shows up like this:

Node 163 is the newly created one.

Here are the details of node 163:



The next step will be to use SignalR and let my followers know that I posted something altogether with using the NotificationHub to notify mobile users!




Thursday, October 10, 2013

Setting up Neo4j for Geotopia's graph DB

Since my datamodel probably is going to be very chaotic (lots of relationships between entities) I decided to pick a graph database. Neo4j is my choice and there is an excellent article out there that helps you setup an environment and host it on Azure! Social networks are graphs after all. Querying a social graph can lead to massive and complex joins when you use a SQL RDBMS.

http://blog.jongallant.com/2013/03/neo4j-azure-vs2012.html

After following the instructions on the blog of Jon, I have a cloud service running neo4j and I am ready to store some nodes over there. I have neo4j server running locally and in my Azure space. Running it locally caused some error due to "too long filenames". You can tackle this by changing the output directory of the dev fabric.

To execute CRUD actions on my Neo4j graph db I use Neo4jClient from Readify.

Example: here is a simple representation of a Geotopic and a GeotopicUser.

    public class Geotopic
    {
        public string id { get; private set; }
        public string latitude { get; set; }
        public string longitude { get; set; }
        public string message { get; set; }

        public Geotopic(string latitude, string longitude, string message)
        {
            this.latitude = latitude;
            this.longitude = longitude;
            this.message = message;

            this.id = Guid.NewGuid().ToString();
        }
    }

    public class GeotopiaUser
    {
        public string id { get; set; }
        public string FirstName { get; set; }
        public string LastName { get; set; }

        public GeotopiaUser(string firstName, string lastName, string id)
        {
            this.FirstName = firstName;
            this.LastName = lastName;

            this.id = id;
        }
    }

These entities do have relationships. A user can post a geotopic. A user can follow another user. A user can like a topic. A user can recommend a topic.

First, after a user is logged in by using the Windows Azure Active Directory credentials, I make sure that the GeotopicUser exists in the neo4j graph db. The id of the user is the username that is registered in the actived directory. See the code snippet below on how users are created and how creating relationships between them is accomplished.

Using the Neo4j Client makes it quite easy to create a graph. In this example I create 2 users, one follows the other and a geotopic that is posted by the first user.

            GraphClient client = new GraphClient(new Uri("http://db/data"));
            client.Connect();

             // Create Indexes
            client.CreateIndex("GeotopiaUser", new IndexConfiguration() { Provider = IndexProvider.lucene, Type = IndexType.exact }, IndexFor.Node); // exact node index
            client.CreateIndex("Geotopic", new IndexConfiguration() { Provider = IndexProvider.lucene, Type = IndexType.exact }, IndexFor.Node); // exact node index

            // Create Entities
            // Movies
            GeotopiaUser user1 = new GeotopiaUser("Riccardo", "Becker", "riccardo@geotopia.onmicrosoft.com");

            var geoUser1 = client.Create(user1,
                new IRelationshipAllowingParticipantNode[0],
                new[]
                {
                    new IndexEntry("GeotopiaUser")
                    {
                        { "Id", user1.id.ToString() }
                    }
                });

            //new topic
            Geotopic topic1 = new Geotopic("0.0", "0.0", "First topic!");

            var firsttopic = client.Create(topic1,
                new IRelationshipAllowingParticipantNode[0],
                new[]
                {
                    new IndexEntry("Geotopic")
                    {
                         { "Id", topic1.id.ToString() }
                    }
                });

            client.CreateRelationship(firsttopic, new PostedBy(geoUser1));

            GeotopiaUser user2 = new GeotopiaUser("John", "Doe", "johndoe@geotopia.onmicrosoft.com");

            var geoUser2 = client.Create(user2,
                new IRelationshipAllowingParticipantNode[0],
                new[]
                {
                    new IndexEntry("GeotopiaUser")
                    {
                        { "Id", user2.id.ToString() }
                    }
                });

            //john follows me
            client.CreateRelationship(geoUser2, new FollowRelationship(geoUser1));

Executing this code created the following graph:


Node with id 49 is my first user (riccardo) and node 51 is the second user (John Doe). John follows me and I created one topic. This is setup by creating a "posted_by" relationship between node 50 and 49.

The following code uses the index "GeotopiaUser" and looks up every user in the graph.

            List> list = client.QueryIndex("GeotopiaUser", IndexFor.Node, "Id: *").ToList();

            foreach (Node user in list)
            {
                Console.WriteLine(user.Data.FirstName + " " + user.Data.LastName);

            }

The next post will describe how to post a Geotopic on the canvas of Geotopia and how this is added to the graph DB of Neo4j.

Happy coding!


Thursday, October 3, 2013

The Wireframe of Geotopia

Using the Visual Studio 2013 RC, I created a cloud project with an ASP.NET webrole. When you add a webrole to your cloud project, the screen below appears.



I want to create an MVC app and want to use Windows Azure Active Directory authentication. To enable this you need to create a new Directory in your Active Directory in the Windows Azure portal. When you create a new directory, this screen appears.


Add some users to your directory. In this case, I have "riccardo@geotopia.onmicrosoft.com" for now.
Go back to Visual Studio and select the Change Authencation button. Next, select the Organization Acounts and fill in your directory.


When you click Ok now you need to login with the credentials of one of your users you created in your directory. I log in with riccardo@geotopia.onmicrosoft.com. Next, select Create Project and your MVC5 app is ready to go. Before this application is able to run locally, in your development fabric, you need to change the return URL in the Windows Azure portal. Go to applications in the designated Directory on the Windows Azure portal. The newly created MVC project appears there, in my case it's Geotopia.WebRole. In this screen you see the app url which points to https://localhost:44305. This URL is NOT correct when you run the MVC5 app as a cloud project in the development fabric. Click the application in the portal and select Configure. Change the app URL and the return URL to the correct url when your app runs locally in development fabric. In my case: https://localhost:8080. When you run your app, you get a warning about a problem with the security certificate, but you can ignore this for now. After logging in succesfully, you will be redirected to the correct return URL (configured in the portal) and showing you that you are logged in. I also created a bing map which centers on the beautiful Isola di Dino in Italy with a bird's eye view.



In the next blog I will show how to create a Geotopic on the Bing Map and how to store it in Table Storage and how add your own fotographs to it to show your friends how beautiful the places are you visited. This creates an enriched view, additional to the bing map.