/// <exception cref="ArgumentNullException">The specified arguments are null</exception> /// <exception cref="ArgumentException">A feature with the specified name already exists</exception> /// <exception cref="ResourceNotFoundException{Feature}">The referenced parent feature does not exist</exception> public Feature CreateFeature(FeatureArgs args) { if (args == null) { throw new ArgumentNullException(nameof(args)); } if (Db.Features.Any(f => f.Name == args.Name)) { throw new ArgumentException($"A feature with name '{args.Name}' already exists"); } var feature = new Feature { Name = args.Name }; if (args.Parent != null) { feature.Parent = GetFeature(args.Parent.Value); if (feature.Parent == null) { throw new ResourceNotFoundException <Feature>(args.Parent); } } Db.Features.Add(feature); Db.SaveChanges(); return(feature); }
public IActionResult Create([FromBody] FeatureArgs args) { if (!UserPermissions.IsAllowedToAdminister(User.Identity)) { return(Forbid()); } if (!ModelState.IsValid) { return(BadRequest(ModelState)); } try { var feature = _manager.CreateFeature(args); return(Created($"{Request.Scheme}://{Request.Host}/api/Features/{feature.Id}", feature.Id)); } catch (ResourceNotFoundException <Feature> e) { return(StatusCode(422, e.Message)); // invalid parent feature ID } catch (ArgumentException e) { return(StatusCode(409, e.Message)); // feature name already in use } }
public IActionResult Update(int featureId, [FromBody] FeatureArgs args) { if (!UserPermissions.IsAllowedToAdminister(User.Identity)) { return(Forbid()); } if (!ModelState.IsValid) { return(BadRequest(ModelState)); } try { _manager.UpdateFeature(featureId, args); return(NoContent()); } catch (ArgumentException e) { return(StatusCode(409, e.Message)); // new feature name already in use } catch (ResourceNotFoundException <Feature> e) when((int?)e.Keys.FirstOrDefault() == featureId) { return(NotFound(e.Message)); // feature to be updated does not exist } catch (ResourceNotFoundException <Feature> e) { return(StatusCode(422, e.Message)); // referenced parent feature does not exist } catch (InvalidOperationException e) { return(StatusCode(409, e.Message)); // invalid parent modification } }
/// <summary> /// Updates a feature. If the reference to the parent is modified, this moves the whole subtree of features. /// </summary> /// <exception cref="ArgumentNullException">The specified arguments are null</exception> /// <exception cref="ArgumentException">The new feature name is already in use</exception> /// <exception cref="ResourceNotFoundException{Feature}">There is no feature with the specified ID</exception> /// <exception cref="InvalidOperationException">Parent modification causes a cycle (destroys tree structure)</exception> public void UpdateFeature(int featureId, FeatureArgs args) { if (args == null) { throw new ArgumentNullException(nameof(args)); } if (Db.Features.Any(f => f.Name == args.Name && f.Id != featureId)) { throw new ArgumentException($"A feature with name '{args.Name}' already exists"); } var feature = GetFeature(featureId, loadParent: true, loadChildren: true, loadGroups: true); if (feature == null) { throw new ResourceNotFoundException <Feature>(featureId); } var newParent = args.Parent.HasValue ? GetFeature(args.Parent.Value, loadChildren: true) : null; if (newParent == null && args.Parent.HasValue) { throw new ResourceNotFoundException <Feature>(args.Parent.Value); } // ensure that the new parent is not a descendant of or equal to the feature to be updated // (this would create a cycle destroying the tree structure) if (IsDescendantOrEqual(newParent, feature)) { if (args.Parent != null) { throw new InvalidOperationException($"Changing the parent of '{featureId}' to '{args.Parent.Value}' would destroy the tree structure because '{args.Parent.Value}' is a descendant of or the same as '{featureId}'"); } } // 1) update name feature.Name = args.Name; // 2) detach from old parent feature.Parent?.Children.Remove(feature); // 3) attach to new parent newParent?.Children.Add(feature); feature.Parent = newParent; Db.SaveChanges(); }