using System.Collections.Generic; using System.Linq; using AyaNova.Biz; using Newtonsoft.Json.Linq; using Microsoft.Extensions.Logging; using AyaNova.Models; using System.Threading.Tasks; using Microsoft.EntityFrameworkCore; using EnumsNET; namespace AyaNova.DataList { internal static class DataListFetcher { //////////////////////////////////////////////// // Get the data list data requested // // internal static async Task GetResponseAsync(AyContext ct, DataListTableProcessingOptions dataListTableProcessingOptions,IDataListProcessing DataList, AuthorizationRoles userRoles, ILogger log, long userId) { // var DataList = DataListFactory.GetAyaDataList(dataListTableRequest.DataListKey); //was the name not found as a list? // if (DataList == null) // throw new System.ArgumentOutOfRangeException($"DataList \"{dataListTableRequest.DataListKey}\" specified does not exist"); //turn the DataListTableRequest into a DataListTableProcesingOptions object here //hydrates filter and column selections etc // = new DataListTableProcessingOptions(dataListTableRequest, DataList, ct); //#### TODO: below block into above method to clean it up and centralize it // //STATIC filter options from server // List StaticServerFilterOptions = new List(); // if (DataList is IDataListInternalCriteria) // StaticServerFilterOptions = ((IDataListInternalCriteria)DataList).DataListInternalCriteria(userId, userRoles, dataListTableOptions); // //Get the public field key names in a list from the listview // List PublicListViewFieldList = listOptions.Columns; //Get the combination of all unique fields from both StaticServerFilterOptions and listOptions //NOTE: this assumes no list options filter colums that don't exist in listoptions.columns // var AllUniqueFieldKeysRequiredForQuery = dataListTableOptions.Columns.Union(StaticServerFilterOptions.Select(z => z.Column).ToList()).ToList(); // //Add the internal filters into the listoptions existing filters // //NOTE: There is currently no overlap between internal filtered columns and filters coming from the client // foreach (DataListFilterOption dfo in StaticServerFilterOptions) // dataListTableOptions.Filter.Add(dfo); //BUILD THE QUERY //SELECT CLAUSE var qSelect = DataListSqlSelectBuilder.BuildForDataTableListResponse(DataList.FieldDefinitions, dataListTableProcessingOptions.AllUniqueColumnKeysReferenced); //FROM CLAUSE var qFrom = DataList.SQLFrom; var qWhere = string.Empty; var qOrderBy = string.Empty; //WHERE CLAUSE - FILTER qWhere = DataListSqlFilterCriteriaBuilder.DataFilterToSQLCriteria(DataList.FieldDefinitions, dataListTableProcessingOptions); //ORDER BY CLAUSE - SORT //BUILD ORDER BY qOrderBy = DataListSqlFilterOrderByBuilder.DataFilterToSQLOrderBy(DataList.FieldDefinitions, dataListTableProcessingOptions); //LIMIT AND OFFSET CLAUSE - PAGING dataListTableProcessingOptions.Offset = dataListTableProcessingOptions.Offset ?? DataListTableProcessingOptions.DefaultOffset; dataListTableProcessingOptions.Limit = dataListTableProcessingOptions.Limit ?? DataListTableProcessingOptions.DefaultLimit; var qLimitOffset = $"LIMIT {dataListTableProcessingOptions.Limit} OFFSET {dataListTableProcessingOptions.Offset}"; //PUT IT ALL TOGETHER string qDataQuery = string.Empty; string qTotalRecordsQuery = string.Empty; qDataQuery = $"{qSelect.Select} {qFrom} {qWhere} {qOrderBy} {qLimitOffset}".Replace(" ", " "); qTotalRecordsQuery = $"SELECT COUNT(*) {qFrom} {qWhere}".Replace(" ", " "); //RETURN OBJECTS int returnRowColumnCount = dataListTableProcessingOptions.Columns.Count(); List> rows = new List>(); long totalRecordCount = 0; //QUERY THE DB using (var command = ct.Database.GetDbConnection().CreateCommand()) { await ct.Database.OpenConnectionAsync(); //GET DATA RETURN ROWS command.CommandText = qDataQuery; try { using (var dr = await command.ExecuteReaderAsync()) { while (dr.Read()) { List row = new List(returnRowColumnCount); //INSERT REMAINING FIELDS FROM TEMPLATE INTO THE RETURN ROWS LIST foreach (string TemplateField in dataListTableProcessingOptions.Columns) { //get the AyaObjectFieldDefinition DataListFieldDefinition f = DataList.FieldDefinitions.FirstOrDefault(z => z.FieldKey == TemplateField); if (f.IsCustomField) { DataListField AyaField = new DataListField(); var cust = dr.GetString(qSelect.map[f.GetSqlValueColumnName()]); if (!string.IsNullOrWhiteSpace(cust)) { JObject j = JObject.Parse(cust); //convert field name to cust name then get value var InternalCustomFieldName = FormFieldReference.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; 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 { DataListField AyaField = new DataListField(); AyaField.v = dr.GetValue(qSelect.map[f.GetSqlValueColumnName()]); if (f.IsRowId) { AyaField.rid = true; } else { AyaField.rid = null; } if (f.SqlIdColumnName != null) { var ordinal = qSelect.map[f.SqlIdColumnName]; if (!await dr.IsDBNullAsync(ordinal)) AyaField.i = dr.GetInt64(ordinal); } if (f.SqlAyTypeColumnName != null) { var ordinal = qSelect.map[f.SqlAyTypeColumnName]; if (!await dr.IsDBNullAsync(ordinal)) AyaField.ot = dr.GetInt32(ordinal); } if (f.SqlColorColumnName != null) { var ordinal = qSelect.map[f.SqlColorColumnName]; if (!await dr.IsDBNullAsync(ordinal)) AyaField.clr = dr.GetString(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.LogError("DataListFetcher:GetResponseAsync query failed. Data Query was:"); log.LogError(qDataQuery); log.LogError("Count Query was:"); log.LogError(qTotalRecordsQuery); log.LogError(e, "DB Exception"); throw new System.Exception("DataListFetcher:GetResponseAsync - Query failed see log"); } catch (System.Exception e) { //ensure any other type of exception gets surfaced properly //log out the exception and the query log.LogError("DataListFetcher:GetResponseAsync unexpected failure. Data Query was:"); log.LogError(qDataQuery); log.LogError(e, "Exception"); throw new System.Exception("DataListFetcher:GetResponseAsync - unexpected failure see log"); } } //BUILD THE COLUMNS RETURN PROPERTY JSON FRAGMENT Newtonsoft.Json.Linq.JArray ColumnsJSON = null; ColumnsJSON = DataList.GenerateReturnListColumns(dataListTableProcessingOptions.Columns); return new DataListReturnData(rows, totalRecordCount, ColumnsJSON); } ///////////////////////////////////////////////////////////////// // Get a list of id's of the datalist results for reporting // // //internal static async Task GetIdListResponseAsync(string dataListKey, string listView, string metaListView, AyContext ct, AuthorizationRoles userRoles, ILogger log, long userId) internal static async Task GetIdListResponseAsync(AyContext ct, DataListSelectedProcessingOptions dataListSelectionOptions, AuthorizationRoles userRoles, ILogger log, long userId) { var DataList = DataListFactory.GetAyaDataList(dataListSelectionOptions.DataListKey); //was the name not found as a list? if (DataList == null) throw new System.ArgumentOutOfRangeException($"DataList \"{dataListSelectionOptions.DataListKey}\" specified does not exist"); //check rights 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(listView)) // listView = DataList.DefaultListView; DataList.SetListOptionDefaultsIfNecessary(dataListSelectionOptions); // //parse the list view // var ListViewArray = JArray.Parse(listView); // //Hard coded extra criteria from server end // if (DataList is IAyaDataListServerCriteria) // { // var ServerCriteriaListView = JArray.Parse(((IAyaDataListServerCriteria)DataList).DataListServerCriteria(await ct.User.AsNoTracking().FirstOrDefaultAsync(z => z.Id == userId), ct)); // foreach (JToken jt in ServerCriteriaListView) // ListViewArray.Add(jt); // } //STATIC filter options from server List StaticServerFilterOptions = null; if (DataList is IDataListInternalCriteria) StaticServerFilterOptions = ((IDataListInternalCriteria)DataList).DataListInternalCriteria(userId, userRoles, dataListSelectionOptions); //Add the internal filters into the listoptions existing filters //NOTE: There is currently no overlap between internal filtered columns and filters coming from the client foreach (DataListFilterOption dfo in StaticServerFilterOptions) dataListSelectionOptions.Filter.Add(dfo); // //Hard coded extra criteria from Client end // //parse and combine any additional listview hard coded from Client UI // var MetaListViewArray = JArray.Parse(metaListView ?? "[]"); // foreach (JToken jt in MetaListViewArray) // ListViewArray.Add(jt); // //Get the field key names in a list from the listview // List ListViewFieldList = DataList.GetFieldListFromListView(ListViewArray); //BUILD THE QUERY //SELECT FRAGMENT COLUMNS FROM TEMPLATE var qSelect = DataListSqlSelectBuilder.BuildForIdListResponse(DataList.FieldDefinitions, dataListSelectionOptions); //FROM CLAUSE var qFrom = DataList.SQLFrom; var qWhere = string.Empty; var qOrderBy = string.Empty; //WHERE CLAUSE - FILTER qWhere = DataListSqlFilterCriteriaBuilder.DataFilterToSQLCriteria(DataList.FieldDefinitions, dataListSelectionOptions); //ORDER BY CLAUSE - SORT qOrderBy = DataListSqlFilterOrderByBuilder.DataFilterToSQLOrderBy(DataList.FieldDefinitions, dataListSelectionOptions); //PUT IT ALL TOGETHER string qDataQuery = string.Empty; qDataQuery = $"{qSelect} {qFrom} {qWhere} {qOrderBy} ".Replace(" ", " "); //RETURN OBJECTS var retList = new List(); //GET ID COLUMN ORDINAL int IDColumnOrdinal = 0; //NOTE: assuming change of code means id column is *always* the zeroth column // foreach (string TemplateField in ListViewFieldList) // { // //get the AyaObjectFieldDefinition // AyaDataListFieldDefinition f = DataList.FieldDefinitions.FirstOrDefault(z => z.FieldKey == TemplateField); // if (f.IsRowId) // { // IDColumnOrdinal = SelectBuild.map[f.SqlIdColumnName]; // break; // } // } using (var command = ct.Database.GetDbConnection().CreateCommand()) { await ct.Database.OpenConnectionAsync(); command.CommandText = qDataQuery; try { using (var dr = await command.ExecuteReaderAsync()) { while (dr.Read()) { if (!dr.IsDBNull(IDColumnOrdinal)) retList.Add(dr.GetInt64(IDColumnOrdinal)); } } } catch (Npgsql.PostgresException e) { //log out the exception and the query log.LogError("DataListFetcher:GetIdListResponseAsync query failed unexpectedly. IDList Query was:"); log.LogError(qDataQuery); log.LogError(e, "DB Exception"); throw new System.Exception("DataListFetcher:GetIdListResponseAsync - Query failed see log"); } catch (System.Exception e) { //ensure any other type of exception gets surfaced properly //log out the exception and the query log.LogError("DataListFetcher:GetIdListResponseAsync unexpected failure. IDList Query was:"); log.LogError(qDataQuery); log.LogError(e, "Exception"); throw new System.Exception("DataListFetcher:GetIdListResponseAsync - unexpected failure see log"); } } return retList.ToArray(); } }//eoc }//eons