This commit is contained in:
2018-09-25 18:01:30 +00:00
parent 51832b532d
commit 52ba3bb970
4 changed files with 118 additions and 15 deletions

View File

@@ -28,7 +28,6 @@ Once that is done then can steam ahead on the biz objects but until I have the c
IMMEDIATE ITEMS:
================
HAPPY MONDAY RADIANT MAIDEN!!
- Search and search text indexing
- NEXT UP: See the integration tests for the next things to be done
@@ -42,6 +41,16 @@ IMMEDIATE ITEMS:
- I originally didn't have the save in there because I thought subsequent code might all share in the single context save,
however that's impossible as things like the search indexing require a save to harvest id's so it's not actually saving any time just adding complexity
where it shouldn't (in the caller)
- Why does user create entries in the biz object but Widget does it in the controller?
Determine which is "canon" and change the other. Must be consistent unless absolutely needs not to be.
- REFACTOR: can biz objects or controllers be improved to contain the type and then all code within then use that central type for consistency?
- Anything else that can be improved? The indexing code is all identical mostly with the exception of the actual fields, maybe it can be formalized and moved up or down a level?
- If it's because it needs to be called directly, then maybe have the controller call the biz object to do the indexing so that it's clear what is happening?
- Or move it to the biz object entirely?? Seems maybe logical since it's not really a controller issue.
- Delete children in User object: I don't like it this way, the children would all be common to another biz object so instead I would like to see it call the other object, not directly issue sql changes
(right now the user deletes the tag maps directly, this is just wrong)
- NAMING is it dbObj, o, inObj? Go through and consistivize
- Auto visible id number assigning code
- Give widgets a visible ID number scheme and add to tests

View File

