Awesome
Test Data
Install Core
Install-Package TestData.Interface
Web Api Support
Install-Package TestData.Interface.Web
MediatR Support
Install-Package TestData.Interface.MediatR
Csv (FileSets) Support
Install-Package TestData.Interface.Files
Angular front end
This is designed to work with the webapi TestDataController
which uses the attribute route prefix /api/testdata
. However you could use any backend provided it accepts and returns data in the expected format. The test data UI is served on the client route: /testdata
.
bower install ng-test-data --save
TestDataController returns a list of datasets in this format. It accepts a get
request with no parameters.
descriptors.Select(d => new
{
Dependencies = d.Dependencies.Select(t => t.FullName),
d.Name,
d.Description,
d.Type.FullName,
TypeName = d.Type.Name,
Properties = d.Properties.Select(p => new
{
FieldName = p.MemberInfo.Name,
p.Property.Name,
p.Property.Description,
p.Property.DataType,
p.Property.Required
})
}
DataSet request payloads should match below. It makes a post
request.
public interface IDataSetRequest
{
string DataSet { get; }
IDictionary<string, IDictionary<string, string>> Properties { get; }
}
Configuring your angular application
You will need to require the module test-data
and register a constant called apiBase
. Do not include a trailing forward slash. This is the base path used to make the request e.g. /api
becomes /api/testdata
.
angular.module('app', ['ngTestData'])
.constant('apiBase', '/api');
Configuring for AutoFac and MediatR
//Assemblies = AppDomain.CurrentDomain.GetAssemblies().Where(a => a.GetName().Name.StartsWith("demo") || a.GetName().Name.StartsWith("TestData")).ToArray();
void RegisterTestData(ContainerBuilder builder)
{
builder.RegisterApiControllers(typeof(TestDataController).Assembly);
builder.RegisterTypes(Assemblies.SelectMany(a => a.GetTypes()).Where(x => !x.IsAbstract && typeof(IDataSet).IsAssignableFrom(x)).ToArray());
builder
.Register<Func<Type, IDataSet>>(x =>
{
var scope = x.Resolve<ILifetimeScope>();
return (t) => scope.Resolve(t) as IDataSet;
})
.InstancePerRequest();
builder
.Register(x =>
{
var mediator = x.Resolve<IMediator>();
var dispatcher = new MediatRDispatcher(mediator);
return dispatcher;
})
.As<IDispatcher>()
.InstancePerRequest();
}
DataSets
The string returned from a dataset is returned to the client (to be used as feedback).
Sample DataSet
This example is a bit contrived but demonstrates specifying a property and making use of it.
[DataSet("Cities", "A list of cities")]
public class CityDataSet : IDataSet
{
private readonly IDbContext _dbContext;
public CityDataSet(IDbContext dbContext)
{
_dbContext = dbContext;
}
[DataSetProperty("Prefix", DataType.String, "Prefix the city name")]
public string Prefix { get; set; }
public async Task<string> Execute()
{
var city = new City
{
Name = Prefix + " Melbourne"
};
var cities = _dbContext.Set<City>();
cities.Add(city);
await _dbContext.SaveChangesAsync();
return "Inserted 1 City";
}
}
This example shows reading data from a CSV. The csv path is relative to the assembly containing the DataSet.
[DataSet("States", "A list of states")]
[DataSetDependency(typeof(CityDataSet))]
public class StateDataSet : IDataSet
{
private readonly IDbSet<State> _states;
public StateDataSet(IDbSet<State> states)
{
_states = states;
}
[DataSetProperty("Starts With", DataType.String, "Filter the states inserted", Required = true)]
public string StartsWith { get; set; }
[DataSetProperty("Created On", DataType.Date, "Date the states were created on")]
public DateTime CreatedOn { get; set; }
public async Task<string> Execute()
{
int count = 0;
States.ForEach(stateRecord =>
{
if (StartsWith != null && !stateRecord.Name.StartsWith(StartsWith))
{
return;
}
var state = new State
{
Name = stateRecord.Name,
CreatedOn = CreatedOn
};
count ++;
states.Add(state);
}, this);
return String.Format("Inserted {0} States", count);
}
[FileDataSet("./data/states.csv")]
public FileDataSetInstance<State> States { get; set; }
}