From 2ac227875fa10e741fdb29e2a190ce36af8a6ceb Mon Sep 17 00:00:00 2001 From: John Cardinal Date: Thu, 12 Mar 2020 18:37:31 +0000 Subject: [PATCH] --- .../AyaNova/Controllers/PickListController.cs | 37 +- server/AyaNova/biz/PickListBiz.cs | 323 +++++++++--------- server/AyaNova/models/AyContext.cs | 1 + server/AyaNova/models/PickListTemplate.cs | 15 + server/AyaNova/util/AySchema.cs | 14 + 5 files changed, 215 insertions(+), 175 deletions(-) create mode 100644 server/AyaNova/models/PickListTemplate.cs diff --git a/server/AyaNova/Controllers/PickListController.cs b/server/AyaNova/Controllers/PickListController.cs index 29a78208..487c0638 100644 --- a/server/AyaNova/Controllers/PickListController.cs +++ b/server/AyaNova/Controllers/PickListController.cs @@ -40,10 +40,6 @@ namespace AyaNova.Api.Controllers serverState = apiServerState; } -TODO: need a db schema table and objects to hold the picklist templates -//while they may have an ID to make them easier to work with, they are fetched and stored by the object type only (which is a type of id and unique so maybe it's still id -//but not an autonumber type id, have to check into that) - /// @@ -70,7 +66,9 @@ TODO: need a db schema table and objects to hold the picklist templates if (!ModelState.IsValid) return BadRequest(new ApiErrorResponse(ModelState)); - var o = await biz.GetPickListAsync(ayaType, query); + var UserRoles = UserRolesFromContext.Roles(HttpContext.Items); + + var o = await biz.GetPickListAsync(ayaType, query, UserRoles); if (o == null) return NotFound(new ApiErrorResponse(ApiErrorCode.NOT_FOUND)); @@ -84,14 +82,25 @@ TODO: need a db schema table and objects to hold the picklist templates /// /// List of strings [HttpGet("TemplateList")] - public ActionResult GetTemplateList() + public async Task GetTemplateList() { - if (!serverState.IsOpen) + if (!serverState.IsOpen) { return StatusCode(503, new ApiErrorResponse(ApiErrorCode.API_CLOSED, null, serverState.Reason)); } - return Ok(ApiOkResponse.Response(PickListFactory.GetListOfAllPickListKeyNames(), true)); + //Instantiate the business object handler + PickListBiz biz = PickListBiz.GetBiz(ct, HttpContext); + + + + var o = await biz.GetListOfAllPickListKeyNames(); + if (o == null) + return NotFound(new ApiErrorResponse(ApiErrorCode.NOT_FOUND)); + + + return Ok(ApiOkResponse.Response(o, true)); + } @@ -100,7 +109,7 @@ TODO: need a db schema table and objects to hold the picklist templates /// /// POST (replace) Pick List template /// - /// + /// /// /// [HttpPost("Template/{ayatype}")] @@ -132,7 +141,7 @@ TODO: need a db schema table and objects to hold the picklist templates return StatusCode(409, new ApiErrorResponse(ApiErrorCode.CONCURRENCY_CONFLICT)); } - return Ok(ApiOkResponse.Response(new { ConcurrencyToken = o.ConcurrencyToken }, true)); + return NoContent(); } @@ -140,10 +149,10 @@ TODO: need a db schema table and objects to hold the picklist templates /// Delete customized template /// (revert to default) /// - /// + /// /// Ok [HttpDelete("Template/{ayatype}")] - public async Task DeletePickListTemplate([FromRoute] AyaType ayatype) + public async Task DeletePickListTemplate([FromRoute] AyaType ayaType) { if (!serverState.IsOpen) return StatusCode(503, new ApiErrorResponse(ApiErrorCode.API_CLOSED, null, serverState.Reason)); @@ -154,11 +163,11 @@ TODO: need a db schema table and objects to hold the picklist templates //Instantiate the business object handler PickListBiz biz = PickListBiz.GetBiz(ct, HttpContext); - + if (!Authorized.HasDeleteRole(HttpContext.Items, biz.BizType)) return StatusCode(403, new ApiNotAuthorizedResponse()); - if (!await biz.DeleteAsync(o)) + if (!await biz.DeleteAsync(ayaType)) return BadRequest(new ApiErrorResponse(biz.Errors)); return NoContent(); diff --git a/server/AyaNova/biz/PickListBiz.cs b/server/AyaNova/biz/PickListBiz.cs index d5497e9b..69caa2b4 100644 --- a/server/AyaNova/biz/PickListBiz.cs +++ b/server/AyaNova/biz/PickListBiz.cs @@ -6,7 +6,7 @@ using Newtonsoft.Json.Linq; using AyaNova.Util; using AyaNova.Api.ControllerHelpers; using AyaNova.Models; -using AyaNova.DataList; +//using AyaNova.PickList; namespace AyaNova.Biz @@ -34,7 +34,7 @@ namespace AyaNova.Biz } - + //////////////////////////////////////////////////////////////////////////////////////////////// /// GET @@ -56,24 +56,20 @@ namespace AyaNova.Biz //get picklist - internal async Task> GetPickListAsync(AyaType ayaType, string query) + internal async Task> GetPickListAsync(AyaType ayaType, string query, AuthorizationRoles userRoles) { - //TODO: Use picklist objects to formulate and query and return results - List items = new List(); - // if (!string.IsNullOrWhiteSpace(listKey)) - // { - // items = await ct.DataListView - // .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(); + throw new System.NotImplementedException(); + // List items = await PickListFetcher.GetResponseAsync(ayaType, query, ct, UserId, userRoles); + // return items; + } - // } - return items; + + //get picklist templates, basically all the object types that support picklists + internal async Task> GetListOfAllPickListKeyNames() + { + throw new System.NotImplementedException(); + // List items = await PickListFetcher.GetResponseAsync(ayaType, query, ct, UserId, userRoles); + // return items; } @@ -84,59 +80,64 @@ namespace AyaNova.Biz // //put - internal async Task PutAsync(DataListView dbObj, DataListView inObj) + internal async Task ReplaceAsync(AyaType ayaType, string template) { - //preserve the owner ID if none was specified - if (inObj.UserId == 0) - inObj.UserId = dbObj.UserId; + throw new System.NotImplementedException(); + // //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; + // //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; - await ValidateAsync(dbObj, false); - if (HasErrors) - return false; - await ct.SaveChangesAsync(); + // await ValidateAsync(dbObj, false); + // if (HasErrors) + // return false; + // await ct.SaveChangesAsync(); - //Log modification and save context - await EventLogProcessor.LogEventToDatabaseAsync(new Event(UserId, dbObj.Id, BizType, AyaEvent.Modified), ct); + // //Log modification and save context + // await EventLogProcessor.LogEventToDatabaseAsync(new Event(UserId, dbObj.Id, BizType, AyaEvent.Modified), ct); - return true; + //return true; } //////////////////////////////////////////////////////////////////////////////////////////////// //DELETE // - internal async Task DeleteAsync(DataListView dbObj) + internal async Task DeleteAsync(AyaType ayaType) { + + //REMOVE ANY RECORD WITH SAME AYATYPE ID + throw new System.NotImplementedException(); + //Determine if the object can be deleted, do the deletion tentatively //Probably also in here deal with tags and associated search text etc //FUTURE POSSIBLE NEED //ValidateCanDelete(dbObj); - if (HasErrors) - return false; - ct.DataListView.Remove(dbObj); - await ct.SaveChangesAsync(); + // if (HasErrors) + // return false; + // ct.DataListView.Remove(dbObj); + // await ct.SaveChangesAsync(); - //Delete sibling objects + // //Delete sibling objects - //Event log process delete - await EventLogProcessor.DeleteObjectLogAsync(UserId, BizType, dbObj.Id, dbObj.Name, ct); + // //Event log process delete + // await EventLogProcessor.DeleteObjectLogAsync(UserId, BizType, dbObj.Id, dbObj.Name, ct); - //Delete search index - //Search.ProcessDeletedObjectKeywords(dbObj.Id, BizType); + // //Delete search index + // //Search.ProcessDeletedObjectKeywords(dbObj.Id, BizType); - return true; + // return true; } //////////////////////////////////////////////////////////////////////////////////////////////// @@ -147,164 +148,164 @@ namespace AyaNova.Biz private async Task ValidateAsync(DataListView inObj, bool isNew) { - //UserId required - if (!isNew) - { - if (inObj.UserId == 0) - AddError(ApiErrorCode.VALIDATION_REQUIRED, "UserId"); - } + // //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 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"); + // //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 (await ct.DataListView.AnyAsync(m => m.Name == inObj.Name && m.Id != inObj.Id)) - { - AddError(ApiErrorCode.VALIDATION_NOT_UNIQUE, "Name"); - } - } + // //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 (await ct.DataListView.AnyAsync(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"); + // if (string.IsNullOrWhiteSpace(inObj.ListKey)) + // AddError(ApiErrorCode.VALIDATION_REQUIRED, "ListKey"); - var DataList = DataListFactory.GetAyaDataList(inObj.ListKey); + // var DataList = DataListFactory.GetAyaDataList(inObj.ListKey); - if (DataList == null) - { - AddError(ApiErrorCode.VALIDATION_INVALID_VALUE, "ListKey", $"ListKey \"{inObj.ListKey}\" DataListKey is not valid"); - } + // if (DataList == null) + // { + // AddError(ApiErrorCode.VALIDATION_INVALID_VALUE, "ListKey", $"ListKey \"{inObj.ListKey}\" DataListKey is not valid"); + // } - if (inObj.ListKey.Length > 255) - AddError(ApiErrorCode.VALIDATION_LENGTH_EXCEEDED, "ListKey", "255 max"); + // if (inObj.ListKey.Length > 255) + // AddError(ApiErrorCode.VALIDATION_LENGTH_EXCEEDED, "ListKey", "255 max"); - //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.ListView)) - { - try - { - var v = JArray.Parse(inObj.ListView); - for (int i = 0; i < v.Count; i++) - { - var filterItem = v[i]; - if (filterItem["fld"] == null) - AddError(ApiErrorCode.VALIDATION_REQUIRED, "ListView", $"ListView array item {i}, object is missing required \"fld\" property "); - else - { - var fld = filterItem["fld"].Value(); - if (string.IsNullOrWhiteSpace(fld)) - AddError(ApiErrorCode.VALIDATION_REQUIRED, "ListView", $"ListView 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, "ListView", $"ListView array item {i}, fld property value \"{fld}\" is not a valid value for ListKey specified"); - } - - } - } - //This is the old filter validation code but at this point only going to validate that the fields are present and valid as the bare minimum - // 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 (!DataListFilterComparisonOperator.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 - // { - // //check if the value is present, not what it is exactly, just that it's present - // //value also could contain relative date tokens, not that it checks them anyway but just noting it here - // 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, "ListView", "ListView is not valid JSON string: " + ex.Message); - - } - } - - - // //VALIDATE SORT // //Filter json must parse - // if (!string.IsNullOrWhiteSpace(inObj.Sort)) + // //this is all automated normally so not going to do too much parsing here + // //just ensure it's basically there + // if (!string.IsNullOrWhiteSpace(inObj.ListView)) // { // try // { - // var v = JArray.Parse(inObj.Sort); + // var v = JArray.Parse(inObj.ListView); // 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 "); + // var filterItem = v[i]; + // if (filterItem["fld"] == null) + // AddError(ApiErrorCode.VALIDATION_REQUIRED, "ListView", $"ListView array item {i}, object is missing required \"fld\" property "); // else // { - // var fld = sortItem["fld"].Value(); + // var fld = filterItem["fld"].Value(); // if (string.IsNullOrWhiteSpace(fld)) - // AddError(ApiErrorCode.VALIDATION_REQUIRED, "Sort", $"Sort array item {i}, \"fld\" property is empty and required"); + // AddError(ApiErrorCode.VALIDATION_REQUIRED, "ListView", $"ListView 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)) + // var TheField = DataList.FieldDefinitions.SingleOrDefault(x => x.FieldKey.ToLowerInvariant() == fld); + + // if (TheField == null) // { - // AddError(ApiErrorCode.VALIDATION_INVALID_VALUE, "Sort", $"Sort array item {i}, fld property value \"{fld}\" is not a valid value for ListKey specified"); + // AddError(ApiErrorCode.VALIDATION_INVALID_VALUE, "ListView", $"ListView 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"); - // } + // //This is the old filter validation code but at this point only going to validate that the fields are present and valid as the bare minimum + // // 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 (!DataListFilterComparisonOperator.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 + // // { + // // //check if the value is present, not what it is exactly, just that it's present + // // //value also could contain relative date tokens, not that it checks them anyway but just noting it here + // // 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, "Sort", "Sort is not valid JSON string: " + ex.Message); + // AddError(ApiErrorCode.VALIDATION_INVALID_VALUE, "ListView", "ListView 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; } diff --git a/server/AyaNova/models/AyContext.cs b/server/AyaNova/models/AyContext.cs index f1fb38d4..6a06541b 100644 --- a/server/AyaNova/models/AyContext.cs +++ b/server/AyaNova/models/AyContext.cs @@ -20,6 +20,7 @@ namespace AyaNova.Models public virtual DbSet DataListView { get; set; } public virtual DbSet Tag { get; set; } public virtual DbSet FormCustom { get; set; } + public virtual DbSet PickListTemplate { get; set; } //Note: had to add this constructor to work with the code in startup.cs that gets the connection string from the appsettings.json file diff --git a/server/AyaNova/models/PickListTemplate.cs b/server/AyaNova/models/PickListTemplate.cs new file mode 100644 index 00000000..fd1491fb --- /dev/null +++ b/server/AyaNova/models/PickListTemplate.cs @@ -0,0 +1,15 @@ +using System.ComponentModel.DataAnnotations; + +namespace AyaNova.Models +{ + + //See core-picklist-autocomplete-template-system.txt for details + public partial class PickListTemplate + { + public long Id { get; set; } + public uint ConcurrencyToken { get; set; } + + public string Template { get; set; }//JSON fragment of picklist template + + } +} diff --git a/server/AyaNova/util/AySchema.cs b/server/AyaNova/util/AySchema.cs index ad032b44..c796ac30 100644 --- a/server/AyaNova/util/AySchema.cs +++ b/server/AyaNova/util/AySchema.cs @@ -296,6 +296,20 @@ namespace AyaNova.Util } + + ////////////////////////////////////////////////// + //PICKLISTTEMPLATE table + if (currentSchema < 10) + { + LogUpdateMessage(log); + + await ExecQueryAsync("CREATE TABLE apicklisttemplate (id integer NOT NULL PRIMARY KEY, " + + "template text)"); + await SetSchemaLevelAsync(++currentSchema); + } + + + //MAKE SURE THE DESIRED SCHEMA WAS SET PROPERLY if (currentSchema > DESIRED_SCHEMA_LEVEL) throw new ArgumentOutOfRangeException("AySchema::DesiredSchemaLevel WASN'T SET PROPERLY");