@@ -209,6 +209,7 @@ namespace AyaNova.Api.Controllers
//Log
EventLogProcessor.AddEntry(new Event(biz.userId, o.Id, AyaType.User, AyaEvent.Modified), ct);
await ct.SaveChangesAsync();
Search.ProcessUpdatedObjectKeywords(ct, UserLocaleIdFromContext.Id(HttpContext.Items), o.Id, AyaType.User, o.Name, o.EmployeeNumber, o.Notes, o.Name);
}
catch (DbUpdateConcurrencyException)
{
@@ -283,6 +284,7 @@ namespace AyaNova.Api.Controllers
//Log
EventLogProcessor.AddEntry(new Event(biz.userId, o.Id, AyaType.User, AyaEvent.Modified), ct);
await ct.SaveChangesAsync();
Search.ProcessUpdatedObjectKeywords(ct, UserLocaleIdFromContext.Id(HttpContext.Items), o.Id, AyaType.User, o.Name, o.EmployeeNumber, o.Notes, o.Name);
}
catch (DbUpdateConcurrencyException)
{
@@ -345,7 +347,12 @@ namespace AyaNova.Api.Controllers
else
{
//EVENT LOG
//Note that event log is processed in the biz object unlike the widget for no apparent reason
//SEARCH INDEXING
Search.ProcessNewObjectKeywords(ct, UserLocaleIdFromContext.Id(HttpContext.Items), o.Id, AyaType.User, o.Name, o.EmployeeNumber, o.Notes, o.Name);
//return success and link
//NOTE: this is a USER object so we don't want to return some key fields for security reasons
//which is why the object is "cleaned" before return
@@ -398,12 +405,14 @@ namespace AyaNova.Api.Controllers
//Log
EventLogProcessor.DeleteObject(biz.userId, AyaType.User, dbObj.Id, dbObj.Name, ct);
await ct.SaveChangesAsync();
//Delete children / attached objects
biz.DeleteChildren(dbObj);
//This will directly execute and is not part of context for saving purposes
Search.ProcessDeletedObjectKeywords(ct, dbObj.Id, AyaType.User);
return NoContent();
}

View File

@@ -210,8 +210,8 @@ namespace AyaNova.Api.Controllers
{
//Log
EventLogProcessor.AddEntry(new Event(biz.userId, o.Id, AyaType.Widget, AyaEvent.Modified), ct);
Search.ProcessUpdatedObjectKeywords(ct, UserLocaleIdFromContext.Id(HttpContext.Items), o.Id, AyaType.Widget, o.Name, o.Notes, o.Name);
await ct.SaveChangesAsync();
Search.ProcessUpdatedObjectKeywords(ct, UserLocaleIdFromContext.Id(HttpContext.Items), o.Id, AyaType.Widget, o.Name, o.Notes, o.Name);
}
catch (DbUpdateConcurrencyException)
{
@@ -291,7 +291,7 @@ namespace AyaNova.Api.Controllers
//this will save the context as part of it's operations
Search.ProcessUpdatedObjectKeywords(ct, UserLocaleIdFromContext.Id(HttpContext.Items), o.Id, AyaType.Widget, o.Name, o.Notes, o.Name);
}
catch (DbUpdateConcurrencyException)
{
@@ -359,7 +359,7 @@ namespace AyaNova.Api.Controllers
//this will save the context as part of it's operations
Search.ProcessNewObjectKeywords(ct, UserLocaleIdFromContext.Id(HttpContext.Items), o.Id, AyaType.Widget, o.Name, o.Notes, o.Name);
//return success and link
return CreatedAtAction("GetWidget", new { id = o.Id }, new ApiCreatedResponse(o));
@@ -416,7 +416,7 @@ namespace AyaNova.Api.Controllers
//This will directly execute and is not part of context for saving purposes
Search.ProcessDeletedObjectKeywords(ct, dbObj.Id, AyaType.Widget);
//Delete children / attached objects
biz.DeleteChildren(dbObj);

View File

@@ -10,16 +10,11 @@ namespace raven_integration
public class SearchOps
{
/// <summary>
/// Test simple phrase only search
/// Test simple phrase only search is fundamentally working
/// </summary>
[Fact]
public async void PhraseOnlySearchShouldWork()
public async void PhraseOnlySearchShouldReturnCorrectResultsInOrder()
{
const string TEST_SEARCH_PHRASE = "simple dogs";
@@ -59,6 +54,96 @@ namespace raven_integration
Util.ValidateDataReturnResponseOk(a);
long NoMatchThirdWidgetId = a.ObjectResponse["result"]["id"].Value<long>();
//CREATE A USER
D = new JObject();
D.name = Util.Uniquify("Search simple Test User");
D.notes = "This user has the word 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 MatchUserId = a.ObjectResponse["result"]["id"].Value<long>();
//Now see if can find those objects with a phrase search
dynamic SearchParameters = new JObject();
SearchParameters.phrase = TEST_SEARCH_PHRASE;
SearchParameters.nameOnly = false;
SearchParameters.typeOnly = 0;//no type
a = await Util.PostAsync("Search", await Util.GetTokenAsync("manager", "l3tm3in"), SearchParameters.ToString());
Util.ValidateDataReturnResponseOk(a);
//Now validate the return list
((JArray)a.ObjectResponse["result"]).Count.Should().BeGreaterOrEqualTo(3);
//Turn the list into an array of id's
var v = ((JArray)a.ObjectResponse["result"]);
List<long> MatchingIdList = new List<long>();
foreach (JObject j in v)
{
MatchingIdList.Add(j["id"].Value<long>());
}
MatchingIdList.Should().Contain(MatchFirstWidgetId, "ShouldContainFirstWidget");
MatchingIdList.Should().Contain(MatchSecondWidgetId, "ShouldContainSecondWidget");
MatchingIdList.Should().NotContain(NoMatchThirdWidgetId, "ShouldNotContainThirdWidget");
}//eot
/// <summary>
/// Test return grouping and sorting properly
/// </summary>
[Fact]
public async void ResultsInCorrectOrderAndGrouping()
{
const string TEST_SEARCH_PHRASE = "mango ducky";
//CREATE A WIDGET
dynamic D = new JObject();
D.name = Util.Uniquify("Search mango Test WIDGET");
D.dollarAmount = 1.11m;
D.active = true;
D.roles = 0;
D.notes = "The quick brown and ducky 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>();
//CREATE A SECOND WIDGET
D = new JObject();
D.name = Util.Uniquify("Search simple as in dogs SECOND 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";
a = await Util.PostAsync("Widget", await Util.GetTokenAsync("manager", "l3tm3in"), D.ToString());
Util.ValidateDataReturnResponseOk(a);
long MatchSecondWidgetId = a.ObjectResponse["result"]["id"].Value<long>();
//CREATE A THIRD WIDGET
D = new JObject();
D.name = Util.Uniquify("Search Simple THIRD Test WIDGET");
D.dollarAmount = 1.11m;
D.active = true;
D.roles = 0;
D.notes = "This Widget should not be returned in the search as it only contains a single keyword in the name not both";
a = await Util.PostAsync("Widget", await Util.GetTokenAsync("manager", "l3tm3in"), D.ToString());
Util.ValidateDataReturnResponseOk(a);
long NoMatchThirdWidgetId = a.ObjectResponse["result"]["id"].Value<long>();
//Now see if can find that widget with a phrase search
dynamic SearchParameters = new JObject();
@@ -85,7 +170,7 @@ namespace raven_integration
}//eot
TODO: TEST RESULTS ARE RETURNED GROUPED (together) BY OBJECT TYPE THEN OBJECT ID DESCENDING
// TODO: TEST RESULTS ARE RETURNED GROUPED (together) BY OBJECT TYPE THEN OBJECT ID DESCENDING
//TODO: TEST THAT RIGHTS ARE WORKING PROPERLY WITH RETURNED RESULTS
//NAME ONLY SEARCH SHOULD WORK WITH NO RIGHTS TO READ FULL RECORD