using System.Linq; using System.Threading.Tasks; using System.Collections.Generic; using Microsoft.Extensions.Logging; using Microsoft.EntityFrameworkCore; using Newtonsoft.Json.Linq; using Sockeye.Util; using Sockeye.Api.ControllerHelpers; using Sockeye.Models; using Sockeye.PickList; namespace Sockeye.Biz { internal class PickListBiz : BizObject { internal PickListBiz(AyContext dbcontext, long currentUserId, long userTranslationId, AuthorizationRoles UserRoles) { ct = dbcontext; UserId = currentUserId; UserTranslationId = userTranslationId; CurrentUserRoles = UserRoles; BizType = SockType.PickListTemplate; } internal static PickListBiz GetBiz(AyContext ct, Microsoft.AspNetCore.Http.HttpContext httpContext = null) { if (httpContext != null) return new PickListBiz(ct, UserIdFromContext.Id(httpContext.Items), UserTranslationIdFromContext.Id(httpContext.Items), UserRolesFromContext.Roles(httpContext.Items)); else return new PickListBiz(ct, 1, ServerBootConfig.SOCKEYE_DEFAULT_TRANSLATION_ID, AuthorizationRoles.BizAdmin); } //////////////////////////////////////////////////////////////////////////////////////////////// /// GET //Get one internal async Task GetAsync(SockType sockType, bool logTheGetEvent = true) { long lTypeId = (long)sockType; //first try to fetch from db var ret = await ct.PickListTemplate.SingleOrDefaultAsync(z => z.Id == lTypeId); if (logTheGetEvent && ret != null) { //Log await EventLogProcessor.LogEventToDatabaseAsync(new Event(UserId, lTypeId, BizType, SockEvent.Retrieved), ct); } //not in db then get the default if (ret == null) { var PickList = PickListFactory.GetAyaPickList(sockType); if (PickList != null) { ret = new PickListTemplate(); ret.Id = lTypeId; ret.Template = PickList.DefaultTemplate; } } return ret; } //get picklist internal async Task> GetPickListAsync(IAyaPickList PickList, string query, bool inactive, long[] preIds, string variant, ILogger log, string template) { //Crack and validate the query part set a broken rule if not valid and return null //else do the query string TagSpecificQuery = null; string AutoCompleteQuery = null; //Here need to handle scenario of badly formed query so user knows they did it wrong and doesn't just assume it's not there //determine if this is a tag query and extract it bool HasQuery = !string.IsNullOrWhiteSpace(query); if (HasQuery) { AutoCompleteQuery = query; //is it a dual template and tag query? if (AutoCompleteQuery.Contains(" ")) { // split the query on space var querySegments = AutoCompleteQuery.Split(' '); if (querySegments.Length > 2) { AddError(ApiErrorCode.VALIDATION_INVALID_VALUE, "query", await Translate("ErrorPickListQueryInvalid")); return null; } //check the two query segments, it's valid for the user to put the tag first or the template query first //we handle either way, but if there are no tas in either then it's not a valid query if (querySegments[0].Contains("..")) { TagSpecificQuery = querySegments[0].Replace("..", ""); AutoCompleteQuery = querySegments[1]; } else if (querySegments[1].Contains("..")) { TagSpecificQuery = querySegments[1].Replace("..", ""); AutoCompleteQuery = querySegments[0]; } else { AddError(ApiErrorCode.VALIDATION_INVALID_VALUE, "query", await Translate("ErrorPickListQueryInvalid")); return null; } } else { //is it a tag only query? if (AutoCompleteQuery.Contains("..")) { TagSpecificQuery = AutoCompleteQuery.Replace("..", ""); AutoCompleteQuery = null; } } } //Final fixup if user specifies tag query but there are not tags on this object then //rather than error just accept it as a no tag query //Note: it's not valid to have more than one field with tags in the picklist definition so this works if (PickList.ColumnDefinitions.FirstOrDefault(z => z.ColumnDataType == UiFieldDataType.Tags) == null) { TagSpecificQuery = null; } //Autocomplete and tagonly query terms now set for consumption by PickListFetcher, ready to fetch... List items = await PickListFetcher.GetResponseAsync(PickList, AutoCompleteQuery, TagSpecificQuery, inactive, preIds, variant, ct, log, template); return items; } //get picklist display for a single item //used to populate UI with picklist format display for items internal async Task GetTemplatedNameAsync(SockType sockType, long id, string variant, ILogger log, string template) { //short circuit for empty types if (id == 0) { return string.Empty; } long[] preIds = { id }; var PickList = PickListFactory.GetAyaPickList(sockType); if (log == null) log = Sockeye.Util.ApplicationLogging.CreateLogger("PickListBiz::GetTemplatedNameAsync"); //Autocomplete and tagonly query terms now set for consumption by PickListFetcher, ready to fetch... List items = await PickListFetcher.GetResponseAsync(PickList, null, null, true, preIds, variant, ct, log, template); if (items.Count == 0) { return string.Empty; } return items[0].Name; } //get picklist templates, basically all the object types that support picklists internal List GetListOfAllPickListTypes(long translationId) { return PickListFactory.GetListOfAllPickListTypes(translationId); } //////////////////////////////////////////////////////////////////////////////////////////////// //UPDATE // //put internal async Task ReplaceAsync(PickListTemplate template) { var o = await ct.PickListTemplate.FirstOrDefaultAsync(z => z.Id == (long)template.Id); bool bAdd = false; if (o == null) { o = new PickListTemplate(); bAdd = true; } o.Id = (long)template.Id; o.Template = template.Template; Validate(o); if (HasErrors) return false; if (bAdd) await ct.PickListTemplate.AddAsync(o); await ct.SaveChangesAsync(); //Log modification and save context await EventLogProcessor.LogEventToDatabaseAsync(new Event(UserId, o.Id, BizType, SockEvent.Modified), ct); return true; } //////////////////////////////////////////////////////////////////////////////////////////////// //DELETE (return to default template) // internal async Task DeleteAsync(SockType sockType) { //REMOVE ANY RECORD WITH SAME AYATYPE ID long lTypeId = (long)sockType; var o = await ct.PickListTemplate.FirstOrDefaultAsync(z => z.Id == lTypeId); if (o != null) { ct.PickListTemplate.Remove(o); await ct.SaveChangesAsync(); //Event log process delete await EventLogProcessor.DeleteObjectLogAsync(UserId, BizType, lTypeId, sockType.ToString(), ct); } return true; } //////////////////////////////////////////////////////////////////////////////////////////////// //VALIDATION // //Can save or update? private void Validate(PickListTemplate inObj) { //validate that the template is valid, the type is legit etc var TemplateType = (SockType)inObj.Id; if (!TemplateType.HasAttribute(typeof(CoreBizObjectAttribute))) { AddError(ApiErrorCode.VALIDATION_INVALID_VALUE, "sockType", "SockType specified is not a Core object type and doesn't support pick list templates"); return; } if (string.IsNullOrWhiteSpace(inObj.Template)) AddError(ApiErrorCode.VALIDATION_REQUIRED, "Template"); var PickList = PickListFactory.GetAyaPickList(TemplateType); //Filter json must parse //this is all automated normally so not going to do too much parsing here //just ensure it's basically there if (!string.IsNullOrWhiteSpace(inObj.Template)) { try { var v = JArray.Parse(inObj.Template); for (int i = 0; i < v.Count; i++) { var filterItem = v[i]; if (filterItem["fld"] == null) AddError(ApiErrorCode.VALIDATION_REQUIRED, "Template", $"Template array item {i}, object is missing required \"fld\" property "); else { var fld = filterItem["fld"].Value(); if (string.IsNullOrWhiteSpace(fld)) AddError(ApiErrorCode.VALIDATION_REQUIRED, "Template", $"Template array item {i}, \"fld\" property is empty and required"); //validate the field name if we can if (PickList != null) { var TheField = PickList.ColumnDefinitions.SingleOrDefault(z => z.FieldKey.ToLowerInvariant() == fld.ToLowerInvariant()); if (TheField == null) { AddError(ApiErrorCode.VALIDATION_INVALID_VALUE, "Template", $"Template array item {i}, fld property value \"{fld}\" is not a valid value for SockType specified"); } } } } } catch (Newtonsoft.Json.JsonReaderException ex) { AddError(ApiErrorCode.VALIDATION_INVALID_VALUE, "Template", "Template is not valid JSON string: " + ex.Message); } } } ///////////////////////////////////////////////////////////////////// }//eoc }//eons