Saturday 31 January 2015

Viewing local site from other computers on the network

This easy solution is turning out to be a huge convenience for me. I run Windows as a VM on my Mac using Parallels Desktop. I want the Windows installation to be as clean as possible, not having to install a lot of browsers to be able to check my code and so forth. 

The Mac OS is where I normally do things, I keep all my non .Net code there and all the developer utility tools I need. So to use the browsers on my Mac side to view what I'm working on in my Windows VS dev environment, this is what I do.

I just use the built in IIS Express while working. This means my settings for the web project in VS looks like this. A random portnumber is given for the localhost. To find this info, right click your web project and choose 'Properties'.


This results in a nice webpage showing up at http://localhost:2686 when i start and run the site.


It also results in the following configuration being made in the applicationHost.config file for IISExpress. This is the part describing which website can be reached on which address. The applicationHost.config is cleverly hidden, usually at {youruseraccount}/Documents\IISExpress\config. You need to open it as an administrator to be able to save your changes. 


Under the <sites>-node you find all the sites currently configured for your IISExpress. The interesting part right now is the binding configuration. This one tells you the site can be found at localhost:2686. That seems about right. But the Mac host, or any other computer on your network can't call localhost:2686 to see the site, they'd just be ending up on their own localhost of course. So this is what we need to fix.

The pattern for the binding is <ipnumber>:<port>:<localhost>. I want to keep the current localhost:2686, that's nice. But I want to add a new binding making sure I can view the site from my Mac. And since I'm calling the windows localhost with win.localhost in other contexts, I add the following line.


Now, to be sure Visual Studio can use this binding, it has to be run in administrator mode. Just start the site up and go to your other machine...


And look at that! Just as beautiful on a Mac. Now... A few things need to be said. The reason I can write win.localhost:2686 on my Mac is because I have that IP-number mapped in my hostfile, like this:

10.211.55.3 win.localhost

And, another reason is that I have opened the Firewall in my VM for incoming calls from the Mac. So those things need to be fixed as well.

If you want to access the localhost from the outside via the IP-number, all you have to do is change the binding in applicationHost.config to the IP-number of your computer on the internal network. Like this, in my case:


Monday 5 January 2015

Step one - A simple Elastic Search Repository using NEST

So, I now have Elastic Search set up on my Mac and decided to build an API using .Net and Nancy that will serve as an inventory service for the cars in my Car Rental-solution. I've made sure I can call the Elastic Search instance using the address mac.localhost:9200. 

The first thing I want to do is to create a kind of repository wrapping Elastic Search, and tests to make sure everything works. But since I really wanna do my solutions teeny tiny, I don't want to do that in a separate project but in the Nancy App itself. Which makes the first step creating the Nancy App that will hold the API.

Nowadays, Nancy comes with some nice project templates for Visual Studio. Just go into Tools >> Extensions and updates to download them.








And now you find them together with all the normal Project templates:













I'm trying hard to do things as simple as possible to start with, which for instance means I try to avoid interfaces unless I see the clear need, meaning either several implementations of a common contract or the need to mock a dependency for unit testing. Here, I see the need to do the latter so I start with a simple ICarRepository interface:

public interface ICarRepository
{
  string Save(Entities.Car car);
        Entities.Car Get(Guid id);
        bool Delete(Guid id);
        IEnumerable<Entities.Car> List();
        IEnumerable<Entities.Car> Search(string query);
}
And as you see, not a trace of generics. Cause this is a small API serving only cars. :)
Now, next step is to implement this contract in an ElasticSearchCarRepository. And to do that, I need an Elastic Search client for .Net.


NEST

The client I've chosen to use is NEST, which seems to have all the basic functionality I could possible want for a simple application. It's one of the two official ES clients, the other being the more low level Elasticsearch.net, which NEST actually uses internally. NEST has a nice strongly typed Query DSL which makes queries against Elastic Search quite easy. To use NEST, just install the Nuget package.
All that is needed to start indexing and querying Elastic Search with NEST is to create an ElasticClient and start using it as the simple examples below show. The "cars"-string is the name of the index targeted by the client.

elasticClient = new ElasticClient(new ConnectionSettings(
 new Uri("http://mac.localhost:9200"), "cars"));
elasticClient.Index<Entities.Car>(car);
elasticClient.Search<Entities.Car>(s => s.QueryString(query))

