public void When_Passing_Null_Or_Empty_Parameter_To_GetTarget_Then_Exception_Is_Thrown()
        {
            // ARRANGE
            InitializeFakeObjects();

            // ACTS  & ASSERTS
            Assert.Throws <ArgumentNullException>(() => _filterParser.GetTarget(null));
            Assert.Throws <ArgumentNullException>(() => _filterParser.GetTarget(string.Empty));
        }
        public async Task <ApiActionResult> Execute(string id, JObject jObj, string schemaId, string locationPattern)
        {
            // 1. Check parameters.
            if (string.IsNullOrWhiteSpace(id))
            {
                throw new ArgumentNullException(nameof(id));
            }

            if (jObj == null)
            {
                throw new ArgumentNullException(nameof(jObj));
            }

            if (string.IsNullOrWhiteSpace(schemaId))
            {
                throw new ArgumentNullException(nameof(schemaId));
            }

            _parametersValidator.ValidateLocationPattern(locationPattern);

            // 2. Check representation exists
            var representation = await _representationStore.GetRepresentation(id);

            if (representation == null)
            {
                return(_apiResponseFactory.CreateError(
                           HttpStatusCode.NotFound,
                           string.Format(ErrorMessages.TheResourceDoesntExist, id)));
            }

            // 3. Get patch operations.
            ErrorResponse errorResponse;
            var           operations = _patchRequestParser.Parse(jObj, out errorResponse);

            if (operations == null)
            {
                return(_apiResponseFactory.CreateError(
                           (HttpStatusCode)errorResponse.Status,
                           errorResponse));
            }

            // 4. Process operations.
            foreach (var operation in operations)
            {
                // 4.1 Check path is filled-in.
                if (operation.Type == PatchOperations.remove &&
                    string.IsNullOrWhiteSpace(operation.Path))
                {
                    return(_apiResponseFactory.CreateError(
                               HttpStatusCode.BadRequest,
                               _errorResponseFactory.CreateError(ErrorMessages.ThePathNeedsToBeSpecified, HttpStatusCode.BadRequest, Common.Constants.ScimTypeValues.InvalidSyntax)));
                }

                // 4.2 Check value is filled-in.
                if ((operation.Type == PatchOperations.add || operation.Type == PatchOperations.replace) &&
                    operation.Value == null)
                {
                    return(_apiResponseFactory.CreateError(
                               HttpStatusCode.BadRequest,
                               _errorResponseFactory.CreateError(ErrorMessages.TheValueNeedsToBeSpecified, HttpStatusCode.BadRequest, Common.Constants.ScimTypeValues.InvalidSyntax)));
                }

                // 4.3 Process filter & get values.
                IEnumerable <RepresentationAttribute> attrs         = null;
                IEnumerable <RepresentationAttribute> filteredAttrs = null;
                if (!string.IsNullOrWhiteSpace(operation.Path))
                {
                    // 4.3.1 Process filter.
                    var filter   = _filterParser.Parse(operation.Path);
                    var filtered = filter.Evaluate(representation);
                    if (filtered == null || !filtered.Any())
                    {
                        return(_apiResponseFactory.CreateError(
                                   HttpStatusCode.BadRequest,
                                   _errorResponseFactory.CreateError(ErrorMessages.TheFilterIsNotCorrect, HttpStatusCode.BadRequest, Common.Constants.ScimTypeValues.InvalidFilter)
                                   ));
                    }

                    // 4.3.2 Get targeted attributes.
                    var target = _filterParser.GetTarget(operation.Path);
                    var filterRepresentation = _filterParser.Parse(target);
                    attrs = filterRepresentation.Evaluate(representation);

                    if (operation.Type == PatchOperations.remove)
                    {
                        // 4.3.3 If operation = remove then values are not retrieved.
                        filteredAttrs = filtered;
                    }
                    else
                    {
                        // 4.3.4 if operation = replace or add then retrieve values.
                        var name  = filtered.First().SchemaAttribute.Name;
                        var token = operation.Value.SelectToken(name);
                        if (token == null)
                        {
                            token       = new JObject();
                            token[name] = operation.Value;
                        }

                        var value = _jsonParser.GetRepresentation(token, filtered.First().SchemaAttribute, CheckStrategies.Standard);
                        if (!value.IsParsed)
                        {
                            return(_apiResponseFactory.CreateError(
                                       HttpStatusCode.BadRequest,
                                       _errorResponseFactory.CreateError(value.ErrorMessage, HttpStatusCode.BadRequest, Common.Constants.ScimTypeValues.InvalidSyntax)));
                        }
                        filteredAttrs = new[] { value.RepresentationAttribute };
                    }
                }

                // 4.4 If there's no filter then parse the value with the schema.
                if (filteredAttrs == null)
                {
                    if (operation.Value != null)
                    {
                        var repr = await _representationRequestParser.Parse(operation.Value, schemaId, CheckStrategies.Standard);

                        if (!repr.IsParsed)
                        {
                            return(_apiResponseFactory.CreateError(
                                       HttpStatusCode.BadRequest,
                                       _errorResponseFactory.CreateError(repr.ErrorMessage, HttpStatusCode.BadRequest, Common.Constants.ScimTypeValues.InvalidSyntax)));
                        }

                        filteredAttrs = repr.Representation.Attributes;
                        attrs         = representation.Attributes;
                    }
                }

                foreach (var filteredAttr in filteredAttrs)
                {
                    var attr = attrs.FirstOrDefault(a => a.SchemaAttribute.Name == filteredAttr.SchemaAttribute.Name);
                    // 4.5.1 Check mutability.
                    if (filteredAttr.SchemaAttribute.Mutability == Common.Constants.SchemaAttributeMutability.Immutable ||
                        filteredAttr.SchemaAttribute.Mutability == Common.Constants.SchemaAttributeMutability.ReadOnly)
                    {
                        return(_apiResponseFactory.CreateError(
                                   HttpStatusCode.BadRequest,
                                   _errorResponseFactory.CreateError(string.Format(ErrorMessages.TheImmutableAttributeCannotBeUpdated, filteredAttr.SchemaAttribute.Name), HttpStatusCode.BadRequest, Common.Constants.ScimTypeValues.Mutability)));
                    }

                    // 4.5.2 Check uniqueness
                    if (filteredAttr.SchemaAttribute.Uniqueness == Common.Constants.SchemaAttributeUniqueness.Server) // TH  : SELECT THE VALUE AND CHECK THE UNIQUENESS.
                    {
                        var filter      = _filterParser.Parse(filteredAttr.FullPath);
                        var uniqueAttrs = await _representationStore.SearchValues(representation.ResourceType, filter);

                        if (uniqueAttrs.Any())
                        {
                            if (uniqueAttrs.Any(a => a.CompareTo(filteredAttr) == 0))
                            {
                                return(_apiResponseFactory.CreateError(
                                           HttpStatusCode.BadRequest,
                                           _errorResponseFactory.CreateError(string.Format(ErrorMessages.TheAttributeMustBeUnique, filteredAttr.SchemaAttribute.Name), HttpStatusCode.BadRequest, Common.Constants.ScimTypeValues.Uniqueness)));
                            }
                        }
                    }

                    switch (operation.Type)
                    {
                    // 4.5.3.1 Remove attributes.
                    case PatchOperations.remove:
                        if (attr == null)
                        {
                            return(_apiResponseFactory.CreateError(
                                       HttpStatusCode.BadRequest,
                                       _errorResponseFactory.CreateError(string.Format(ErrorMessages.TheAttributeDoesntExist, filteredAttr.SchemaAttribute.Name), HttpStatusCode.BadRequest)
                                       ));
                        }

                        if (filteredAttr.SchemaAttribute.MultiValued)
                        {
                            // 4.5.3.1.1 Remove attribute from array
                            if (!Remove(attr, filteredAttr))
                            {
                                return(_apiResponseFactory.CreateError(
                                           HttpStatusCode.BadRequest,
                                           _errorResponseFactory.CreateError(ErrorMessages.TheRepresentationCannotBeRemoved, HttpStatusCode.BadRequest)
                                           ));
                            }
                        }
                        else
                        {
                            // 4.5.3.1.2 Remove attribute from complex representation.
                            if (attr.Parent != null)
                            {
                                var complexParent = attr.Parent as ComplexRepresentationAttribute;
                                if (complexParent == null)
                                {
                                    continue;
                                }

                                complexParent.Values = complexParent.Values.Where(v => !v.Equals(attr));
                            }
                            else
                            {
                                representation.Attributes = representation.Attributes.Where(v => !v.Equals(attr));
                            }
                        }
                        break;

                    // 4.5.3.2 Add attribute.
                    case PatchOperations.add:
                        if (string.IsNullOrWhiteSpace(operation.Path) && attr == null)
                        {
                            representation.Attributes = representation.Attributes.Concat(new[] { filteredAttr });
                            continue;
                        }

                        if (attr == null)
                        {
                            return(_apiResponseFactory.CreateError(
                                       HttpStatusCode.BadRequest,
                                       _errorResponseFactory.CreateError(string.Format(ErrorMessages.TheAttributeDoesntExist, filteredAttr.SchemaAttribute.Name), HttpStatusCode.BadRequest)
                                       ));
                        }

                        if (!filteredAttr.SchemaAttribute.MultiValued)
                        {
                            if (!Set(attr, filteredAttr))
                            {
                                return(_apiResponseFactory.CreateError(
                                           HttpStatusCode.BadRequest,
                                           _errorResponseFactory.CreateError(ErrorMessages.TheRepresentationCannotBeSet, HttpStatusCode.BadRequest)
                                           ));
                            }
                        }
                        else
                        {
                            if (!Add(attr, filteredAttr))
                            {
                                return(_apiResponseFactory.CreateError(
                                           HttpStatusCode.BadRequest,
                                           _errorResponseFactory.CreateError(ErrorMessages.TheRepresentationCannotBeAdded, HttpStatusCode.BadRequest)
                                           ));
                            }
                        }
                        break;

                    // 4.5.3.3 Replace attribute
                    case PatchOperations.replace:
                        if (attr == null)
                        {
                            return(_apiResponseFactory.CreateError(
                                       HttpStatusCode.BadRequest,
                                       _errorResponseFactory.CreateError(string.Format(ErrorMessages.TheAttributeDoesntExist, filteredAttr.SchemaAttribute.Name), HttpStatusCode.BadRequest)
                                       ));
                        }

                        if (attr.SchemaAttribute.MultiValued)
                        {
                            if (!SetEnum(attr, filteredAttr))
                            {
                                return(_apiResponseFactory.CreateError(
                                           HttpStatusCode.BadRequest,
                                           _errorResponseFactory.CreateError(ErrorMessages.TheRepresentationCannotBeSet, HttpStatusCode.BadRequest)
                                           ));
                            }
                        }
                        else
                        {
                            if (!Set(attr, filteredAttr))
                            {
                                return(_apiResponseFactory.CreateError(
                                           HttpStatusCode.BadRequest,
                                           _errorResponseFactory.CreateError(ErrorMessages.TheRepresentationCannotBeSet, HttpStatusCode.BadRequest)
                                           ));
                            }
                        }
                        break;
                    }
                }
            }

            // 5. Save the representation.
            representation.Version = Guid.NewGuid().ToString();
            await _representationStore.UpdateRepresentation(representation);

            // 6. Returns the JSON representation.
            var response = await _responseParser.Parse(representation, locationPattern.Replace("{id}", id), schemaId, OperationTypes.Modification);

            return(_apiResponseFactory.CreateResultWithContent(HttpStatusCode.OK,
                                                               response.Object,
                                                               response.Location,
                                                               representation.Version,
                                                               representation.Id));
        }