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 GetGames(ApiModel model); IEnumerable GetSystems(ApiModel model); IEnumerable GetCategories(ApiModel model); IEnumerable GetPublishers(ApiModel model); IEnumerable GetQuotes(ApiModel model); } public class ApiService : IApiService { private readonly IRepository gameRepository; private readonly IRepository systemRepository; private readonly IRepository categoryRepository; private readonly IRepository publisherRepository; private readonly IRepository quoteRepository; private readonly IDictionary> categoryHandlers = new Dictionary>(); private readonly IDictionary> systemHandlers = new Dictionary>(); private readonly IDictionary> publisherHandlers = new Dictionary>(); private readonly IDictionary> quoteHandlers = new Dictionary> { { "game", new Quote.GameCriterionHandler() } }; private readonly IDictionary> gameHandlers = new Dictionary> { { "system", new Game.SystemCriterionHandler() }, { "publisher", new Game.PublisherCriterionHandler() } }; public ApiService( IRepository gameRepository, IRepository systemRepository, IRepository categoryRepository, IRepository publisherRepository, IRepository quoteRepository ) { this.gameRepository = gameRepository; this.systemRepository = systemRepository; this.categoryRepository = categoryRepository; this.publisherRepository = publisherRepository; this.quoteRepository = quoteRepository; } private static IEnumerable SetSortMethod(IEnumerable records, SortMethod sortMethod, SortOrder sortOrder, Func propertySelector) where T : Entity { 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 GetRecords( ApiModel model, IRepository repository, Func sortSelector, IDictionary> criterionHandlers ) where T : Entity where TDto : new() { IEnumerable records; if (model.FetchAll) { records = SetSortMethod(repository.Records, model.SortMethod, model.SortOrder, sortSelector); var expressionBuilder = new List>(); 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()); } [UnitOfWork] public IEnumerable GetGames(ApiModel model) { return GetRecords(model, gameRepository, game => game.Name, gameHandlers); } [UnitOfWork] public IEnumerable GetSystems(ApiModel model) { return GetRecords(model, systemRepository, system => system.Name, systemHandlers); } [UnitOfWork] public IEnumerable GetCategories(ApiModel model) { return GetRecords(model, categoryRepository, category => category.Name, categoryHandlers); } [UnitOfWork] public IEnumerable GetPublishers(ApiModel model) { return GetRecords(model, publisherRepository, publisher => publisher.Name, publisherHandlers); } [UnitOfWork] public IEnumerable GetQuotes(ApiModel model) { return GetRecords(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(Func> recordGetter, string key) { if (!ModelState.IsValid) { return Error(HttpStatusCode.BadRequest); } try { return Json(this.CreateJsonResponse(data: new Dictionary { { key, 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), "games"); } public ActionResult System(ApiModel model) { return GetRecords(service => service.GetSystems(model), "systems"); } public ActionResult Category(ApiModel model) { return GetRecords(service => service.GetCategories(model), "categories"); } public ActionResult Publisher(ApiModel model) { return GetRecords(service => service.GetPublishers(model), "publishers"); } public ActionResult Quote(ApiModel model) { return GetRecords(service => service.GetQuotes(model), "quotes"); } } }