diff --git a/server/AyaNova/biz/DataFilterBiz.cs b/server/AyaNova/biz/DataFilterBiz.cs index c6601fa5..29a96b54 100644 --- a/server/AyaNova/biz/DataFilterBiz.cs +++ b/server/AyaNova/biz/DataFilterBiz.cs @@ -292,8 +292,19 @@ namespace AyaNova.Biz if (!FilterComparisonOperator.Operators.Contains(opType)) AddError(ValidationErrorType.InvalidValue, "Filter", $"Filter array item {i}, \"op\" property value of \"{opType}\" is not a valid FilterComparisonOperator type"); } - if (filterItem["value"] == null || string.IsNullOrWhiteSpace(filterItem["value"].Value())) + + if (filterItem["value"] == null) AddError(ValidationErrorType.RequiredPropertyEmpty, "Filter", $"Filter array item {i}, object is missing or is empty the required \"value\" property "); + else + { + if (filterItem["value"].Type == JTokenType.String && string.IsNullOrWhiteSpace(filterItem["value"].Value())) + AddError(ValidationErrorType.RequiredPropertyEmpty, "Filter", $"Filter array item {i}, object is missing or is empty the required \"value\" property "); + + if (filterItem["value"].Type == JTokenType.Array && filterItem["value"].Count() == 0) + AddError(ValidationErrorType.RequiredPropertyEmpty, "Filter", $"Filter array item {i}, object is missing or is empty the required \"value\" property ARRAY "); + } + + //NOTE: value of nothing, null or empty is a valid value so no checking for it here } } diff --git a/server/AyaNova/biz/FilterSqlCriteriaBuilder.cs b/server/AyaNova/biz/FilterSqlCriteriaBuilder.cs index fbd35bd2..c30f239d 100644 --- a/server/AyaNova/biz/FilterSqlCriteriaBuilder.cs +++ b/server/AyaNova/biz/FilterSqlCriteriaBuilder.cs @@ -27,10 +27,18 @@ namespace AyaNova.Biz var filterItem = FilterArray[i]; var fld = filterItem["fld"].Value(); var opType = filterItem["op"].Value(); - var val = filterItem["value"].Value(); + List tagList = new List(); + string val = string.Empty; + if (filterItem["value"].Type != JTokenType.Array) + val = filterItem["value"].Value(); + else + { + tagList = filterItem["value"].ToObject>(); + } + var dataType = filterOptions.Flds.Find(x => x.Fld == fld).Type; - sb.Append(DataFilterToColumnCriteria(fld, dataType, opType, val)); + sb.Append(DataFilterToColumnCriteria(fld, dataType, opType, val, tagList)); if (i < FilterArray.Count - 1) { sb.Append(" AND "); @@ -43,8 +51,9 @@ namespace AyaNova.Biz /// /// Translate DataFilter to Postgres friendly SQL criteria /// - private static string DataFilterToColumnCriteria(string sColumn, string sDataType, string sOperator, string sValue)//, bool IsCompound) + private static string DataFilterToColumnCriteria(string sColumn, string sDataType, string sOperator, string sValue, List sTags)//, bool IsCompound) { + bool TagFilter = sTags.Count < 0; StringBuilder sb = new StringBuilder(); //Column name //sb.Append(" "); @@ -52,7 +61,7 @@ namespace AyaNova.Biz sb.Append(" "); //handle null values separately - if (sValue == FilterSpecialToken.Null) + if (!TagFilter && sValue == FilterSpecialToken.Null) { switch (sDataType) { @@ -91,44 +100,44 @@ namespace AyaNova.Biz //so.... //Special addition to handle nulls - // if (!IsCompound) - // { - switch (sOperator) + if (!TagFilter) { - case FilterComparisonOperator.Equality: - //no change on equals for nulls - break; - case FilterComparisonOperator.GreaterThan: - //no change on greater than for nulls - //(nulls are going to be assumed to be always at the - //less than end of the scale) - break; - case FilterComparisonOperator.GreaterThanOrEqualTo: - //no change on greater than for nulls - //(nulls are going to be assumed to be always at the - //less than end of the scale) - break; - case FilterComparisonOperator.LessThan: - sb.Append("Is Null OR "); - sb.Append(sColumn); - sb.Append(" "); - break; - case FilterComparisonOperator.LessThanOrEqualTo: - sb.Append("Is Null OR "); - sb.Append(sColumn); - sb.Append(" "); - break; - // case "Like": - // //No change on like - // break; - case FilterComparisonOperator.NotEqual: - //This is the big one: - sb.Append("Is Null OR "); - sb.Append(sColumn); - sb.Append(" "); - break; + switch (sOperator) + { + case FilterComparisonOperator.Equality: + //no change on equals for nulls + break; + case FilterComparisonOperator.GreaterThan: + //no change on greater than for nulls + //(nulls are going to be assumed to be always at the + //less than end of the scale) + break; + case FilterComparisonOperator.GreaterThanOrEqualTo: + //no change on greater than for nulls + //(nulls are going to be assumed to be always at the + //less than end of the scale) + break; + case FilterComparisonOperator.LessThan: + sb.Append("Is Null OR "); + sb.Append(sColumn); + sb.Append(" "); + break; + case FilterComparisonOperator.LessThanOrEqualTo: + sb.Append("Is Null OR "); + sb.Append(sColumn); + sb.Append(" "); + break; + // case "Like": + // //No change on like + // break; + case FilterComparisonOperator.NotEqual: + //This is the big one: + sb.Append("Is Null OR "); + sb.Append(sColumn); + sb.Append(" "); + break; + } } - // } #region Build for specific type switch (sDataType) @@ -770,6 +779,14 @@ namespace AyaNova.Biz } break; } + case AyDataType.Tags: + { + //Build tags filter fragment + //for initial release a tag filter is inclusive of all tags only + //in other words all tags presented must be in record to match (simple AND) + + } + break; default: throw new System.ArgumentOutOfRangeException("DATA_TYPE", sDataType, "GridToSqlCriteria unhandled data type[" + sDataType + "]"); } diff --git a/test/raven-integration/DataFilter/DataFilterFilteringLists.cs b/test/raven-integration/DataFilter/DataFilterFilteringLists.cs index 49375679..c601bebd 100644 --- a/test/raven-integration/DataFilter/DataFilterFilteringLists.cs +++ b/test/raven-integration/DataFilter/DataFilterFilteringLists.cs @@ -4077,8 +4077,6 @@ namespace raven_integration - - /////////////////////////////////////////////////////////////////////////////// //TEXT // @@ -7162,7 +7160,151 @@ namespace raven_integration /////////////////////////////////////////////////////////////////////////////// //TAGS // + #region TAG TESTS +/// + /// + /// + [Fact] + public async void TagFilterWorks() + { + + var TestName = "TagFilterWorks"; + var WidgetRunNameStart = Util.Uniquify(TestName); + + List InclusiveWidgetIdList = new List(); + List ExclusiveWidgetIdList = new List(); + + //CREATE 4 TEST WIDGETS + //two inclusive and two not inclusive + + //first inclusive widget + dynamic w = new JObject(); + w.name = Util.Uniquify(WidgetRunNameStart); + //Tags + dynamic InclusiveTagsArray = new JArray(); + InclusiveTagsArray.Add("red-tag-test"); + InclusiveTagsArray.Add("orange-tag-test"); + InclusiveTagsArray.Add("yellow-tag-test"); + InclusiveTagsArray.Add("green-tag-test"); + InclusiveTagsArray.Add("blue-tag-test"); + InclusiveTagsArray.Add("indigo-tag-test"); + InclusiveTagsArray.Add("violet-tag-test"); + w.tags = InclusiveTagsArray; + + ApiResponse a = await Util.PostAsync("Widget", await Util.GetTokenAsync("manager", "l3tm3in"), w.ToString()); + Util.ValidateDataReturnResponseOk(a); + InclusiveWidgetIdList.Add(a.ObjectResponse["data"]["id"].Value()); + + //second inclusive widget + w.name = Util.Uniquify(WidgetRunNameStart); + + a = await Util.PostAsync("Widget", await Util.GetTokenAsync("manager", "l3tm3in"), w.ToString()); + Util.ValidateDataReturnResponseOk(a); + InclusiveWidgetIdList.Add(a.ObjectResponse["data"]["id"].Value()); + + //first exclusive widget + w.name = Util.Uniquify(WidgetRunNameStart); + //Tags + dynamic ExclusiveTagsArray = new JArray(); + ExclusiveTagsArray.Add("crimson-tag-test"); + ExclusiveTagsArray.Add("amber-tag-test"); + ExclusiveTagsArray.Add("saffron-tag-test"); + ExclusiveTagsArray.Add("emerald-tag-test"); + ExclusiveTagsArray.Add("azure-tag-test"); + ExclusiveTagsArray.Add("red-tag-test"); + ExclusiveTagsArray.Add("blue-tag-test"); + ExclusiveTagsArray.Add("cobalt-tag-test"); + ExclusiveTagsArray.Add("magenta-tag-test"); + w.tags = ExclusiveTagsArray;//Missing green + + a = await Util.PostAsync("Widget", await Util.GetTokenAsync("manager", "l3tm3in"), w.ToString()); + Util.ValidateDataReturnResponseOk(a); + ExclusiveWidgetIdList.Add(a.ObjectResponse["data"]["id"].Value()); + + //second exclusive widget + w.name = Util.Uniquify(WidgetRunNameStart); + w.active = false; + + a = await Util.PostAsync("Widget", await Util.GetTokenAsync("manager", "l3tm3in"), w.ToString()); + Util.ValidateDataReturnResponseOk(a); + ExclusiveWidgetIdList.Add(a.ObjectResponse["data"]["id"].Value()); + + + //CREATE FILTER + dynamic d = new JObject(); + d.name = Util.Uniquify(WidgetRunNameStart); + // d.ownerId = 1L; + d["public"] = true; + d.listKey = "widget"; + + dynamic dfilter = new JArray(); + + //name starts with filter to constrict to widgets that this test block created only + dynamic DataFilterNameStart = new JObject(); + DataFilterNameStart.fld = "name"; + DataFilterNameStart.op = OpStartsWith; + DataFilterNameStart.value = WidgetRunNameStart; + dfilter.Add(DataFilterNameStart); + + //active test filter + dynamic DataFilterActive = new JObject(); + DataFilterActive.fld = "tags"; + DataFilterActive.op = OpEquality; + dynamic FilterTagsArray = new JArray(); + FilterTagsArray.Add("red-tag-test"); + FilterTagsArray.Add("green-tag-test");//green is the only one missing from the exclusive widget + FilterTagsArray.Add("blue-tag-test"); + DataFilterActive.value = FilterTagsArray; + dfilter.Add(DataFilterActive); + + d.filter = dfilter.ToString();//it expects it to be a json string, not actual json + + a = await Util.PostAsync("DataFilter", await Util.GetTokenAsync("BizAdminFull"), d.ToString()); + Util.ValidateDataReturnResponseOk(a); + + long DataFilterId = a.ObjectResponse["data"]["id"].Value(); + + //NOW FETCH WIDGET LIST WITH FILTER + a = await Util.GetAsync($"Widget/listwidgets?Offset=0&Limit=999&DataFilterId={DataFilterId.ToString()}", await Util.GetTokenAsync("manager", "l3tm3in")); + Util.ValidateDataReturnResponseOk(a); + Util.ValidateHTTPStatusCode(a, 200); + + //assert contains at least two records + ((JArray)a.ObjectResponse["data"]).Count.Should().BeGreaterThan(1); + var v = ((JArray)a.ObjectResponse["data"]); + List IDInResultList = new List(); + int InclusiveMatchCount = 0; + int ExclusiveMatchCount = 0; + foreach (JObject o in v) + { + if (InclusiveWidgetIdList.Contains(o["id"].Value())) + InclusiveMatchCount++; + if (ExclusiveWidgetIdList.Contains(o["id"].Value())) + ExclusiveMatchCount++; + } + + InclusiveMatchCount.Should().Be(InclusiveWidgetIdList.Count); + ExclusiveMatchCount.Should().Be(0); + + //DELETE WIDGETS + foreach (long l in InclusiveWidgetIdList) + { + a = await Util.DeleteAsync("Widget/" + l.ToString(), await Util.GetTokenAsync("BizAdminFull")); + Util.ValidateHTTPStatusCode(a, 204); + } + + foreach (long l in ExclusiveWidgetIdList) + { + a = await Util.DeleteAsync("Widget/" + l.ToString(), await Util.GetTokenAsync("BizAdminFull")); + Util.ValidateHTTPStatusCode(a, 204); + } + + //DELETE DATAFILTER + a = await Util.DeleteAsync("DataFilter/" + DataFilterId.ToString(), await Util.GetTokenAsync("BizAdminFull")); + Util.ValidateHTTPStatusCode(a, 204); + } + #endregion tag tests //================================================== }//eoc