using System.Collections.Generic; using System.Linq; using AyaNova.Biz; using Newtonsoft.Json.Linq; using AyaNova.Api.ControllerHelpers; using Microsoft.AspNetCore.Mvc; using AyaNova.Models; using System.Threading.Tasks; using Microsoft.EntityFrameworkCore; using EnumsNET; namespace AyaNova.DataList { internal static class DataListFetcher { internal static async Task GetResponseAsync(string DataListKey, AyContext ct, ListOptions listOptions, long UserId, AuthorizationRoles UserRoles) { 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"); } //check rights if (!UserRoles.HasAnyFlags(DataList.AllowedRoles)) throw new System.UnauthorizedAccessException("User roles insufficient for this datalist"); //parse the list view //TODO: do I need to try /catch here? Or will an exception properly bubble back to the controller var ListViewArray = JArray.Parse(listOptions.ListView); //PARSE THE TEMPLATE INTO A STRING ARRAY //SO WE KNOW WHICH FIELDS TO RETURN FROM QUERY // var jtemplate = JObject.Parse(JSONDataListTemplate); // //convert to strings array (https://stackoverflow.com/a/33836599/8939) // string[] templateFieldList; // if (listOptions.Mini != null && listOptions.Mini == true) // { // templateFieldList = ((JArray)jtemplate["mini"]).ToObject(); // } // else // { // templateFieldList = ((JArray)jtemplate["full"]).ToObject(); // } string[] templateFieldList = DataList.GetFieldListFromListView(listOptions.ListView); //BUILD THE QUERY //SELECT FRAGMENT COLUMNS FROM TEMPLATE var qSelectColumns = DataListSqlSelectBuilder.Build(DataList.FieldDefinitions, listOptions.ListView); //FROM CLAUSE var qFrom = DataList.SQLFrom; //FILTERED? bool HasFilter = !string.IsNullOrWhiteSpace(listOptions.FilterJson); bool HasSort = !string.IsNullOrWhiteSpace(listOptions.SortJson); var qWhere = string.Empty; var qOrderBy = string.Empty; if (HasFilter) { //WHERE CLAUSE - FILTER qWhere = DataListSqlFilterCriteriaBuilder.DataFilterToSQLCriteria(DataList.FieldDefinitions, listOptions.FilterJson, UserId); } if (HasSort) { //ORDER BY CLAUSE - SORT //BUILD ORDER BY qOrderBy = DataListSqlFilterOrderByBuilder.DataFilterToSQLOrderBy(DataList.FieldDefinitions, listOptions.SortJson); } else { //BUILD DEFAULT ORDER BY IF POSSIBLE qOrderBy = DataListSqlFilterOrderByBuilder.DataFilterToSQLOrderBy(DataList.FieldDefinitions, null); } //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; if (HasFilter) { qDataQuery = $"{qSelectColumns} {qFrom} {qWhere} {qOrderBy} {qLimitOffset}"; qTotalRecordsQuery = $"SELECT COUNT(*) {qFrom} {qWhere}"; } else { qDataQuery = $"{qSelectColumns} {qFrom} {qOrderBy} {qLimitOffset}"; qTotalRecordsQuery = $"SELECT COUNT(*) {qFrom}"; } //RETURN OBJECTS int returnRowColumnCount = templateFieldList.Count() + 1;//Templates don't have the DF column in them but we need it and it's in the query so plus one 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; using (var dr = await command.ExecuteReaderAsync()) { while (dr.Read()) { List row = new List(returnRowColumnCount); //PROCESS THE DF DEFAULT FIRST COLUMN //first column is always the underlying id value of the default record to open for this row in the client ui if (!dr.IsDBNull(0)) { row.Add(new AyaFieldData() { v = dr.GetInt64(0) }); } else { #if (DEBUG) throw new System.ArgumentNullException($"DEV ERROR in DataListFetcher.cs: fetching df column for {DataListKey} df value is null, expecting long int record value"); #endif } //GetOrdinal by name is flakey in npgsql so just going by field definition and ordinal numerically int nCurrentColumnPointer = 1;//start at 1 //INSERT REMAINING FIELDS FROM TEMPLATE INTO THE RETURN ROWS LIST foreach (string TemplateField in templateFieldList) { //get the AyaObjectFieldDefinition AyaDataListFieldDefinition f = DataList.FieldDefinitions.FirstOrDefault(x => x.FieldKey == TemplateField); AyaFieldData AyaField = new AyaFieldData(); AyaField.v = dr.GetValue(nCurrentColumnPointer); nCurrentColumnPointer++; if (f.SqlIdColumnName != null)//skip over df column id, it's not there { if (!await dr.IsDBNullAsync(nCurrentColumnPointer)) AyaField.i = dr.GetInt64(nCurrentColumnPointer); nCurrentColumnPointer++; } 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); } } } //BUILD THE COLUMNS RETURN PROPERTY JSON FRAGMENT Newtonsoft.Json.Linq.JArray ColumnsJSON = null; ColumnsJSON = DataList.GenerateListColumnsJSONFromListView(JSONDataListTemplate); return new ApiDataListResponse(rows, totalRecordCount, ColumnsJSON); } }//eoc }//eons