Files
raven/server/AyaNova/DataList/DataListFetcher.cs
2020-02-15 01:14:50 +00:00

193 lines
8.6 KiB
C#

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<ApiDataListResponse> 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");
//do we need to default the listView?
if (string.IsNullOrWhiteSpace(listOptions.ListView))
{
listOptions.ListView = DataList.DefaultListView;
}
//parse the list view
var ListViewArray = JArray.Parse(listOptions.ListView);
//Get the field key names in a list from the listview
List<string> ListViewFieldList = DataList.GetFieldListFromListView(ListViewArray);
//BUILD THE QUERY
//SELECT FRAGMENT COLUMNS FROM TEMPLATE
var SelectBuild = DataListSqlSelectBuilder.Build(DataList.FieldDefinitions, ListViewFieldList);
//FROM CLAUSE
var qFrom = DataList.SQLFrom;
var qWhere = string.Empty;
var qOrderBy = string.Empty;
//WHERE CLAUSE - FILTER
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(" ", " ");
//RETURN OBJECTS
int returnRowColumnCount = ListViewFieldList.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<List<AyaFieldData>> rows = new List<List<AyaFieldData>>();
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<AyaFieldData> row = new List<AyaFieldData>(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
// dr.GetOrdinal();
// dr.GetName();
//INSERT REMAINING FIELDS FROM TEMPLATE INTO THE RETURN ROWS LIST
foreach (string TemplateField in ListViewFieldList)
{
if (TemplateField == "df")
continue;
//get the AyaObjectFieldDefinition
AyaDataListFieldDefinition f = DataList.FieldDefinitions.FirstOrDefault(x => x.FieldKey == TemplateField);
if (f.IsCustomField)
{
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);
AyaField.v = j[InternalCustomFieldName].Value<object>();
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()]);
//nCurrentColumnPointer++;
if (f.SqlIdColumnName != null)//skip over df column id, it's not there
{
var ordinal = SelectBuild.map[f.SqlIdColumnName];
if (!await dr.IsDBNullAsync(ordinal))
AyaField.i = dr.GetInt64(ordinal);
//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(ListViewArray);
return new ApiDataListResponse(rows, totalRecordCount, ColumnsJSON);
}
}//eoc
}//eons