Exemplo n.º 1
0
        public void ParseQueryOptionsShouldHandleOperationCustomShortCodes()
        {
            var validOptions = new DynamicQueryOptions
            {
                Filters = new List <Filter>
                {
                    new Filter
                    {
                        Value        = "123",
                        PropertyName = "name",
                        Operator     = FilterOperation.Equals
                    },
                    new Filter
                    {
                        Value        = "21",
                        PropertyName = "age",
                        Operator     = FilterOperation.GreaterThanOrEqual
                    }
                }
            };

            var customOpShortCodes = new CustomOpCodes
            {
                { "fizz", FilterOperation.Equals },
                { "buzz", FilterOperation.GreaterThanOrEqual }
            };

            DynamicQueryOptions result = ExpressionBuilder.ParseQueryOptions("o=fizz&p=name&v=123&o=buzz&p=age&v=21", opShortCodes: customOpShortCodes);

            Assert.True(AreObjectPropertiesMatching(validOptions, result));
        }
        /// <summary>
        /// Populates an Instance of DynamicQueryOptions from parsed query string values.
        /// </summary>
        /// <param name="dynamicQueryOptions">DynamicQueryOptions ref to populate to.</param>
        /// <param name="operations">Operations array.</param>
        /// <param name="parameterNames">ParameterNames array.</param>
        /// <param name="parameterValues">ParameterValues array.</param>
        /// <param name="sortOptions">SortOptions array.</param>
        /// <param name="offsetOptions">Offset array.</param>
        /// <param name="countOptions">Count array.</param>
        /// <param name="opShortCodes">CustomOpCodes instance.</param>
        internal static void PopulateDynamicQueryOptions(
            DynamicQueryOptions dynamicQueryOptions,
            string[] operations,
            string[] parameterNames,
            string[] parameterValues,
            string[] sortOptions,
            string[] offsetOptions,
            string[] countOptions,
            CustomOpCodes opShortCodes             = null,
            DynamicQueryOptions memberQueryOptions = null)
        {
            if (dynamicQueryOptions == null)
            {
                throw new DynamicQueryException("DynamicQueryOptions should not be null");
            }

            // Check the counts for every operation, since they work in tuples they should be the same.
            if (AreCountsMatching(operations, parameterNames, parameterValues))
            {
                for (int i = 0; i < operations.Length; i++)
                {
                    FilterOperation foundOperation = default(FilterOperation);

                    // Check if we support this operation.
                    if (Enum.TryParse(operations[i], true, out FilterOperation parsedOperation))
                    {
                        foundOperation = parsedOperation;
                    }
                    else if (opShortCodes != null &&
                             opShortCodes.Count > 0 &&
                             opShortCodes.TryGetValue(operations[i], out FilterOperation shortCodeOperation)) // Whoop maybe its a short code ?
                    {
                        foundOperation = shortCodeOperation;
                    }
                    else
                    {
                        throw new OperationNotSupportedException($"Invalid operation {operations[i]}");
                    }

                    string[] splittedParameterName = parameterNames[i].Split(PARAMETER_OPTION_DELIMITER);
                    bool     isCaseSensitive       = false;
                    if (splittedParameterName.Length > 1)
                    {
                        if (splittedParameterName[1].ToLower() == CASE_SENSITIVITY_PARAMETER_OPTION)
                        {
                            isCaseSensitive = true;
                        }
                        else
                        {
                            throw new InvalidDynamicQueryException($"Invalid extra option provided for filter property {splittedParameterName[0]}. Received value was {splittedParameterName[1]}");
                        }
                    }


                    var composedFilter = new Filter
                    {
                        Operator      = foundOperation,
                        PropertyName  = splittedParameterName[0],
                        CaseSensitive = isCaseSensitive
                    };

                    if (foundOperation >= FilterOperation.Any)
                    {
                        composedFilter.Value = memberQueryOptions;
                    }
                    else
                    {
                        composedFilter.Value = parameterValues[i];
                    }

                    dynamicQueryOptions.Filters.Add(composedFilter);
                }
            }
            else
            {
                throw new QueryTripletsMismatchException("Invalid query structure. Operation, parameter name and value triplets are not matching.");
            }

            if (sortOptions != null && sortOptions.Length >= 1)
            {
                foreach (string sortOption in sortOptions)
                {
                    if (!string.IsNullOrEmpty(sortOption))
                    {
                        // Split the property name to sort and the direction.
                        string[] splittedParam = sortOption.Split(PARAMETER_OPTION_DELIMITER);

                        bool             isCaseSensitive = false;
                        SortingDirection direction       = SortingDirection.Asc;
                        if (splittedParam.Length == 2)
                        {
                            // If we get an array of 2 we have a sorting direction, try to apply it.
                            if (!Enum.TryParse(splittedParam[1], true, out direction))
                            {
                                throw new InvalidDynamicQueryException("Invalid sorting direction");
                            }
                        }
                        else if (splittedParam.Length == 3)
                        {
                            if (splittedParam[2].ToLower() == CASE_SENSITIVITY_PARAMETER_OPTION)
                            {
                                isCaseSensitive = true;
                            }
                            else
                            {
                                throw new InvalidDynamicQueryException($"Invalid extra option provided for sort property {splittedParam[0]}. Received value was {splittedParam[2]}");
                            }
                        }
                        else if (splittedParam.Length > 3) // If we get more than 3 results in the array, url must be wrong.
                        {
                            throw new InvalidDynamicQueryException("Invalid query structure. SortOption is misformed");
                        }

                        // Create the sorting option.
                        dynamicQueryOptions.SortOptions.Add(new SortOption
                        {
                            SortingDirection = direction,
                            PropertyName     = splittedParam[0],
                            CaseSensitive    = isCaseSensitive
                        });
                    }
                }
            }

            if (offsetOptions != null &&
                countOptions != null &&
                countOptions.Length > 0 &&
                offsetOptions.Length > 0 &&
                offsetOptions.Length == countOptions.Length)
            {
                if (int.TryParse(countOptions[0], out int countValue) && // We only care about the first values.
                    int.TryParse(offsetOptions[0], out int offsetValue))
                {
                    dynamicQueryOptions.PaginationOption = new PaginationOption
                    {
                        Count  = countValue,
                        Offset = offsetValue
                    };
                }
                else
                {
                    throw new DynamicQueryException("Invalid pagination options");
                }
            }
        }
        /// <summary>
        /// Parses a Querystring into DynamicQueryOptions instance.
        /// </summary>
        /// <param name="query">QueryString to parse.</param>
        /// <param name="opShortCodes">Custom operation shortcodes.</param>
        /// <returns>Parsed DynamicQueryOptions instance.</returns>
        public static DynamicQueryOptions ParseQueryOptions(string query, CustomOpCodes opShortCodes = null)
        {
            try
            {
                var dynamicQueryOptions = new DynamicQueryOptions();
                if (string.IsNullOrEmpty(query))
                {
                    return(dynamicQueryOptions);
                }

                string decodedQuery = HttpUtility.UrlDecode(query);
                DynamicQueryOptions innerQueryOptions = null;
                const string        innerMemberKey    = "v=(";
                int indexOfInnerMemberKey             = decodedQuery.IndexOf(innerMemberKey);
                if (indexOfInnerMemberKey != -1)
                {
                    indexOfInnerMemberKey += innerMemberKey.Length;
                    string innerQuery = decodedQuery.Substring(indexOfInnerMemberKey, decodedQuery.LastIndexOf(')') - indexOfInnerMemberKey);
                    innerQueryOptions = ParseQueryOptions(innerQuery, opShortCodes);
                    decodedQuery      = decodedQuery.Replace(innerQuery, string.Empty);
                }

                string[]            defaultArrayValue = new string[0];
                NameValueCollection queryCollection   = HttpUtility.ParseQueryString(decodedQuery);

                string[] operations = queryCollection
                                      .GetValues(OPERATION_PARAMETER_KEY)
                                      ?.Select(x => x.ClearSpaces())
                                      .ToArray() ?? defaultArrayValue;

                string[] parameterNames = queryCollection
                                          .GetValues(PARAMETER_NAME_KEY)
                                          ?.Select(x => x.ClearSpaces())
                                          .ToArray() ?? defaultArrayValue;

                string[] parameterValues = queryCollection
                                           .GetValues(PARAMETER_VALUE_KEY)
                                           .ToArray() ?? defaultArrayValue;

                string[] sortOptions = queryCollection
                                       .GetValues(SORT_OPTIONS_PARAMETER_KEY)
                                       ?.Select(x => x.ClearSpaces())
                                       .ToArray() ?? defaultArrayValue;

                string[] offsetOptions = queryCollection
                                         .GetValues(OFFSET_PARAMETER_KEY)
                                         ?.Select(x => x.ClearSpaces())
                                         .ToArray() ?? defaultArrayValue;

                string[] countOptions = queryCollection
                                        .GetValues(COUNT_PARAMETER_KEY)
                                        ?.Select(x => x.ClearSpaces())
                                        .ToArray() ?? defaultArrayValue;

                PopulateDynamicQueryOptions(
                    dynamicQueryOptions,
                    operations,
                    parameterNames,
                    parameterValues,
                    sortOptions,
                    offsetOptions,
                    countOptions,
                    opShortCodes ?? DefaultOpShortCodes,
                    innerQueryOptions);

                return(dynamicQueryOptions);
            }
            catch (Exception ex)
            {
                throw new DynamicQueryException("DynamicQueryBuilder has encountered an unhandled exception", query, ex);
            }
        }
        /// <summary>
        /// Parses a Querystring into DynamicQueryOptions instance.
        /// </summary>
        /// <param name="query">QueryString to parse.</param>
        /// <param name="opShortCodes">Custom operation shortcodes.</param>
        /// <returns>Parsed DynamicQueryOptions instance.</returns>
        public static DynamicQueryOptions ParseQueryOptions(string query, CustomOpCodes opShortCodes = null)
        {
            try
            {
                var dynamicQueryOptions = new DynamicQueryOptions();
                if (string.IsNullOrEmpty(query))
                {
                    return(dynamicQueryOptions);
                }

                ////+ character issue
                ////https://docs.microsoft.com/en-us/dotnet/api/system.web.httputility.urlencode?redirectedfrom=MSDN&view=net-5.0#System_Web_HttpUtility_UrlEncode_System_String_
                if (QueryStringParser.IsQueryStringEncoded(query))
                {
                    query = HttpUtility.UrlDecode(query);
                }

                DynamicQueryOptions innerQueryOptions = null;
                const string        innerMemberKey    = "v=(";
                int indexOfInnerMemberKey             = query.IndexOf(innerMemberKey, StringComparison.Ordinal);
                if (indexOfInnerMemberKey != -1)
                {
                    indexOfInnerMemberKey += innerMemberKey.Length;
                    string innerQuery = query.Substring(indexOfInnerMemberKey, query.LastIndexOf(')') - indexOfInnerMemberKey);
                    innerQueryOptions = ParseQueryOptions(innerQuery, opShortCodes);
                    query             = query.Replace(innerQuery, string.Empty);
                }

                string[]            defaultArrayValue = new string[0];
                NameValueCollection queryCollection   = HttpUtility.ParseQueryString(query);

                IEnumerable <QueryStringParserResult> queryStringParserResults = QueryStringParser
                                                                                 .GetAllParameterWithValue(query)
                                                                                 .Where(e => !string.IsNullOrEmpty(e.Value) && e.Value.Contains(InternalConstants.PLUS_CHARACTER)).ToList();
                if (queryStringParserResults.Any())
                {
                    QueryStringParser.ReplaceNameValueCollection(queryStringParserResults, queryCollection, InternalConstants.PARAMETER_VALUE_KEY);
                }

                string[] operations = queryCollection
                                      .GetValues(InternalConstants.OPERATION_PARAMETER_KEY)
                                      ?.Select(x => x.ClearSpaces())
                                      .ToArray() ?? defaultArrayValue;

                string[] parameterNames = queryCollection
                                          .GetValues(InternalConstants.PARAMETER_NAME_KEY)
                                          ?.Select(x => x.ClearSpaces())
                                          .ToArray() ?? defaultArrayValue;

                string[] parameterValues = queryCollection
                                           .GetValues(InternalConstants.PARAMETER_VALUE_KEY)
                                           ?.ToArray() ?? defaultArrayValue;

                string[] sortOptions = queryCollection
                                       .GetValues(InternalConstants.SORT_OPTIONS_PARAMETER_KEY)
                                       ?.Select(x => x.ClearSpaces())
                                       .ToArray() ?? defaultArrayValue;

                string[] offsetOptions = queryCollection
                                         .GetValues(InternalConstants.OFFSET_PARAMETER_KEY)
                                         ?.Select(x => x.ClearSpaces())
                                         .ToArray() ?? defaultArrayValue;

                string[] countOptions = queryCollection
                                        .GetValues(InternalConstants.COUNT_PARAMETER_KEY)
                                        ?.Select(x => x.ClearSpaces())
                                        .ToArray() ?? defaultArrayValue;

                PopulateDynamicQueryOptions(
                    dynamicQueryOptions,
                    operations,
                    parameterNames,
                    parameterValues,
                    sortOptions,
                    offsetOptions,
                    countOptions,
                    opShortCodes: opShortCodes ?? Defaults.DefaultOpShortCodes,
                    memberQueryOptions: innerQueryOptions);

                return(dynamicQueryOptions);
            }
            catch (Exception ex)
            {
                throw new DynamicQueryException("DynamicQueryBuilder has encountered an unhandled exception", query, ex);
            }
        }