From dcc86dc90f3aa6b36f76edeef584361858e0490a Mon Sep 17 00:00:00 2001 From: John Cardinal Date: Tue, 1 Sep 2020 18:16:13 +0000 Subject: [PATCH] --- .../AyaNova/Controllers/ReportController.cs | 1 - server/AyaNova/DataList/DataListFetcher.cs | 152 +++++------------- .../DataList/DataListSqlSelectBuilder.cs | 92 ++++++----- 3 files changed, 94 insertions(+), 151 deletions(-) diff --git a/server/AyaNova/Controllers/ReportController.cs b/server/AyaNova/Controllers/ReportController.cs index af07a4b9..b35e3ac7 100644 --- a/server/AyaNova/Controllers/ReportController.cs +++ b/server/AyaNova/Controllers/ReportController.cs @@ -230,7 +230,6 @@ namespace AyaNova.Api.Controllers reportData = await biz.GetReportData(reportDataParam.ObjectType, rowIds); } - if (reportData == null) return BadRequest(new ApiErrorResponse(biz.Errors)); else diff --git a/server/AyaNova/DataList/DataListFetcher.cs b/server/AyaNova/DataList/DataListFetcher.cs index dcd6d76f..b5bb58fe 100644 --- a/server/AyaNova/DataList/DataListFetcher.cs +++ b/server/AyaNova/DataList/DataListFetcher.cs @@ -201,31 +201,31 @@ namespace AyaNova.DataList } -//Get a list of id's for reporting - internal static async Task GetIdListResponseAsync(string DataListKey, string ListView, AyContext ct, long UserId, AuthorizationRoles UserRoles, ILogger log) + //Get a list of id's for reporting + internal static async Task GetIdListResponseAsync(string dataListKey, string listView, AyContext ct, long userId, AuthorizationRoles userRoles, ILogger log) { - var DataList = DataListFactory.GetAyaDataList(DataListKey); + var DataList = DataListFactory.GetAyaDataList(dataListKey); //was the name not found as a list? if (DataList == null) { - throw new System.ArgumentOutOfRangeException($"DataList \"{DataListKey}\" specified does not exist"); + throw new System.ArgumentOutOfRangeException($"DataList \"{dataListKey}\" specified does not exist"); } //check rights - if (!UserRoles.HasAnyFlags(DataList.AllowedRoles)) + if (!userRoles.HasAnyFlags(DataList.AllowedRoles)) throw new System.UnauthorizedAccessException("User roles insufficient for this datalist"); //do we need to default the listView? - if (string.IsNullOrWhiteSpace(listOptions.ListView)) + if (string.IsNullOrWhiteSpace(listView)) { - listOptions.ListView = DataList.DefaultListView; + listView = DataList.DefaultListView; } //parse the list view - var ListViewArray = JArray.Parse(listOptions.ListView); + var ListViewArray = JArray.Parse(listView); //Get the field key names in a list from the listview @@ -233,7 +233,7 @@ namespace AyaNova.DataList //BUILD THE QUERY //SELECT FRAGMENT COLUMNS FROM TEMPLATE - var SelectBuild = DataListSqlSelectBuilder.Build(DataList.FieldDefinitions, ListViewFieldList); + var SelectBuild = DataListSqlSelectBuilder.BuildForReportIdListOnly(DataList.FieldDefinitions, ListViewFieldList); //FROM CLAUSE var qFrom = DataList.SQLFrom; @@ -242,28 +242,20 @@ namespace AyaNova.DataList var qOrderBy = string.Empty; //WHERE CLAUSE - FILTER - qWhere = DataListSqlFilterCriteriaBuilder.DataFilterToSQLCriteria(DataList.FieldDefinitions, ListViewArray, UserId); + qWhere = DataListSqlFilterCriteriaBuilder.DataFilterToSQLCriteria(DataList.FieldDefinitions, ListViewArray, userId); //ORDER BY CLAUSE - SORT //BUILD ORDER BY qOrderBy = DataListSqlFilterOrderByBuilder.DataFilterToSQLOrderBy(DataList.FieldDefinitions, ListViewArray); - //LIMIT AND OFFSET CLAUSE - PAGING - listOptions.Offset = listOptions.Offset ?? ListOptions.DefaultOffset; - listOptions.Limit = listOptions.Limit ?? ListOptions.DefaultLimit; - var qLimitOffset = $"LIMIT {listOptions.Limit} OFFSET {listOptions.Offset}"; //PUT IT ALL TOGETHER string qDataQuery = string.Empty; - string qTotalRecordsQuery = string.Empty; - qDataQuery = $"{SelectBuild.Select} {qFrom} {qWhere} {qOrderBy} {qLimitOffset}".Replace(" ", " "); - qTotalRecordsQuery = $"SELECT COUNT(*) {qFrom} {qWhere}".Replace(" ", " "); + qDataQuery = $"{SelectBuild.Select} {qFrom} {qWhere} {qOrderBy} ".Replace(" ", " "); //RETURN OBJECTS - int returnRowColumnCount = ListViewFieldList.Count(); - List> rows = new List>(); - long totalRecordCount = 0; + var retList = new List(); //QUERY THE DB using (var command = ct.Database.GetDbConnection().CreateCommand()) @@ -278,114 +270,52 @@ namespace AyaNova.DataList { while (dr.Read()) { - List row = new List(returnRowColumnCount); + + retList.Add(dr.GetInt64(0)); + // //INSERT REMAINING FIELDS FROM TEMPLATE INTO THE RETURN ROWS LIST + // foreach (string TemplateField in ListViewFieldList) + // { + + // //get the AyaObjectFieldDefinition + // AyaDataListFieldDefinition f = DataList.FieldDefinitions.FirstOrDefault(z => z.FieldKey == TemplateField); - //INSERT REMAINING FIELDS FROM TEMPLATE INTO THE RETURN ROWS LIST - foreach (string TemplateField in ListViewFieldList) - { + // AyaFieldData AyaField = new AyaFieldData(); + // AyaField.v = dr.GetValue(SelectBuild.map[f.GetSqlValueColumnName()]); - //get the AyaObjectFieldDefinition - AyaDataListFieldDefinition f = DataList.FieldDefinitions.FirstOrDefault(z => z.FieldKey == TemplateField); - if (f.IsCustomField) - { + // if (f.IsRowId) + // { + // AyaField.rid = true; + // } + // else + // { + // AyaField.rid = null; + // } - AyaFieldData AyaField = new AyaFieldData(); - var cust = dr.GetString(SelectBuild.map[f.GetSqlValueColumnName()]); - if (!string.IsNullOrWhiteSpace(cust)) - { - JObject j = JObject.Parse(cust); - //convert field name to cust name then get value - var InternalCustomFieldName = AyaFormFieldDefinitions.TranslateLTCustomFieldToInternalCustomFieldName(TemplateField); - //Sometimes a custom field is specified but doesn't exist in the collection so don't assume it's there - // AyaField.v = j[InternalCustomFieldName].Value(); - JToken o = j[InternalCustomFieldName]; - if (o != null) - AyaField.v = o.Value(); - else - AyaField.v = null; + // if (f.SqlIdColumnName != null) + // { + // var ordinal = SelectBuild.map[f.SqlIdColumnName]; + // if (!await dr.IsDBNullAsync(ordinal)) + // AyaField.i = dr.GetInt64(ordinal); - row.Add(AyaField); - } - - /* - TODO: Custom field handling - GetName works just not with multipart identifiers - I could force naming by making all fields and AS, or - I could map the ordinal when generating the Select fields so that I have a map to refer to here - mapping in advance actually makes a lot of sense, then no more of this fragility of going by pointer index and hoping for the best - it would just be premapped out. - - dr.GetOrdinal(f.SqlValueColumnName) - 'dr.GetOrdinal(f.SqlValueColumnName)' threw an exception of type 'System.IndexOutOfRangeException' - f.SqlValueColumnName - "awidget.customfields" - dr.GetName(nCurrentColumnPointer) - "customfields" - dr.GetOrdinal("customfields"); - 5 - - */ - } - else - { - AyaFieldData AyaField = new AyaFieldData(); - AyaField.v = dr.GetValue(SelectBuild.map[f.GetSqlValueColumnName()]); - - if (f.IsRowId) - { - AyaField.rid = true; - } - else - { - AyaField.rid = null; - } - - if (f.SqlIdColumnName != null) - { - var ordinal = SelectBuild.map[f.SqlIdColumnName]; - if (!await dr.IsDBNullAsync(ordinal)) - AyaField.i = dr.GetInt64(ordinal); - - } - row.Add(AyaField); - } - } - rows.Add(row); - } - } - - //GET TOTAL RECORD COUNT - command.CommandText = qTotalRecordsQuery; - using (var dr = await command.ExecuteReaderAsync()) - { - if (dr.Read()) - { - totalRecordCount = dr.GetInt64(0); + // } + // } } } } catch (Npgsql.PostgresException e) { //log out the exception and the query - log.LogInformation("DataList query failed unexpectedly. Data Query was:"); + log.LogInformation("DataListFetcher:GetIdListResponseAsync query failed unexpectedly. Data Query was:"); log.LogInformation(qDataQuery); - log.LogInformation("Count Query was:"); - log.LogInformation(qTotalRecordsQuery); + log.LogInformation(e, "DB Exception"); - throw new System.Exception("DataListFetcher - Query failed see log"); + throw new System.Exception("DataListFetcher:GetIdListResponseAsync - Query failed see log"); } } - - - //BUILD THE COLUMNS RETURN PROPERTY JSON FRAGMENT - Newtonsoft.Json.Linq.JArray ColumnsJSON = null; - - ColumnsJSON = DataList.GenerateListColumnsJSONFromListView(ListViewArray); - - return new ApiDataListResponse(rows, totalRecordCount, ColumnsJSON); + return retList.ToArray(); } diff --git a/server/AyaNova/DataList/DataListSqlSelectBuilder.cs b/server/AyaNova/DataList/DataListSqlSelectBuilder.cs index 9e54ba5a..9196fab1 100644 --- a/server/AyaNova/DataList/DataListSqlSelectBuilder.cs +++ b/server/AyaNova/DataList/DataListSqlSelectBuilder.cs @@ -20,57 +20,29 @@ namespace AyaNova.DataList //Build the SELECT portion of a list query based on the ListView fields internal static SqlSelectBuilderResult Build(List objectFieldsList, List listViewFieldList) { - - StringBuilder sb = new StringBuilder(); sb.Append("SELECT "); - //DEPRECATED - // //Default ID column for each row (always is aliased as df) - // AyaDataListFieldDefinition def = objectFieldsList.FirstOrDefault(z => z.FieldKey == "df"); - // if (def == null) - // { - // throw new System.ArgumentNullException("DataListSqlSelectBuilder: objectFieldList is missing the df default field"); - // } - // if (string.IsNullOrEmpty(def.SqlIdColumnName)) - // { - // sb.Append("id");//default when no alternate column is specified - // } - // else - // { - // sb.Append(def.SqlIdColumnName); - // } - - // sb.Append(" AS df"); - //keep track of which custom fields columns were added already //this ensures that if there is more than one set of custom fields like from two different objects in the list - //only unique ones will be returned by query - // Dictionary CustomFieldColumnsAddedToQuery = new Dictionary(); + //only unique ones will be returned by query //map sql column name to ordinal name Dictionary map = new Dictionary(); - - //DEPRECATED map.Add("df", 0); int nOrdinal = 0; var firstColumnAdded = false; foreach (string ColumnName in listViewFieldList) { - // //skip the df column, it's already been processed above - // if (ColumnName == "df") - // continue; AyaDataListFieldDefinition o = objectFieldsList.FirstOrDefault(z => z.FieldKey == ColumnName); #if (DEBUG) //Developers little helper if (o == null) { - throw new System.ArgumentNullException($"DEV ERROR in DataListSqlSelectBuilder.cs: field {ColumnName} specified in template was NOT found in ObjectFields list"); + throw new System.ArgumentNullException($"DEV ERROR in DataListSqlSelectBuilder.cs:Build() field {ColumnName} specified in template was NOT found in ObjectFields list"); } #endif if (o != null) {//Ignore missing fields in production - - if (o.IsCustomField) { //if any are custom field then add custom fields column to query var CustomFieldSqlColumnName = o.GetSqlValueColumnName(); @@ -83,12 +55,6 @@ namespace AyaNova.DataList firstColumnAdded = true; map.Add(CustomFieldSqlColumnName, nOrdinal++); } - //if it was already added then can just ignore it - // else - // { - // map.Add(ColumnName, CustomFieldColumnsAddedToQuery[CustomFieldSqlColumnName]); - // } - } else { @@ -115,16 +81,64 @@ namespace AyaNova.DataList map.Add(idColumnName, nOrdinal++); } } - } - } } return new SqlSelectBuilderResult() { map = map, Select = sb.ToString() }; + }//eof - } + //Build the SELECT portion of a list query but only to return rowid's + internal static SqlSelectBuilderResult BuildForReportIdListOnly(List objectFieldsList, List listViewFieldList) + { + StringBuilder sb = new StringBuilder(); + sb.Append("SELECT "); + //map sql column name to ordinal name + Dictionary map = new Dictionary(); + int nOrdinal = 0; + + var firstColumnAdded = false; + foreach (string ColumnName in listViewFieldList) + { + AyaDataListFieldDefinition o = objectFieldsList.FirstOrDefault(z => z.FieldKey == ColumnName); +#if (DEBUG) + //Developers little helper + if (o == null) + { + throw new System.ArgumentNullException($"DEV ERROR in DataListSqlSelectBuilder.cs:BuildForReportIdListOnly() field {ColumnName} specified in template was NOT found in ObjectFields list"); + } +#endif + if (o != null && o.IsRowId)//only return the rowid column + {//Ignore missing fields in production + + var valueColumnName = o.GetSqlValueColumnName(); + if (!map.ContainsKey(valueColumnName)) + { + if (firstColumnAdded) + sb.Append(", "); + sb.Append(valueColumnName); + firstColumnAdded = true; + map.Add(valueColumnName, nOrdinal++); + } + + //does it also have an ID column? + var idColumnName = o.SqlIdColumnName; + if (!string.IsNullOrWhiteSpace(idColumnName)) + { + if (!map.ContainsKey(idColumnName)) + { + if (firstColumnAdded) + sb.Append(", "); + sb.Append(idColumnName); + firstColumnAdded = true; + map.Add(idColumnName, nOrdinal++); + } + } + } + } + return new SqlSelectBuilderResult() { map = map, Select = sb.ToString() }; + }//eof }//eoc