Skip to content

Backend Data Projection

Sam Tyson edited this page Jul 12, 2018 · 5 revisions

Previously we introduced you to our back-end stack and showed you how to added an endpoint action which creates a modem. In this tutorial we are going to dive deep into pulling data out of your Clarify/Dovetail system and exposing more endpoints for your front-end. We will:

  • Provide an endpoint for viewing existing modems.
  • Show how to project data using ModelMaps from the database into your view models
  • Demonstrate a diagnostics and AJAX testing workflow.

Show endpoint action

Let's add the following to method to the ModemEndpoint class.

// GET /custom/modems/{Id}
public ModelData get_custom_modems_Id(ShowModemRequest request)
{
	return new ModelData();
}

Then we'll add this new input model.

public class ShowModemRequest : ICustomRequest
{
	public int Id { get; set; }
}

As before we've added a method whose name will describe the URL used to access it. The input model is simple with only an integer identifier for the modem. This Id property will make to the table_modem.objid database field.

Demonstration

Let's get this endpoint added to the application and verify it is set up correctly using FubuDiagnostics, accessible from http://localhost:25120/_fubu/.

We'll also exercise the endpoint using a Chrome extension called Advanced REST Client.

Data Projection with Model Map

It is a very common need, when using an MVC architecture, to get data from your database into view models. You'll then use these view models with a view engine or serialize them out as JSON to the front-end. We have a handy mechanism found in Dovetail Bootstrap for doing this called ModelMap.

Model

First let's look at the Modem type we created earlier.

public class Modem
{
	public int Id { get; set; }
	public string DeviceName { get; set; }
	public string HostName { get; set; }
	public string Status { get; set; }
	public string DeviceType { get; set; }
}

Map

A ModelMap is used to define a mapping from a database table(s) and fields to the properties of your view model. Take a look at this very simple example.

<model name="modem">
  <query from="modem" type="table">
   <addProperty key="id" field="objid" dataType="int" propertyType="identifier" />
   <addProperty key="deviceName" field="device_name" dataType="string" />
   <addProperty key="hostName" field="hostname" dataType="string" />
   <addProperty key="status" field="active" dataType="string" />
   <addProperty key="deviceType" field="device_type" dataType="string" />
  </query>
</model>

Model map provides a DSL to describe to Dovetail SDK how how want to populate your view model. In this case starting at table_modem. We add each property to the projection from on the modem table.

Notice how the id field's map is a bit different. propertyType declares that this field is unique. In a bit we'll leverage this detail to lookup a modem by id.

This model map is about as simple as they come. You can get a bit more complicated.

If you want to learn more you can always explore, please see the Model Map documentation found in Dovetail Agent's documentation.

Let's take a look at how you'd use this model map in your endpoint action.

Show action using IModelBuilder

To build a view model from one of your ModelMaps, you have your endpoint take a dependency on an IModelBuilder.

public class ModemEndpoint
{
	private readonly IModelBuilder _builder;
	private readonly IModemRepository _repository;

	public ModemEndpoint(IModelBuilder builder, IModemRepository repository)
	{
		_builder = builder;
		_repository = repository;
	}

	public Modem get_custom_modems_Id(ShowModemRequest request)
	{
		return _builder.GetOne("modem", request.Id);
	}

	//... other endpoint action(s)
}

This action will ask the builder to retrieve a Modem by the unique id field which we specified in the map.

Beware of 404s

Note if the requested ID does not match any modems in the database the model builder will return null and in FubuMVC if you return null for an actions result the default behavior is to return a 404 HTTP Status (Not Found.)

Demonstration

Create some modems using SQL

INSERT INTO table_modem VALUES(1, 'Smart Modem 1200', 'Hayes Compatible', 'active', 'akebono.stanford.edu', 1)
INSERT INTO table_modem VALUES(2, 'US Robotics 2400', 'USR', 'active', 'wilson.domain.com', 1)
INSERT INTO table_modem VALUES(3, 'Zoom 300', 'Hayes Compatible', 'active', 'xyzzy.com', 1)

Simulate an AJAX request using Advanced REST Client.

http://localhost:25120/custom/modems/1

Building Many Modems

You are not limited to returning one object from your projections. You can easily return all the modems in the system using GetAll

var allModems = _builder.GetAll("modem");
var tenModems = _builder.GetAll("modem", 10);

An array of modems is returned. You almost never want get all of something or even a limited number of them with out a filter.

Get Modems using a Filter

You are not limited to filtering your model map projects by only unique Ids. You can filter by any fields on the root table of your map.

The Get method of the builder accepts a filter expression which creates a Filter used by the underlying Dovetail SDK Generic. To construct a filter you'll need to know the database field name, an operator and a value to filter by.

var hayesModems = _builder.Get("modem", f=>f.StartsWith("device_name", 'hayes'))

In this example we are getting all modems whose 'device_name' field has a string that starts with hayes.

There are many different filter operators you can use to compose the collection of models you are looking for.

Details... Details...

What is going on under the hood here is that a ClarifyGeneric is created for the map's root table and also for all relations being traversed. When a filter is applied by the builder it is applied to the root table. Because of this you can only filter by fields found on the root table's schema. If you just have to filter on a field on a different table you'll likely want to use a view type="view" as your map's root which contains all the fields you need.

Homework

Create an endpoint that returns Modems for hosts at a given TLD domain.

Exercise this new endpoint using your favorite tool and send a screenshot of the response.

Clone this wiki locally