diff --git a/devdocs/todo.txt b/devdocs/todo.txt index 163b9e73..2641676b 100644 --- a/devdocs/todo.txt +++ b/devdocs/todo.txt @@ -1,6 +1,6 @@ # SERVER TODO (J.F.C. - Just fucking code it already) -Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpYXQiOiIxNTQ0NTU5NzAwIiwiZXhwIjoiMTU0NzE1MTcwMCIsImlzcyI6ImF5YW5vdmEuY29tIiwiaWQiOiIxIiwiYXlhbm92YS9yb2xlcyI6IjMyNzY3In0.fMq_8Dvia63rzN_U2zjczPvUNM40OEAeI4VOeV6ulGw +Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpYXQiOiIxNTQ3NTgwMzg2IiwiZXhwIjoiMTU1MDE3MjM4NiIsImlzcyI6ImF5YW5vdmEuY29tIiwiaWQiOiIxIiwiYXlhbm92YS9yb2xlcyI6IjMyNzY3In0.duYSuTC2v9dCD7POPTvRNB2B4tGCkNQ1UDkgaSrNSYo ## IMMEDIATE ITEMS diff --git a/server/AyaNova/Controllers/FormCustomController.cs b/server/AyaNova/Controllers/FormCustomController.cs index 9afdc792..2ba54473 100644 --- a/server/AyaNova/Controllers/FormCustomController.cs +++ b/server/AyaNova/Controllers/FormCustomController.cs @@ -125,6 +125,53 @@ namespace AyaNova.Api.Controllers } } + /// + /// Get available types allowed for Custom fields + /// Used to build UI for customizing a form + /// + /// Required roles: + /// BizAdminFull only has rights to customize forms + /// + /// + /// A list of type string values valid for custom fields + [HttpGet("AvailableCustomTypes")] + public ActionResult GetAvailableCustomTypes() + { + if (serverState.IsClosed) + return StatusCode(503, new ApiErrorResponse(ApiErrorCode.API_CLOSED, null, serverState.Reason)); + + if (!Authorized.IsAuthorizedToReadFullRecord(HttpContext.Items, AyaType.FormCustom)) + return StatusCode(401, new ApiNotAuthorizedResponse()); + + if (!ModelState.IsValid) + return BadRequest(new ApiErrorResponse(ModelState)); + return Ok(new ApiOkResponse(AyDataType.ValidCustomFieldTypes)); + } + + + /// + /// Get a list of all customizable form keys + /// Used to build UI for customizing a form + /// + /// Required roles: + /// BizAdminFull only has rights to customize forms + /// + /// + /// A list of string formKey values valid for customization + [HttpGet("AvailableCustomizableFormKeys")] + public ActionResult GetAvailableCustomizableFormKeys() + { + if (serverState.IsClosed) + return StatusCode(503, new ApiErrorResponse(ApiErrorCode.API_CLOSED, null, serverState.Reason)); + + if (!Authorized.IsAuthorizedToReadFullRecord(HttpContext.Items, AyaType.FormCustom)) + return StatusCode(401, new ApiNotAuthorizedResponse()); + + if (!ModelState.IsValid) + return BadRequest(new ApiErrorResponse(ModelState)); + return Ok(new ApiOkResponse(FormAvailableFields.AvailableFormKeys)); + } + diff --git a/server/AyaNova/biz/AyDataType.cs b/server/AyaNova/biz/AyDataType.cs index e1225030..d075d6e7 100644 --- a/server/AyaNova/biz/AyDataType.cs +++ b/server/AyaNova/biz/AyDataType.cs @@ -1,3 +1,5 @@ +using System.Collections.Generic; + namespace AyaNova.Biz { public static class AyDataType @@ -10,5 +12,19 @@ namespace AyaNova.Biz public const string Tags = "tags"; public const string Enum = "enum";//enums are just integers in the db so the sql code handles it like an int, but the client needs to know it's an enum + public static List ValidCustomFieldTypes + { + get + { + var ret = new List(); + ret.Add(AyDataType.Date); + ret.Add(AyDataType.Text); + ret.Add(AyDataType.Integer); + ret.Add(AyDataType.Bool); + ret.Add(AyDataType.Decimal); + return ret; + } + } + } } diff --git a/server/AyaNova/biz/FormCustomBiz.cs b/server/AyaNova/biz/FormCustomBiz.cs index 90d3b4c1..164d46b0 100644 --- a/server/AyaNova/biz/FormCustomBiz.cs +++ b/server/AyaNova/biz/FormCustomBiz.cs @@ -149,16 +149,23 @@ namespace AyaNova.Biz // if (inObj.OwnerId != UserId) // AddError(ValidationErrorType.InvalidValue, "OwnerId", "OwnerId must be current user Id"); - //FormKey required + //FormKey required and must be valid if (string.IsNullOrWhiteSpace(inObj.FormKey)) AddError(ValidationErrorType.RequiredPropertyEmpty, "FormKey"); + else + { + if (!FormAvailableFields.IsValidFormKey(inObj.FormKey)) + { + AddError(ValidationErrorType.InvalidValue, "FormKey"); + } + } //FormKey must be less than 255 characters if (inObj.FormKey.Length > 255) AddError(ValidationErrorType.LengthExceeded, "FormKey", "255 max"); //If name is otherwise OK, check that name is unique - if (!PropertyHasErrors("FormKey")) + if (!PropertyHasErrors("FormKey") && isNew) { //Use Any command is efficient way to check existance, it doesn't return the record, just a true or false if (ct.FormCustom.Any(m => m.FormKey == inObj.FormKey && m.Id != inObj.Id)) @@ -169,9 +176,12 @@ namespace AyaNova.Biz + //Template json must parse - if (!string.IsNullOrWhiteSpace(inObj.Template)) + if ((!PropertyHasErrors("FormKey") && !string.IsNullOrWhiteSpace(inObj.Template))) { + var ValidCustomFieldTypes = AyDataType.ValidCustomFieldTypes; + var ValidFormFields = FormAvailableFields.FormFields(inObj.FormKey); try { var v = JArray.Parse(inObj.Template); @@ -182,7 +192,7 @@ namespace AyaNova.Biz */ //Validate the following: - //FormKey is valid value + //All fields specified are valid values //Field is allowed to be hidden if hidden //Type is one of the valid types: @@ -194,59 +204,90 @@ namespace AyaNova.Biz public const string Decimal = "decimal"; */ - TODO: validate the json + //TODO: validate the json /* - JSON FRAGMENT holds items that differ from stock, no "core" fields allowed - FieldKey - Hide - Required - Type (checkbox, date, date time, decimal, number, picklist(FUTURE), and text) + + {[ + { + "fld": "WidgetCustom1", + "hide": false, + "required": true, + "type": "date" + }, + { + "fld": "WidgetCustom2", + "hide": true, + "required": false, + "type": "bool" + } +]} */ - // for (int i = 0; i < v.Count; i++) - // { - // var filterItem = v[i]; - // if (filterItem["fld"] == null) - // AddError(ValidationErrorType.RequiredPropertyEmpty, "Filter", $"Filter array item {i}, object is missing required \"fld\" property "); - // else - // { - // var fld = filterItem["fld"].Value(); - // if (string.IsNullOrWhiteSpace(fld)) - // AddError(ValidationErrorType.RequiredPropertyEmpty, "Filter", $"Filter array item {i}, \"fld\" property is empty and required"); - // //validate the field name if we can - // if (ListValidFilterOptions != null) - // { + for (int i = 0; i < v.Count; i++) + { + FormField MasterFormField = null; - // if (!ListValidFilterOptions.Flds.Exists(x => x.Fld == fld)) - // { - // AddError(ValidationErrorType.InvalidValue, "Filter", $"Filter array item {i}, fld property value \"{fld}\" is not a valid value for ListKey specified"); - // } + var formFieldItem = v[i]; + if (formFieldItem["fld"] == null) + AddError(ValidationErrorType.RequiredPropertyEmpty, "Template", $"Template array item {i}, object is missing required \"fld\" property "); + else + { + var fldKey = formFieldItem["fld"].Value(); + if (string.IsNullOrWhiteSpace(fldKey)) + AddError(ValidationErrorType.RequiredPropertyEmpty, "Template", $"Template array item {i}, \"fld\" property is empty and required"); - // } - // } - // if (filterItem["op"] == null) - // AddError(ValidationErrorType.RequiredPropertyEmpty, "Filter", $"Filter array item {i}, object is missing required \"op\" property "); - // else - // { - // var opType = filterItem["op"].Value(); - // if (!FilterComparisonOperator.Operators.Contains(opType)) - // AddError(ValidationErrorType.InvalidValue, "Filter", $"Filter array item {i}, \"op\" property value of \"{opType}\" is not a valid FilterComparisonOperator type"); - // } + //validate the field name if we can + if (ValidFormFields != null) + { - // if (filterItem["value"] == null) - // AddError(ValidationErrorType.RequiredPropertyEmpty, "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(ValidationErrorType.RequiredPropertyEmpty, "Filter", $"Filter array item {i}, object is missing or is empty the required \"value\" property "); + if (!ValidFormFields.Exists(x => x.Key == fldKey)) + { + AddError(ValidationErrorType.InvalidValue, "Template", $"Template array item {i}, fld property value \"{fldKey}\" is not a valid form field value for formKey specified"); + } + else + { + MasterFormField = ValidFormFields.FirstOrDefault(x => x.Key == fldKey); + } - // if (filterItem["value"].Type == JTokenType.Array && filterItem["value"].Count() == 0) - // AddError(ValidationErrorType.RequiredPropertyEmpty, "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 - // } + if (MasterFormField != null) + { + + if (formFieldItem["hide"] != null) + { + var fieldHideValue = formFieldItem["hide"].Value(); + if (!MasterFormField.Hideable && fieldHideValue == true) + AddError(ValidationErrorType.InvalidValue, "Template", $"Template array item {i}, \"hide\" property value of \"{fieldHideValue}\" is not valid, this field is core and cannot be hidden"); + } + + if (formFieldItem["type"] != null) + { + if (!MasterFormField.Custom) + AddError(ValidationErrorType.InvalidValue, "Template", $"Template array item {i}, \"type\" property value is not valid, only Custom fields can have types specified"); + else + {//It is a custom field, is it a valid type value + var templateFieldCustomTypeValue = formFieldItem["type"].Value(); + if (!ValidCustomFieldTypes.Contains(templateFieldCustomTypeValue)) + AddError(ValidationErrorType.InvalidValue, "Template", $"Template array item {i}, \"type\" property value of \"{templateFieldCustomTypeValue}\" is not a valid custom field type"); + } + } + + } + + if (formFieldItem["required"] == null) + AddError(ValidationErrorType.RequiredPropertyEmpty, "Template", $"Template array item {i}, object is missing required \"required\" property "); + + + //NOTE: value of nothing, null or empty is a valid value so no checking for it here + } } catch (Newtonsoft.Json.JsonReaderException ex) {