using System.Linq; using System.Threading.Tasks; using System.Collections.Generic; using Microsoft.EntityFrameworkCore; using Newtonsoft.Json.Linq; using AyaNova.Util; using AyaNova.Api.ControllerHelpers; using AyaNova.Models; using AyaNova.DataList; namespace AyaNova.Biz { internal class DataListTemplateBiz : BizObject { internal DataListTemplateBiz(AyContext dbcontext, long currentUserId, long userLocaleId, AuthorizationRoles UserRoles) { ct = dbcontext; UserId = currentUserId; UserLocaleId = userLocaleId; CurrentUserRoles = UserRoles; BizType = AyaType.DataListFilter; } internal static DataListTemplateBiz GetBiz(AyContext ct, Microsoft.AspNetCore.Http.HttpContext httpContext) { return new DataListTemplateBiz(ct, UserIdFromContext.Id(httpContext.Items), UserLocaleIdFromContext.Id(httpContext.Items), UserRolesFromContext.Roles(httpContext.Items)); } //Version for internal use internal static DataListTemplateBiz GetBizInternal(AyContext ct) { return new DataListTemplateBiz(ct, 1, ServerBootConfig.AYANOVA_DEFAULT_LANGUAGE_ID, AuthorizationRoles.BizAdminFull); } // //////////////////////////////////////////////////////////////////////////////////////////////// // //EXISTS // internal async Task ExistsAsync(long id) // { // return await ct.DataListTemplate.AnyAsync(e => e.Id == id); // } // //////////////////////////////////////////////////////////////////////////////////////////////// // //CREATE // internal async Task CreateAsync(DataListTemplate inObj) // { // Validate(inObj, true); // if (HasErrors) // return null; // else // { // //do stuff with datafilter // DataListTemplate outObj = inObj; // outObj.UserId = UserId; // await ct.DataListTemplate.AddAsync(outObj); // await ct.SaveChangesAsync(); // //Handle child and associated items: // //EVENT LOG // EventLogProcessor.LogEventToDatabase(new Event(UserId, outObj.Id, BizType, AyaEvent.Created), ct); // //SEARCH INDEXING // // Search.ProcessNewObjectKeywords(UserLocaleId, outObj.Id, BizType, outObj.Name, outObj.Name); // return outObj; // } // } // //////////////////////////////////////////////////////////////////////////////////////////////// // //CREATE // internal DataListTemplate Create(AyContext TempContext, DataListTemplate inObj) // { // Validate(inObj, true); // if (HasErrors) // return null; // else // { // //do stuff with datafilter // DataListTemplate outObj = inObj; // outObj.UserId = UserId; // TempContext.DataListTemplate.Add(outObj); // TempContext.SaveChanges(); // //Handle child and associated items: // //EVENT LOG // EventLogProcessor.LogEventToDatabase(new Event(UserId, outObj.Id, BizType, AyaEvent.Created), TempContext); // //SEARCH INDEXING // // Search.ProcessNewObjectKeywords(UserLocaleId, outObj.Id, BizType, outObj.Name, outObj.Name); // return outObj; // } // } //////////////////////////////////////////////////////////////////////////////////////////////// /// GET //Get one internal async Task GetAsync(string DataListKey, bool log, IAyaDataList dataList) { //DataListKey always exists, if not in db then in default form var ret = await ct.DataListTemplate.SingleOrDefaultAsync(m => m.DataListKey == DataListKey); if (log) { //Log EventLogProcessor.LogEventToDatabase(new Event(UserId, ret.Id, BizType, AyaEvent.Retrieved), ct); } return ret; } // //////////////////////////////////////////////////////////////////////////////////////////////// // /// GET // internal async Task GetNoLogAsync(long fetchId) // { // //This is simple so nothing more here, but often will be copying to a different output object or some other ops // return await ct.DataListTemplate.SingleOrDefaultAsync(m => m.Id == fetchId); // } // //get picklist (NOT PAGED) // internal async Task> GetPickListAsync(string listKey) // { // List items = new List(); // if (!string.IsNullOrWhiteSpace(listKey)) // { // items = await ct.DataListTemplate // .AsNoTracking() // .Where(m => m.ListKey == listKey && (m.Public == true || m.UserId == UserId)) // .OrderBy(m => m.Name) // .Select(m => new NameIdItem() // { // Id = m.Id, // Name = m.Name // }).ToListAsync(); // } // return items; // } //////////////////////////////////////////////////////////////////////////////////////////////// //UPDATE // //put internal bool Put(DataListTemplate dbObj, DataListTemplate inObj) { //preserve the owner ID if none was specified if (inObj.UserId == 0) inObj.UserId = dbObj.UserId; //Replace the db object with the PUT object CopyObject.Copy(inObj, dbObj, "Id"); //Set "original" value of concurrency token to input token //this will allow EF to check it out ct.Entry(dbObj).OriginalValues["ConcurrencyToken"] = inObj.ConcurrencyToken; Validate(dbObj, false); if (HasErrors) return false; //Log modification EventLogProcessor.LogEventToDatabase(new Event(UserId, dbObj.Id, BizType, AyaEvent.Modified), ct); //Update keywords // Search.ProcessUpdatedObjectKeywords(UserLocaleId, dbObj.Id, BizType, dbObj.Name, dbObj.Name); return true; } //////////////////////////////////////////////////////////////////////////////////////////////// //DELETE // internal bool Delete(DataListTemplate dbObj) { //Determine if the object can be deleted, do the deletion tentatively //Probably also in here deal with tags and associated search text etc ValidateCanDelete(dbObj); if (HasErrors) return false; ct.DataListTemplate.Remove(dbObj); ct.SaveChanges(); //Delete sibling objects //Event log process delete EventLogProcessor.DeleteObject(UserId, BizType, dbObj.Id, dbObj.Name, ct); ct.SaveChanges(); //Delete search index //Search.ProcessDeletedObjectKeywords(dbObj.Id, BizType); return true; } //////////////////////////////////////////////////////////////////////////////////////////////// //VALIDATION // //Can save or update? private void Validate(DataListTemplate inObj, bool isNew) { //UserId required if (!isNew) { if (inObj.UserId == 0) AddError(ApiErrorCode.VALIDATION_REQUIRED, "UserId"); } //Name required if (string.IsNullOrWhiteSpace(inObj.Name)) AddError(ApiErrorCode.VALIDATION_REQUIRED, "Name"); //Name must be less than 255 characters if (inObj.Name.Length > 255) AddError(ApiErrorCode.VALIDATION_LENGTH_EXCEEDED, "Name", "255 max"); //If name is otherwise OK, check that name is unique if (!PropertyHasErrors("Name")) { //Use Any command is efficient way to check existance, it doesn't return the record, just a true or false if (ct.DataListTemplate.Any(m => m.Name == inObj.Name && m.Id != inObj.Id)) { AddError(ApiErrorCode.VALIDATION_NOT_UNIQUE, "Name"); } } if (string.IsNullOrWhiteSpace(inObj.ListKey)) AddError(ApiErrorCode.VALIDATION_REQUIRED, "ListKey"); var DataList = DataListFactory.GetAyaDataList(inObj.ListKey); // List FieldList = null; //if (!AyaFormFieldDefinitions.IsValidFormFieldDefinitionKey(inObj.ListKey)) if (DataList == null) { AddError(ApiErrorCode.VALIDATION_INVALID_VALUE, "ListKey", $"ListKey \"{inObj.ListKey}\" DataListKey is not valid"); } // else // { // FieldList = AyaDataListFieldDefinition.AyaObjectFields(inObj.ListKey); // } if (inObj.ListKey.Length > 255) AddError(ApiErrorCode.VALIDATION_LENGTH_EXCEEDED, "ListKey", "255 max"); //Filter json must parse if (!string.IsNullOrWhiteSpace(inObj.Filter)) { try { var v = JArray.Parse(inObj.Filter); for (int i = 0; i < v.Count; i++) { var filterItem = v[i]; if (filterItem["fld"] == null) AddError(ApiErrorCode.VALIDATION_REQUIRED, "Filter", $"Filter array item {i}, object is missing required \"fld\" property "); else { var fld = filterItem["fld"].Value(); if (string.IsNullOrWhiteSpace(fld)) AddError(ApiErrorCode.VALIDATION_REQUIRED, "Filter", $"Filter array item {i}, \"fld\" property is empty and required"); //validate the field name if we can if (DataList != null) { var TheField = DataList.FieldDefinitions.SingleOrDefault(x => x.FieldKey.ToLowerInvariant() == fld); if (TheField == null) { AddError(ApiErrorCode.VALIDATION_INVALID_VALUE, "Filter", $"Filter array item {i}, fld property value \"{fld}\" is not a valid value for ListKey specified"); } else if (TheField.IsFilterable == false) { AddError(ApiErrorCode.VALIDATION_INVALID_VALUE, "Filter", $"Filter array item {i}, fld property value \"{fld}\" is not filterable"); } } } if (filterItem["op"] == null) AddError(ApiErrorCode.VALIDATION_REQUIRED, "Filter", $"Filter array item {i}, object is missing required \"op\" property "); else { var opType = filterItem["op"].Value(); if (!DataListTemplateComparisonOperator.Operators.Contains(opType)) AddError(ApiErrorCode.VALIDATION_INVALID_VALUE, "Filter", $"Filter array item {i}, \"op\" property value of \"{opType}\" is not a valid FilterComparisonOperator type"); } if (filterItem["value"] == null) AddError(ApiErrorCode.VALIDATION_REQUIRED, "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(ApiErrorCode.VALIDATION_REQUIRED, "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(ApiErrorCode.VALIDATION_REQUIRED, "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 } } catch (Newtonsoft.Json.JsonReaderException ex) { AddError(ApiErrorCode.VALIDATION_INVALID_VALUE, "Filter", "Filter is not valid JSON string: " + ex.Message); } } //VALIDATE SORT //Filter json must parse if (!string.IsNullOrWhiteSpace(inObj.Sort)) { try { var v = JArray.Parse(inObj.Sort); for (int i = 0; i < v.Count; i++) { var sortItem = v[i]; if (sortItem["fld"] == null) AddError(ApiErrorCode.VALIDATION_REQUIRED, "Sort", $"Sort array item {i}, object is missing required \"fld\" property "); else { var fld = sortItem["fld"].Value(); if (string.IsNullOrWhiteSpace(fld)) AddError(ApiErrorCode.VALIDATION_REQUIRED, "Sort", $"Sort array item {i}, \"fld\" property is empty and required"); //validate the field name if we can if (DataList != null) { if (!DataList.FieldDefinitions.Exists(x => x.FieldKey.ToLowerInvariant() == fld && x.IsFilterable)) { AddError(ApiErrorCode.VALIDATION_INVALID_VALUE, "Sort", $"Sort array item {i}, fld property value \"{fld}\" is not a valid value for ListKey specified"); } } } if (sortItem["dir"] == null) AddError(ApiErrorCode.VALIDATION_REQUIRED, "Sort", $"Sort array item {i}, object is missing required \"dir\" sort direction property "); else { var sortDir = sortItem["dir"].Value(); if (sortDir != "+" && sortDir != "-") AddError(ApiErrorCode.VALIDATION_INVALID_VALUE, "Sort", $"Sort array item {i}, \"dir\" property value of \"{sortDir}\" is not a valid sort direction value, must be \"+\" or \"-\" only"); } //NOTE: value of nothing, null or empty is a valid value so no checking for it here } } catch (Newtonsoft.Json.JsonReaderException ex) { AddError(ApiErrorCode.VALIDATION_INVALID_VALUE, "Sort", "Sort is not valid JSON string: " + ex.Message); } } return; } //Can delete? private void ValidateCanDelete(DataListTemplate inObj) { //Leaving this off for now } // //////////////////////////////////////////////////////////////////////////////////////////////// // //JOB / OPERATIONS // // // public async Task HandleJobAsync(OpsJob job) // { // //Hand off the particular job to the corresponding processing code // //NOTE: If this code throws an exception the caller (JobsBiz::ProcessJobsAsync) will automatically set the job to failed and log the exeption so // //basically any error condition during job processing should throw up an exception if it can't be handled // switch (job.JobType) // { // case JobType.TestDataFilterJob: // await ProcessTestJobAsync(job); // break; // default: // throw new System.ArgumentOutOfRangeException($"DataFilterBiz.HandleJob-> Invalid job type{job.JobType.ToString()}"); // } // } //Other job handlers here... ///////////////////////////////////////////////////////////////////// }//eoc }//eons