vgquotes/Src/VideoGameQuotes.Web/Controllers/ApiController.cs

172 lines
6.4 KiB
C#
Raw Normal View History

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Web.Mvc;
using Portoa.Persistence;
using Portoa.Web.Controllers;
using Portoa.Web.Results;
using VideoGameQuotes.Api;
using VideoGameQuotes.Web.Models;
namespace VideoGameQuotes.Web.Controllers {
public interface IApiService {
IEnumerable<GameDto> GetGames(ApiModel model);
IEnumerable<SystemDto> GetSystems(ApiModel model);
IEnumerable<CategoryDto> GetCategories(ApiModel model);
IEnumerable<PublisherDto> GetPublishers(ApiModel model);
IEnumerable<QuoteDto> GetQuotes(ApiModel model);
}
public class ApiService : IApiService {
private readonly IRepository<Game> gameRepository;
private readonly IRepository<GamingSystem> systemRepository;
private readonly IRepository<Category> categoryRepository;
private readonly IRepository<Publisher> publisherRepository;
private readonly IRepository<Quote> quoteRepository;
private readonly IDictionary<string, CriterionHandler<Category>> categoryHandlers = new Dictionary<string, CriterionHandler<Category>>();
private readonly IDictionary<string, CriterionHandler<GamingSystem>> systemHandlers = new Dictionary<string, CriterionHandler<GamingSystem>>();
private readonly IDictionary<string, CriterionHandler<Publisher>> publisherHandlers = new Dictionary<string, CriterionHandler<Publisher>>();
private readonly IDictionary<string, CriterionHandler<Quote>> quoteHandlers = new Dictionary<string, CriterionHandler<Quote>> {
{ "game", new Quote.GameCriterionHandler() }
};
private readonly IDictionary<string, CriterionHandler<Game>> gameHandlers = new Dictionary<string, CriterionHandler<Game>> {
{ "system", new Game.SystemCriterionHandler() },
{ "publisher", new Game.PublisherCriterionHandler() }
};
public ApiService(
IRepository<Game> gameRepository,
IRepository<GamingSystem> systemRepository,
IRepository<Category> categoryRepository,
IRepository<Publisher> publisherRepository,
IRepository<Quote> quoteRepository
) {
this.gameRepository = gameRepository;
this.systemRepository = systemRepository;
this.categoryRepository = categoryRepository;
this.publisherRepository = publisherRepository;
this.quoteRepository = quoteRepository;
}
private static IEnumerable<T> SetSortMethod<T, TKey>(IEnumerable<T> records, SortMethod sortMethod, SortOrder sortOrder, Func<T, TKey> propertySelector) where T : Entity<T, int> {
switch (sortMethod) {
case SortMethod.Alphabetical:
return sortOrder == SortOrder.Descending ? records.OrderByDescending(propertySelector) : records.OrderBy(propertySelector);
case SortMethod.Date:
return sortOrder == SortOrder.Descending ? records.OrderByDescending(propertySelector) : records.OrderBy(propertySelector);
default:
return records;
}
}
private static IEnumerable<TDto> GetRecords<T, TDto>(
ApiModel model,
IRepository<T> repository,
Func<T, object> sortSelector,
IDictionary<string,
CriterionHandler<T>> criterionHandlers
)
where T : Entity<T, int>
where TDto : new() {
IEnumerable<T> records;
if (model.FetchAll) {
records = SetSortMethod(repository.Records, model.SortMethod, model.SortOrder, sortSelector);
var expressionBuilder = new List<Func<T, bool>>();
foreach (var kvp in model.Criteria) {
if (!criterionHandlers.ContainsKey(kvp.Key)) {
throw new ApiException(string.Format("Unknown criterion: \"{0}\"", kvp.Key));
}
expressionBuilder.AddRange(criterionHandlers[kvp.Key].HandleCriterion(kvp.Value));
}
if (expressionBuilder.Count > 0) {
records = records.Where(game => expressionBuilder.Aggregate(false, (current, next) => current || next(game)));
}
} else {
records = new[] { repository.FindById(model.Id) };
}
return records.ToArray().Select(entity => entity.ToDto<TDto>());
}
[UnitOfWork]
public IEnumerable<GameDto> GetGames(ApiModel model) {
return GetRecords<Game, GameDto>(model, gameRepository, game => game.Name, gameHandlers);
}
[UnitOfWork]
public IEnumerable<SystemDto> GetSystems(ApiModel model) {
return GetRecords<GamingSystem, SystemDto>(model, systemRepository, system => system.Name, systemHandlers);
}
[UnitOfWork]
public IEnumerable<CategoryDto> GetCategories(ApiModel model) {
return GetRecords<Category, CategoryDto>(model, categoryRepository, category => category.Name, categoryHandlers);
}
[UnitOfWork]
public IEnumerable<PublisherDto> GetPublishers(ApiModel model) {
return GetRecords<Publisher, PublisherDto>(model, publisherRepository, publisher => publisher.Name, publisherHandlers);
}
[UnitOfWork]
public IEnumerable<QuoteDto> GetQuotes(ApiModel model) {
return GetRecords<Quote, QuoteDto>(model, quoteRepository, quote => quote.Id, quoteHandlers);
}
}
public class ApiController : Controller {
private readonly IApiService apiService;
public ApiController(IApiService apiService) {
this.apiService = apiService;
}
protected new JsonResult Json(object data) {
return Json(data, JsonRequestBehavior.AllowGet);
}
private ActionResult Error(HttpStatusCode statusCode, string message = null) {
return new StatusOverrideResult(Json(this.CreateJsonErrorResponse(message ?? "Invalid request"))) { StatusCode = statusCode };
}
private ActionResult GetRecords<TDto>(Func<IApiService, IEnumerable<TDto>> recordGetter) {
if (!ModelState.IsValid) {
return Error(HttpStatusCode.BadRequest);
}
try {
return Json(this.CreateJsonResponse(data: new { records = recordGetter(apiService) }));
} catch (ApiException e) {
return Error(HttpStatusCode.BadRequest, e.Message);
} catch {
return Error(HttpStatusCode.InternalServerError, "An error occurred while trying to fulfill your stupid request");
}
}
public ActionResult Game(ApiModel model) {
return GetRecords(service => service.GetGames(model));
}
public ActionResult System(ApiModel model) {
return GetRecords(service => service.GetSystems(model));
}
public ActionResult Category(ApiModel model) {
return GetRecords(service => service.GetCategories(model));
}
public ActionResult Publisher(ApiModel model) {
return GetRecords(service => service.GetPublishers(model));
}
public ActionResult Quote(ApiModel model) {
return GetRecords(service => service.GetQuotes(model));
}
}
}