diff --git a/ChaosBot/WebServer/App/ApiControllers/BaseApiController.cs b/ChaosBot/WebServer/App/ApiControllers/BaseApiController.cs new file mode 100644 index 0000000..38b96ca --- /dev/null +++ b/ChaosBot/WebServer/App/ApiControllers/BaseApiController.cs @@ -0,0 +1,145 @@ +using System.Collections.Generic; +using System.Dynamic; +using System.Linq; +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 BaseApiController( + AccessTokenCache accessTokenCache + ) + { + AccessTokenCache = accessTokenCache; + } + + 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, dynamic requestBody) + { + if (!CheckPermissions.GetResult(AccessTokenCache, Request, guildId, out IActionResult result)) + return result; + + 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; + } + } + } +} diff --git a/ChaosBot/WebServer/App/ApiControllers/CustomCommandController.cs b/ChaosBot/WebServer/App/ApiControllers/CustomCommandController.cs new file mode 100644 index 0000000..14d1a8b --- /dev/null +++ b/ChaosBot/WebServer/App/ApiControllers/CustomCommandController.cs @@ -0,0 +1,89 @@ +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using ChaosBot.Models; +using ChaosBot.WebServer.Models; +using ChaosBot.WebServer.Services; +using FlexLabs.EntityFrameworkCore.Upsert; +using Microsoft.AspNetCore.Mvc; +using Microsoft.EntityFrameworkCore; + +namespace ChaosBot.WebServer.App.ApiControllers +{ + [ApiController] + [Route("/api/custom-commands")] + public class CustomCommandController : BaseApiController + { + public CustomCommandController( + AccessTokenCache accessTokenCache + ) : base( + accessTokenCache + ) {} + + [HttpGet] + [Route("{guildId}")] + public async Task IndexAction(ulong guildId) + { + return await Index(guildId); + } + + [HttpPost] + [Route("{guildId}")] + public async Task UpsertAction( + [FromRoute] ulong guildId, + [FromBody] dynamic requestBody) + { + return await Upsert(guildId, requestBody); + } + + [HttpDelete] + [Route("{guildId}/{command}")] + public async Task DeleteAction([FromRoute]ulong guildId, [FromRoute]string command) + { + return await Delete(guildId, command); + } + + protected override DbSet GetBasicQuery(ChaosbotContext context) + { + return context.CustomCommands; + } + + protected override IQueryable ApplyFilterForCurrentGuild(IQueryable query, ulong guildId) + { + return query.Where(e => e.DiscordGuildId == guildId); + } + + protected override List GetIndexFields() { + return new List + { + "Command", + "Type", + "Content" + }; + } + + protected override Dictionary> GetValidationRules() + { + return new Dictionary>(); + } + + protected override UpsertCommandBuilder ApplyFilterForUpsert( + UpsertCommandBuilder builder) + { + return builder.On(cc => new {cc.DiscordGuildId, cc.Command}); + } + + protected override CustomCommand FilterQueryForDeletion(IQueryable query, ulong guildId, string deleteParameter) + { + return query + .Where(cc => cc.DiscordGuildId == guildId) + .First(cc => cc.Command == deleteParameter) + ; + } + + protected override List FilterQueryMultipleForDeletion(IQueryable query, ulong guildId, string deleteParameter) + { + return new List(); + } + } +} diff --git a/ChaosBot/WebServer/App/CustomCommandController.cs b/ChaosBot/WebServer/App/CustomCommandController.cs deleted file mode 100644 index 5e4c638..0000000 --- a/ChaosBot/WebServer/App/CustomCommandController.cs +++ /dev/null @@ -1,93 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using ChaosBot.Discord; -using ChaosBot.Models; -using ChaosBot.WebServer.Models; -using ChaosBot.WebServer.Services; -using Discord.WebSocket; -using Microsoft.AspNetCore.Mvc; -using Microsoft.EntityFrameworkCore; - -namespace ChaosBot.WebServer.App -{ - [ApiController] - [Route("/api/custom-commands")] - public class CustomCommandController : Controller - { - private readonly AccessTokenCache _accessTokenCache; - - public CustomCommandController( - AccessTokenCache accessTokenCache - ) - { - _accessTokenCache = accessTokenCache; - } - - [HttpGet] - [Route("{guildId}")] - public async Task GetCustomCommands([FromRoute]ulong guildId) - { - if (!CheckPermissions.GetResult(_accessTokenCache, Request, guildId, out IActionResult result)) - return result; - - await using ChaosbotContext dbContext = new ChaosbotContext(); - - IQueryable customCommandsQuery = dbContext.CustomCommands; - List customCommands = customCommandsQuery - .Where(cc => cc.DiscordGuildId == guildId) - .ToList(); - - List response = customCommands.Select(e => new CustomCommandResponse(e)).ToList(); - - return Json(response); - } - - [HttpPost] - [Route("{guildId}")] - public async Task UpsertCustomCommands([FromRoute]ulong guildId, [FromBody]CustomCommandRequest customCommandRequest) - { - if (!CheckPermissions.GetResult(_accessTokenCache, Request, guildId, out IActionResult result)) - return result; - - await using ChaosbotContext dbContext = new ChaosbotContext(); - - CustomCommand customCommand = new CustomCommand - { - DiscordGuildId = guildId, - Command = customCommandRequest.Command, - Type = customCommandRequest.Type, - Content = customCommandRequest.Content - }; - await dbContext.CustomCommands.Upsert(customCommand) - .On(cc => new {cc.DiscordGuildId, cc.Command}).RunAsync(); - await dbContext.SaveChangesAsync(); - - return NoContent(); - } - - [HttpDelete] - [Route("{guildId}/{command}")] - public async Task DeleteCustomCommands([FromRoute]ulong guildId, [FromRoute]string command) - { - if (!CheckPermissions.GetResult(_accessTokenCache, Request, guildId, out IActionResult result)) - return result; - - await using ChaosbotContext dbContext = new ChaosbotContext(); - - IQueryable customCommandQuery = dbContext.CustomCommands; - CustomCommand customCommand = customCommandQuery - .Where(cc => cc.DiscordGuildId == guildId) - .First(cc => cc.Command == command); - - if (customCommand == null) - return NotFound(); - - dbContext.CustomCommands.Remove(customCommand); - await dbContext.SaveChangesAsync(); - - return NoContent(); - } - } -}