using System.Collections.Generic; using System.Dynamic; using System.Linq; using System.Text.Json; using System.Threading.Tasks; using ChaosBot.Models; using ChaosBot.WebServer.Services; using FlexLabs.EntityFrameworkCore.Upsert; using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; using Microsoft.Net.Http.Headers; using Newtonsoft.Json; namespace ChaosBot.WebServer.App.ApiControllers { public abstract class BaseApiController : Controller where T : class, new() { protected readonly AccessTokenCache AccessTokenCache; protected readonly ValidationService ValidationService; protected BaseApiController( AccessTokenCache accessTokenCache, ValidationService validationService ) { AccessTokenCache = accessTokenCache; ValidationService = validationService; } public async Task Index(ulong guildId) { if (!CheckPermissions.GetResult(AccessTokenCache, Request, guildId, out IActionResult result)) return result; await using ChaosbotContext dbContext = new ChaosbotContext(); IQueryable query = GetBasicQuery(dbContext); List list = ApplyFilterForCurrentGuild(query, guildId).ToList(); LoggingFacade.Info("Received request"); List response = list.Select(element => { Dictionary responseDict = new Dictionary(); foreach (string field in GetIndexFields()) { responseDict.Add(field, element.GetType().GetProperty(field)?.GetValue(element, null)); } dynamic dynamicResponseElement = new DynamicResponse(responseDict); return dynamicResponseElement; }).ToList(); return Content(JsonConvert.SerializeObject(response), new MediaTypeHeaderValue("application/json")); } public async Task Upsert(ulong guildId, JsonElement requestBody) { if (!CheckPermissions.GetResult(AccessTokenCache, Request, guildId, out IActionResult result)) return result; if (!ValidationService.Validate(requestBody, GetValidationRules(), out string errors)) return BadRequest(errors); await using ChaosbotContext dbContext = new ChaosbotContext(); T databaseObject = new T(); foreach (string key in GetValidationRules().Keys) { databaseObject .GetType() .GetProperty(key) ?.SetValue(databaseObject, requestBody.GetType().GetProperty(key)?.GetValue(requestBody, null)); } await ApplyFilterForUpsert(GetBasicQuery(dbContext).Upsert(databaseObject)).RunAsync(); await dbContext.SaveChangesAsync(); return NoContent(); } public async Task Delete(ulong guildId, TDeleteParameter deleteParameter) { if (!CheckPermissions.GetResult(AccessTokenCache, Request, guildId, out IActionResult result)) return result; await using ChaosbotContext dbContext = new ChaosbotContext(); IQueryable query = GetBasicQuery(dbContext); List toDelete = FilterQueryMultipleForDeletion(query, guildId, deleteParameter); toDelete.Add(FilterQueryForDeletion(query, guildId, deleteParameter)); if (toDelete.Count == 0) return NotFound(); foreach (T obj in toDelete) { GetBasicQuery(dbContext).Remove(obj); } await dbContext.SaveChangesAsync(); return NoContent(); } protected abstract DbSet GetBasicQuery(ChaosbotContext context); protected abstract IQueryable ApplyFilterForCurrentGuild(IQueryable query, ulong guildId); protected abstract List GetIndexFields(); protected abstract Dictionary> GetValidationRules(); protected abstract UpsertCommandBuilder ApplyFilterForUpsert(UpsertCommandBuilder builder); protected abstract T FilterQueryForDeletion(IQueryable query, ulong guildId, TDeleteParameter deleteParameter); protected abstract List FilterQueryMultipleForDeletion(IQueryable query, ulong guildId, TDeleteParameter deleteParameter); private class DynamicResponse : DynamicObject { private readonly Dictionary _properties; public DynamicResponse(Dictionary properties) { _properties = properties; } public override IEnumerable GetDynamicMemberNames() { return _properties.Keys; } public override bool TryGetMember(GetMemberBinder binder, out object result) { if (_properties.ContainsKey(binder.Name)) { result = _properties[binder.Name]; return true; } result = null; return false; } public override bool TrySetMember(SetMemberBinder binder, object value) { if (_properties.ContainsKey(binder.Name)) { _properties[binder.Name] = value; return true; } return false; } } } }