Since the client is threadsafe I choose to inject it into the repository. Doing that lets me change the client for the tests I'll write so I can test the repository using a testindex that I can play with any way I want to without worrying about destroying real data.

The ElasticSearchCarRepository

The first version of the repository ends up looking like this.

public class ElasticSearchCarRepository : ICarRepository
{
    private readonly ElasticClient _elasticClient;
 
    public ElasticSearchCarRepository(ElasticClient elasticClient)
    {
        _elasticClient = elasticClient;
    }
 
    public string Save(Entities.Car car)
    {
        var result = _elasticClient.Index(car);
        return result.Id;
    }
 
    public Entities.Car Get(Guid id)
    {
        var result = _elasticClient.Get<Entities.Car>(id.ToString());
        return result.Source;
    }
 
    public bool Delete(Guid id)
    {
        var result = _elasticClient.Delete<Entities.Car>(id.ToString(),
            x => x.Type("car"));
        return result.Found;
    }
 
    public IEnumerable<Entities.Car> List()
    {
        var result = _elasticClient.Search<Entities.Car>(search => 
            search.MatchAll());
 
        return result.Documents;
    }
 
    public IEnumerable<Entities.Car> Search(string query)
    {
        var result = _elasticClient.Search<Entities.Car>(search => 
            search.QueryString(query));
 
        return result.Documents;
    }
} 

The only thing really interesting here is the id. Elastic Search creates it's own string id if you don't provide it with a specific id when indexing documents. My car entity already has a Guid id that I want to use. This is possible with a simple mapping in the entity class:

[ElasticType(IdProperty = "CarId")]
public class Car
{
 
    [ElasticProperty(Index = FieldIndexOption.NotAnalyzed)]
    public Guid CarId { getset; }
    public string Make { getset; }
    public CarType CarType { getset; }
    public int DoorCount { getset; }
    public int BagCount { getset; }
    public bool HasAirCondition { getset; }
    public TransmissionType TransmissionType { getset; }
    public decimal RentalPricePerDay { getset; }
    public string Currency { getset; }
 
}

The IdProperty on the class tells Elastic Search which property to use as id. And since this is a Guid, we want to make sure ES does not analyze the field, in which case it would split the guid into several strings using the dash as a delimiter.

Another thing worth mentioning here is that I use two enums. NEST sends these values to ES as the integer values. It works well and the enum gets nicely set again when serializing documents, but I'd rather have the string value in ES for those cases when I just wanna look at the data directly. To do this, I have to set the proper converter when creating the client, like so:

public static ElasticClient CreateElasticClient()
{
    var uri = new Uri(ConfigurationManager.ConnectionStrings
        ["ElasticSearch"].ConnectionString);
    var settings = new ConnectionSettings(uri, "cars");
    settings.AddContractJsonConverters(type => 
        typeof(Enum).IsAssignableFrom(type)
            ? new StringEnumConverter()
            : null);
    return new ElasticClient(settings);
}

Testing the repository

To test the repository out I create a simple class library project and import xUnit.Net as a Nuget. There are some different options on running xUnit-tests. I use Resharper, where the option is available as an extension. Other runners are available via Nuget.

xUnit has a nice and easy way to share context between methods, meaning setup and teardown methods. By creating a fixture-class that inherits from IDisposable, you can put your setup in the constructor and your teardown in the Dispose-method. What I do here is that I create the repository in the fixture-class, using a client targeting a "cars_test"-index. In the Dispose-method, I just delete that index. It's only there for testing so it's ok.

public class ElasticSearchFixtureIDisposable
{
 
    private readonly ElasticSearchCarRepository _repository;
    private readonly ElasticClient _elasticClient;
    private const string IndexName = "cars_test";
 
    public ElasticSearchCarRepository Repository
    {
        get { return _repository;}
    }
 
    public ElasticSearchFixture()
    {
        _elasticClient = CreateElasticClient();
        CreateTestIndex();
        _repository = new ElasticSearchCarRepository(_elasticClient);
    }
 
    private ElasticClient CreateElasticClient()
    {
        var uri = new Uri(ConfigurationManager.ConnectionStrings
            ["ElasticSearch"].ConnectionString);
        var settings = new ConnectionSettings(uri, IndexName);
        settings.AddContractJsonConverters(type => 
            typeof(Enum).IsAssignableFrom(type)
                ? new StringEnumConverter()
                : null);
        return new ElasticClient(settings);
    }
 
