public async Task <IActionResult> ModifyCharacter(Guid characterId, CharacterModification model, bool updateSockets = true) { if (!ModelState.IsValid) { return(BadRequest(ModelState)); } string userId = users.GetUserId(); try { bool changed = await games.ModifyCharacter(characterId, model, userId); if (updateSockets && changed) { var sheet = await games.GetCharacter(characterId, userId); await sockets.SendCharacter(sheet); } return(NoContent()); } catch (UnauthorizedException e) { logger.LogInformation(0, e, $"User {userId} tried to modify {characterId} but was not authorized to do so"); return(Forbid()); } catch (DataNotFoundException e) { logger.LogInformation(0, e, $"Could not modify non-existent character {characterId}"); return(NotFound()); } }
private CharacterModification LoadModification(XElement el) { CharacterModification mod = new CharacterModification(); IEnumerable <XElement> nodes = el.Descendants(); foreach (XElement xe in nodes) { switch (xe.Name.LocalName) { case "Type": if (Enum.TryParse((string)xe, out ModType mt)) { mod.Type = mt; } break; case "Mode": if (Enum.TryParse((string)xe, out ModMode mm)) { mod.Mode = mm; } break; case "Property": mod.Property = (string)xe; break; case "Options": mod.OptionsList = new List <string>(); IEnumerable <XElement> optionsNodes = xe.Descendants(); foreach (XElement xeo in optionsNodes) { mod.OptionsList.Add((string)xeo); } break; case "LevelTable": mod.LevelTable = LoadLevelTable(xe); break; case "Value": mod.Value = (double)xe; break; case "DieSize": if (Enum.TryParse((string)xe, out DieSize ds)) { mod.DieSize = ds; } break; } } return(mod); }
/// <summary> /// Modifies a character with any CharacterModification fields that are not null /// </summary> /// <param name="characterId">The character ID of the character to update</param> /// <param name="model">The parameters to update</param> /// <param name="userId">Ensures that the given user is allowed to modify the character</param> /// <param name="shouldValidate">Whether or not to explicitly validate the DataAnnotations of the model</param> /// <exception cref="ArgumentNullException">If model is empty</exception> /// <exception cref="ValidationException">If shouldValidate = true and model is invalid</exception> /// <exception cref="UnauthorizedException">If the character modification is valid but the user is not allowed to make that modification</exception> /// <returns>True if the any properties were changed, false otherwise</returns> public async Task <bool> ModifyCharacter(Guid characterId, CharacterModification model, string userId, bool shouldValidate = false) { if (model == null || string.IsNullOrWhiteSpace(userId)) { throw new ArgumentNullException(nameof(model), "The modifications and user ID must not be null"); } if (shouldValidate) { Validator.ValidateObject(model, new ValidationContext(model), true); } using (var db = new DatabaseContext(options)) { var character = await db.Characters.SingleOrDefaultAsync(c => c.CharacterId == characterId); if (character == null) { throw new DataNotFoundException("Could not find the character given by CharacterId"); } if (character.UserId != userId) { throw new UnauthorizedException($"User {userId} is not the owner of character {character.CharacterId}"); } // update properties only if they are not null character.Name = model.Name ?? character.Name; character.Height = model.Height ?? character.Height; character.Weight = model.Weight ?? character.Weight; character.HairColor = model.HairColor ?? character.HairColor; character.EyeColor = model.EyeColor ?? character.EyeColor; character.BaseStrength = model.Strength ?? character.BaseStrength; character.BaseDexterity = model.Dexterity ?? character.BaseDexterity; character.BaseMind = model.Mind ?? character.BaseMind; character.Level = model.Level ?? character.Level; int changes = await db.SaveChangesAsync(); return(changes == 1); } }
public async Task Success() { // Arrange var options = DatabaseSetup.CreateContextOptions(); const string userId = "The Best ID"; const string hairColor = "Brown"; var character = new Character { BaseStrength = 10, BaseDexterity = 10, BaseMind = 10, Class = Micro20ClassType.Cleric, Race = Micro20RaceType.Dwarf, Level = 1, UserId = userId }; bool?result = null; Type eType = null; using (var db = new DatabaseContext(options)) { db.Characters.Add(character); int changes = await db.SaveChangesAsync(); Assert.Equal(1, changes); } // Act var games = new GameService(options); try { var m1 = new CharacterModification { Dexterity = 12 }; result = await games.ModifyCharacter(character.CharacterId, m1, userId, true); Assert.True(result); var m2 = new CharacterModification { HairColor = hairColor }; result = await games.ModifyCharacter(character.CharacterId, m2, userId, true); Assert.True(result); } catch (Exception e) when(e is DataNotFoundException || e is ValidationException) { eType = e.GetType(); } // Assert Assert.NotNull(result); Assert.Null(eType); using (var db = new DatabaseContext(options)) { Assert.Equal(1, db.Characters.Count()); var dbCharacter = await db.Characters.SingleAsync(); Assert.Equal(hairColor, dbCharacter.HairColor); Assert.Equal(10, dbCharacter.BaseStrength); Assert.Equal(12, dbCharacter.BaseDexterity); Assert.Equal(10, dbCharacter.BaseMind); Assert.Equal(Micro20RaceType.Dwarf, dbCharacter.Race); Assert.Equal(Micro20ClassType.Cleric, dbCharacter.Class); Assert.Equal(userId, dbCharacter.UserId); Assert.Equal(1, dbCharacter.Level); } }