Exemplo n.º 1
0
        /// <summary>
        /// Updates a location using a <see cref="Delta"/> object.
        /// </summary>
        /// <param name="id">ID of the location to be updated.</param>
        /// <param name="delta">
        /// Delta containing a list of location properties.  Web Api does the magic of converting the JSON to
        /// a delta.
        /// </param>
        /// <returns>
        /// An asynchronous task result containing information needed to create an API response message.
        /// </returns>
        public override async Task <CommandResult <LocationBaseDto, Guid> > Update(Guid id, Delta <LocationBaseDto> delta)
        {
            // Thread.CurrentPrincipal is not available in the constrtor.  Do not try and move this
            var uid = GetCurrentUser();

            // User ID should always be available, but if not ...
            if (!uid.HasValue)
            {
                return(Command.Error <LocationBaseDto>(GeneralErrorCodes.TokenInvalid("UserId")));
            }

            if (delta == null)
            {
                return(Command.Error <LocationBaseDto>(EntityErrorCode.EntityFormatIsInvalid));
            }

            var location = await _context.GetLocationsForUser(uid.Value)
                           .SingleOrDefaultAsync(l => l.Id == id)
                           .ConfigureAwait(false);

            if (location == null)
            {
                return(Command.Error <LocationBaseDto>(EntityErrorCode.EntityNotFound));
            }

            var locationDto = _mapper.Map(location, new LocationBaseDto());

            delta.Patch(locationDto);

            var validationResponse = ValidatorUpdate.Validate(locationDto);

            if (locationDto.ParentId.HasValue)
            {
                var existingTask = _context.Locations.AnyAsync(l => l.Id == locationDto.ParentId.Value);

                if (!existingTask.Result)
                {
                    validationResponse.FFErrors.Add(ValidationErrorCode.ForeignKeyValueDoesNotExist(nameof(Location.ParentId)));
                }
                else
                {
                    // Check for circular references
                    if (await LocationValidator.IsCircularReference(_context.Locations, locationDto, id))
                    {
                        validationResponse.FFErrors.Add(ValidationErrorCode.CircularReferenceNotAllowed(nameof(Location.ParentId)));
                    }
                }
            }

            // Check that Location Type exists
            if (locationDto.LocationTypeId != Guid.Empty && !_context.LocationTypes.Any(lt => lt.Id == locationDto.LocationTypeId))
            {
                validationResponse.FFErrors.Add(ValidationErrorCode.ForeignKeyValueDoesNotExist(nameof(Location.LocationTypeId)));
            }

            // Including the original Id in the Patch request will not return an error but attempting to change the Id is not allowed.
            if (locationDto.Id != id)
            {
                validationResponse.FFErrors.Add(ValidationErrorCode.EntityIDUpdateNotAllowed("Id"));
            }

            // Check that unique fields are still unique
            if (_context.Locations.Any(l => l.Id != id && l.Name == locationDto.Name))
            {
                validationResponse.FFErrors.Add(ValidationErrorCode.EntityPropertyDuplicateNotAllowed(nameof(Location.Name)));
            }

            if (validationResponse.IsInvalid)
            {
                return(Command.Error <LocationBaseDto>(validationResponse));
            }

            _context.Locations.Attach(location);
            _mapper.Map(locationDto, location);

            location.SetAuditFieldsOnUpdate(uid.Value);

            await _context.SaveChangesAsync().ConfigureAwait(false);

            return(Command.NoContent <LocationBaseDto>());
        }