    private void CreateTestIndex()
    {
        DeleteIndex();
        _elasticClient.CreateIndex(IndexName, x => x
            .NumberOfReplicas(1)
            .NumberOfShards(1));
    }
 
    
    public void Dispose()
    {
        DeleteIndex();
    }
 
    private void DeleteIndex()
    {
        _elasticClient.DeleteIndex(IndexName);
    }
}

With the fixture in place, I create my test class and can now test if my favourite car can be saved. The fixture class gives me access to the repository and takes care of cleaning up the test data afterwards.

public class ElasticSearchCarRepositoryTests : 
    IClassFixture<ElasticSearchFixture>
{
 
    private readonly ElasticSearchFixture _fixture;
 
    public ElasticSearchCarRepositoryTests(ElasticSearchFixture fixture)
    {
        _fixture = fixture;
    }
 
    [Fact]
    public void Save_Should_Return_Id_Of_Indexed_Car()
    {
        // given
        var carId = Guid.NewGuid();
        var car = CarBuilder.CreateCar()
            .WithId(carId)
            .WithMake("Ford Focus")
            .WithCarType(CarType.Premium).Build();
 
        // when 
        var result = _fixture.Repository.Save(car);
 
        // then
        result.Should().Be(carId.ToString());
    }
}

Very easy first step. With loads of stuff missing. Error handling, logging will come later. But next step is to get the Nancy API up and running with tests. And make everything async.

Code, in one state or another, can be found at github.com: https://github.com/asalilje/CarAPI :)

Thursday 1 January 2015

Now where is that localhost..?

Running Parallels desktop on my MacBook, I figured I wanted to add as little as possible to my Windows installation. Just the necessary things to be able to do .Net development. Visual Studio and SQL Server. As to other databases, I should be able to use the instances already installed on my Mac.

So how to call the localhost of the other OS? Well, not too hard really. Since they're both on a shared network, I should just be able to use the IP-adresses. So first, we need to find them.


Finding the IP-address for the Mac host and using it on Windows

Go into the Parallels Desktop menu >> Preferences. Click the tab Advanced and then Network: Change settings. Check the box 'Show in System Preferences'.








Now you can go into the Apple menu >> System preferences >> Network and find the IP-address of the host under the Parallels network provider.









To check if this works, I started up a node server on my Mac, on localhost:4000.









I went in to my Windowsinstallation and tried to call it with the IP-number 10.211.55.2.







Great success! But of course, I don't want to remember that IP-number. So let's edit the hosts-file. And as always, when editing system files, don't forget to run notepad or whatnot as an administrator. The hosts-file, if you managed to avoid it this far, is located in C:\Windows\System32\drivers\etc.

Add the following line to the file: 
10.211.55.2     mac.localhost

Save it and you should now be able to call the service with mac.localhost instead. Yay!






Finding the IP-address for the Windows guest and using it on Mac

As you probably guess, you do the same thing. To get the IP-number for the Windows guest, go into Windows, start a Command window and type ipconfig. Look for the IPv4 Address.







Now go back to the Mac and find the hosts-file under /etc. Add the following line to the file:
10.211.55.3 win.localhost

And now you should be able to call the windows localhost from Mac using win.localhost.

ElasticSearch issue

After running Elastic Search on my Mac I wanted to use that instance for my Nancy API I was building on Windows. Since Elastic Search was running on localhost:9200 on the Mac, I assumed the above solution would work without problems. But nope, I just got a Website not found response when I tried to access mac.localhost:9200 in the browser. 

Since everything worked with my node server it seemed there wasn't an issue about firewalls or security. I tried some CORS-configuration for a while, thinking that might be the problem. It definitely seemed to have something to do with the Elastic Search configuration and setup. 

After quite a long while I found the thing. The Elastic Search config file elasticsearch.yml had the entry network.host: 127.0.0.1. Apparently this entry didn't care about the parallel network adapter at all. So I simply changed the entry to use that IP instead: 10.211.55.2 and restarted Elastic Search.

But wait! Now it works perfectly calling mac.localhost from Windows but I can't call localhost:9200 from the Mac! So another trip into both of the hosts files.

Now I have this in Windows:
10.211.55.3     win.localhost
10.211.55.2     mac.localhost

And this in Mac:
10.211.55.2 mac.localhost
10.211.55.3 win.localhost

All is well and I can develop my .Net-stuff and call services on the Mac. Very neat. I haven't tried the other way around yet, that might be a blogpost for another day.