From 7d71d7ca3398cfbc0e928f5f508762e51da9b89e Mon Sep 17 00:00:00 2001 From: John Cardinal Date: Tue, 25 Sep 2018 21:58:54 +0000 Subject: [PATCH] --- devdocs/specs/core-search.txt | 1 + server/AyaNova/biz/Search.cs | 10 +- test/raven-integration/Search/SearchOps.cs | 131 ++++++++++++--------- 3 files changed, 84 insertions(+), 58 deletions(-) diff --git a/devdocs/specs/core-search.txt b/devdocs/specs/core-search.txt index cc919b8f..3f97eb55 100644 --- a/devdocs/specs/core-search.txt +++ b/devdocs/specs/core-search.txt @@ -49,6 +49,7 @@ REQUIREMENTS - NAME: in search index include a bool indicating the word is actually part of the name or equivalent of the object, this will make name searches WAAAAYYY easier!!! - in non named objects it's whatever the primary identifier is, i.e. workorder number for a workorder, partnumber for a part - Maybe all objects should have a "name" column in db be it a workorder number or part number just for consistency + - RIGHTS: if a search is performed that is not NAME ONLY then the user role must have rights to read the full object even if the results are found in the name as this simplifies things greatly - TAGS: Any search, anywhere, can include tags (internally it will post filter on tags after initial text search or if no text then will just search tags) - ROLES: Needs to internally be able to filter by CANREAD property of role user is in (e.g. if no rights to read client no clients returned) diff --git a/server/AyaNova/biz/Search.cs b/server/AyaNova/biz/Search.cs index 07b2f598..b3613f6d 100644 --- a/server/AyaNova/biz/Search.cs +++ b/server/AyaNova/biz/Search.cs @@ -108,7 +108,6 @@ namespace AyaNova.Biz - public static async Task> DoSearch(AyContext ct, long localeId, AuthorizationRoles currentUserRoles, SearchRequestParameters searchParameters) { List ResultList = new List(); @@ -181,12 +180,11 @@ namespace AyaNova.Biz //In name? if (searchParameters.NameOnly) - q.Where(m => m.InName == true); + q = q.Where(m => m.InName == true); //Of type? if (searchParameters.TypeOnly != AyaType.NoType) - q.Where(m => m.ObjectType == searchParameters.TypeOnly); - + q = q.Where(m => m.ObjectType == searchParameters.TypeOnly); //Find the records that have the search terms in searchkey @@ -296,7 +294,9 @@ namespace AyaNova.Biz //REMOVE ANY ITEMS THAT USER IS NOT PERMITTED TO READ //If it's a name only search then all is allowed - //If it's not a name only search then rights need to be checked for full read because even if it's just a tags search that's part of the full record of the object + //If it's not a name only search then rights need to be checked for full read because even if it's just a tags search that's part of the full record of the object + //Note: I have decided in the interests of simplicity that even if the result was only found in the name, the user still needs full rights to read the object if the type of search + //was not InNameOnly type. This greatly simplifies processing. if (!searchParameters.NameOnly) { //list to hold temporary matches diff --git a/test/raven-integration/Search/SearchOps.cs b/test/raven-integration/Search/SearchOps.cs index a68f9899..9ec6b68a 100644 --- a/test/raven-integration/Search/SearchOps.cs +++ b/test/raven-integration/Search/SearchOps.cs @@ -20,20 +20,20 @@ namespace raven_integration //CREATE A WIDGET dynamic D = new JObject(); - D.name = Util.Uniquify("Search Simple Test WIDGET"); + D.name = Util.Uniquify("Search NOTES Test WIDGET"); D.dollarAmount = 1.11m; D.active = true; D.roles = 0; - D.notes = "The quick brown and simple fox jumped over the six lazy dogs!"; + D.notes = "This record will match in notes: The quick brown and simple fox jumped over the six lazy dogs!"; ApiResponse a = await Util.PostAsync("Widget", await Util.GetTokenAsync("manager", "l3tm3in"), D.ToString()); Util.ValidateDataReturnResponseOk(a); - long MatchFirstWidgetId = a.ObjectResponse["result"]["id"].Value(); + long MatchWidgetInNotesId = a.ObjectResponse["result"]["id"].Value(); - //CREATE A USER + //CREATE FIRST TEST USER WITH PHRASE IN NAME D = new JObject(); - D.name = Util.Uniquify("Search simple Test User"); - D.notes = "This user has the word dogs in its notes"; + D.name = Util.Uniquify("Search NAME DOGS simple Test User"); + D.notes = "This user has the match in it's name"; D.ownerId = 1L; D.active = true; D.login = Util.Uniquify("LOGIN"); @@ -44,23 +44,39 @@ namespace raven_integration a = await Util.PostAsync("User", await Util.GetTokenAsync("manager", "l3tm3in"), D.ToString()); Util.ValidateDataReturnResponseOk(a); - long MatchUserId = a.ObjectResponse["result"]["id"].Value(); + long MatchUserInNameId = a.ObjectResponse["result"]["id"].Value(); + + //CREATE A SECOND TEST USER WITH PHRASE IN NOTES + D = new JObject(); + D.name = Util.Uniquify("Search NOTES Test User"); + D.notes = "This user has the match simple dogs in its notes"; + D.ownerId = 1L; + D.active = true; + D.login = Util.Uniquify("LOGIN"); + D.password = Util.Uniquify("PASSWORD"); + D.roles = 0;//norole + D.localeId = 1;//random locale + D.userType = 3;//non scheduleable + + a = await Util.PostAsync("User", await Util.GetTokenAsync("manager", "l3tm3in"), D.ToString()); + Util.ValidateDataReturnResponseOk(a); + long MatchUserInNotesId = a.ObjectResponse["result"]["id"].Value(); //CREATE A SECOND WIDGET D = new JObject(); - D.name = Util.Uniquify("Search simple as in dogs SECOND Test WIDGET"); + D.name = Util.Uniquify("Search NAME simple as in dogs Test WIDGET"); D.dollarAmount = 1.11m; D.active = true; D.roles = 0; - D.notes = "This Widget should be returned in the search as it contains both keywords in the name"; + D.notes = "This Widget matches in name"; a = await Util.PostAsync("Widget", await Util.GetTokenAsync("manager", "l3tm3in"), D.ToString()); Util.ValidateDataReturnResponseOk(a); - long MatchSecondWidgetId = a.ObjectResponse["result"]["id"].Value(); + long MatchWidgetInNameId = a.ObjectResponse["result"]["id"].Value(); //CREATE A THIRD WIDGET D = new JObject(); - D.name = Util.Uniquify("Search Simple THIRD Test WIDGET"); + D.name = Util.Uniquify("Search NO-MATCH THIRD Test WIDGET"); D.dollarAmount = 1.11m; D.active = true; D.roles = 0; @@ -68,7 +84,7 @@ namespace raven_integration a = await Util.PostAsync("Widget", await Util.GetTokenAsync("manager", "l3tm3in"), D.ToString()); Util.ValidateDataReturnResponseOk(a); - long NoMatchThirdWidgetId = a.ObjectResponse["result"]["id"].Value(); + long MatchNothingWidgetId = a.ObjectResponse["result"]["id"].Value(); //Now see if can find those objects with a phrase search @@ -92,10 +108,11 @@ namespace raven_integration } //Ensure the expected items are returned - MatchingIdList.Should().Contain(MatchFirstWidgetId, "ShouldContainFirstWidget"); - MatchingIdList.Should().Contain(MatchSecondWidgetId, "ShouldContainSecondWidget"); - MatchingIdList.Should().Contain(MatchUserId, "ShouldContainUser"); - MatchingIdList.Should().NotContain(NoMatchThirdWidgetId, "ShouldNotContainThirdWidget"); + MatchingIdList.Should().Contain(MatchWidgetInNotesId, "ShouldContainFirstWidget"); + MatchingIdList.Should().Contain(MatchWidgetInNameId, "ShouldContainSecondWidget"); + MatchingIdList.Should().Contain(MatchUserInNotesId, "ShouldContainUserNotesMatch"); + MatchingIdList.Should().Contain(MatchUserInNameId, "ShouldContainUserNameMatch"); + MatchingIdList.Should().NotContain(MatchNothingWidgetId, "ShouldNotContainThirdWidget"); //Assert the order (roughly, this is kind of a waste of time, either the code is sorting or not, it's not going to change) //first item must be a widget @@ -105,12 +122,29 @@ namespace raven_integration a.ObjectResponse["result"][MatchingIdList.Count - 1]["type"].Value().Should().Be(3); - //TEST THAT RIGHTS ARE WORKING PROPERLY WITH RETURNED RESULTS + //FULL BODY SEARCH RIGHTS + //First up test a full record search returns no results due to insufficient rights + //even though the record exists + //Just re-run the above search exactly but with a no rights to full User or Widget role instead + //Only BizAdmin* roles can read a full user record but anyone should be able to see names - // + //This search should return zero items + + a = await Util.PostAsync("Search", await Util.GetTokenAsync("SubContractorLimited"), SearchParameters.ToString()); + Util.ValidateDataReturnResponseOk(a); + ((JArray)a.ObjectResponse["result"]).Count.Should().Be(0, "User with no rights should not see any results in body search"); + //NAME ONLY SEARCH SHOULD WORK WITH NO RIGHTS TO READ FULL RECORD - //IF NO RIGHTS TO FULL READ AND SEARCH IS ON FULL OBJECT AND NOT NAME THEN IT SHOULD NOT RETURN THE RESULTS + //repeat same search but with nameOnly = true and should return at two records at least but not any of the body ones + SearchParameters = new JObject(); + SearchParameters.phrase = TEST_SEARCH_PHRASE; + SearchParameters.nameOnly = true; + SearchParameters.typeOnly = 0;//no type + a = await Util.PostAsync("Search", await Util.GetTokenAsync("SubContractorLimited"), SearchParameters.ToString()); + Util.ValidateDataReturnResponseOk(a); + ((JArray)a.ObjectResponse["result"]).Count.Should().BeGreaterOrEqualTo(2); + //Check that list does *not* include the notes only records @@ -118,40 +152,31 @@ namespace raven_integration /* - {{ - "result": [ - { - "name": "Search simple as in dogs SECOND Test WIDGET1537899693", - "type": 2, - "id": 205 - }, - { - "name": "Search Simple Test WIDGET1537899693", - "type": 2, - "id": 204 - }, - { - "name": "Search simple as in dogs SECOND Test WIDGET1537899490", - "type": 2, - "id": 202 - }, - { - "name": "Search Simple Test WIDGET1537899489", - "type": 2, - "id": 201 - }, - { - "name": "Search simple Test User1537899693", - "type": 3, - "id": 22 - }, - { - "name": "Search simple Test User1537899490", - "type": 3, - "id": 21 - } - ] - }} + {{Initial search results: borked! + {{ + "result": [ + { + "name": "Search NAME simple as in dogs Test WIDGET1537911374", + "type": 2, + "id": 202 + }, + { + "name": "Search NOTES Test WIDGET1537911373", + "type": 2, + "id": 201 + }, + { + "name": "Search NOTES Test User1537911374", + "type": 3, + "id": 22 + }, + { + "name": "Search NAME DOGS simple Test User1537911374", + "type": 3, + "id": 21 + } + ] +}} */