This commit is contained in:
2020-09-01 18:16:13 +00:00
parent 84a8b7f05a
commit dcc86dc90f
3 changed files with 94 additions and 151 deletions

View File

@@ -230,7 +230,6 @@ namespace AyaNova.Api.Controllers
reportData = await biz.GetReportData(reportDataParam.ObjectType, rowIds); reportData = await biz.GetReportData(reportDataParam.ObjectType, rowIds);
} }
if (reportData == null) if (reportData == null)
return BadRequest(new ApiErrorResponse(biz.Errors)); return BadRequest(new ApiErrorResponse(biz.Errors));
else else

View File

@@ -201,31 +201,31 @@ namespace AyaNova.DataList
} }
//Get a list of id's for reporting //Get a list of id's for reporting
internal static async Task<long[]> GetIdListResponseAsync(string DataListKey, string ListView, AyContext ct, long UserId, AuthorizationRoles UserRoles, ILogger log) internal static async Task<long[]> 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? //was the name not found as a list?
if (DataList == null) 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 //check rights
if (!UserRoles.HasAnyFlags(DataList.AllowedRoles)) if (!userRoles.HasAnyFlags(DataList.AllowedRoles))
throw new System.UnauthorizedAccessException("User roles insufficient for this datalist"); throw new System.UnauthorizedAccessException("User roles insufficient for this datalist");
//do we need to default the listView? //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 //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 //Get the field key names in a list from the listview
@@ -233,7 +233,7 @@ namespace AyaNova.DataList
//BUILD THE QUERY //BUILD THE QUERY
//SELECT FRAGMENT COLUMNS FROM TEMPLATE //SELECT FRAGMENT COLUMNS FROM TEMPLATE
var SelectBuild = DataListSqlSelectBuilder.Build(DataList.FieldDefinitions, ListViewFieldList); var SelectBuild = DataListSqlSelectBuilder.BuildForReportIdListOnly(DataList.FieldDefinitions, ListViewFieldList);
//FROM CLAUSE //FROM CLAUSE
var qFrom = DataList.SQLFrom; var qFrom = DataList.SQLFrom;
@@ -242,28 +242,20 @@ namespace AyaNova.DataList
var qOrderBy = string.Empty; var qOrderBy = string.Empty;
//WHERE CLAUSE - FILTER //WHERE CLAUSE - FILTER
qWhere = DataListSqlFilterCriteriaBuilder.DataFilterToSQLCriteria(DataList.FieldDefinitions, ListViewArray, UserId); qWhere = DataListSqlFilterCriteriaBuilder.DataFilterToSQLCriteria(DataList.FieldDefinitions, ListViewArray, userId);
//ORDER BY CLAUSE - SORT //ORDER BY CLAUSE - SORT
//BUILD ORDER BY //BUILD ORDER BY
qOrderBy = DataListSqlFilterOrderByBuilder.DataFilterToSQLOrderBy(DataList.FieldDefinitions, ListViewArray); 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 //PUT IT ALL TOGETHER
string qDataQuery = string.Empty; string qDataQuery = string.Empty;
string qTotalRecordsQuery = string.Empty;
qDataQuery = $"{SelectBuild.Select} {qFrom} {qWhere} {qOrderBy} {qLimitOffset}".Replace(" ", " "); qDataQuery = $"{SelectBuild.Select} {qFrom} {qWhere} {qOrderBy} ".Replace(" ", " ");
qTotalRecordsQuery = $"SELECT COUNT(*) {qFrom} {qWhere}".Replace(" ", " ");
//RETURN OBJECTS //RETURN OBJECTS
int returnRowColumnCount = ListViewFieldList.Count(); var retList = new List<long>();
List<List<AyaFieldData>> rows = new List<List<AyaFieldData>>();
long totalRecordCount = 0;
//QUERY THE DB //QUERY THE DB
using (var command = ct.Database.GetDbConnection().CreateCommand()) using (var command = ct.Database.GetDbConnection().CreateCommand())
@@ -278,114 +270,52 @@ namespace AyaNova.DataList
{ {
while (dr.Read()) while (dr.Read())
{ {
List<AyaFieldData> row = new List<AyaFieldData>(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 // AyaFieldData AyaField = new AyaFieldData();
foreach (string TemplateField in ListViewFieldList) // AyaField.v = dr.GetValue(SelectBuild.map[f.GetSqlValueColumnName()]);
{
//get the AyaObjectFieldDefinition // if (f.IsRowId)
AyaDataListFieldDefinition f = DataList.FieldDefinitions.FirstOrDefault(z => z.FieldKey == TemplateField); // {
if (f.IsCustomField) // AyaField.rid = true;
{ // }
// else
// {
// AyaField.rid = null;
// }
AyaFieldData AyaField = new AyaFieldData(); // if (f.SqlIdColumnName != null)
var cust = dr.GetString(SelectBuild.map[f.GetSqlValueColumnName()]); // {
if (!string.IsNullOrWhiteSpace(cust)) // var ordinal = SelectBuild.map[f.SqlIdColumnName];
{ // if (!await dr.IsDBNullAsync(ordinal))
JObject j = JObject.Parse(cust); // AyaField.i = dr.GetInt64(ordinal);
//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<object>();
JToken o = j[InternalCustomFieldName];
if (o != null)
AyaField.v = o.Value<object>();
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
{
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) catch (Npgsql.PostgresException e)
{ {
//log out the exception and the query //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(qDataQuery);
log.LogInformation("Count Query was:");
log.LogInformation(qTotalRecordsQuery);
log.LogInformation(e, "DB Exception"); log.LogInformation(e, "DB Exception");
throw new System.Exception("DataListFetcher - Query failed see log"); throw new System.Exception("DataListFetcher:GetIdListResponseAsync - Query failed see log");
} }
} }
return retList.ToArray();
//BUILD THE COLUMNS RETURN PROPERTY JSON FRAGMENT
Newtonsoft.Json.Linq.JArray ColumnsJSON = null;
ColumnsJSON = DataList.GenerateListColumnsJSONFromListView(ListViewArray);
return new ApiDataListResponse(rows, totalRecordCount, ColumnsJSON);
} }

View File

@@ -20,57 +20,29 @@ namespace AyaNova.DataList
//Build the SELECT portion of a list query based on the ListView fields //Build the SELECT portion of a list query based on the ListView fields
internal static SqlSelectBuilderResult Build(List<AyaDataListFieldDefinition> objectFieldsList, List<string> listViewFieldList) internal static SqlSelectBuilderResult Build(List<AyaDataListFieldDefinition> objectFieldsList, List<string> listViewFieldList)
{ {
StringBuilder sb = new StringBuilder(); StringBuilder sb = new StringBuilder();
sb.Append("SELECT "); 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 //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 //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 //only unique ones will be returned by query
// Dictionary<string, int> CustomFieldColumnsAddedToQuery = new Dictionary<string, int>();
//map sql column name to ordinal name //map sql column name to ordinal name
Dictionary<string, int> map = new Dictionary<string, int>(); Dictionary<string, int> map = new Dictionary<string, int>();
//DEPRECATED map.Add("df", 0);
int nOrdinal = 0; int nOrdinal = 0;
var firstColumnAdded = false; var firstColumnAdded = false;
foreach (string ColumnName in listViewFieldList) 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); AyaDataListFieldDefinition o = objectFieldsList.FirstOrDefault(z => z.FieldKey == ColumnName);
#if (DEBUG) #if (DEBUG)
//Developers little helper //Developers little helper
if (o == null) 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 #endif
if (o != null) if (o != null)
{//Ignore missing fields in production {//Ignore missing fields in production
if (o.IsCustomField) if (o.IsCustomField)
{ //if any are custom field then add custom fields column to query { //if any are custom field then add custom fields column to query
var CustomFieldSqlColumnName = o.GetSqlValueColumnName(); var CustomFieldSqlColumnName = o.GetSqlValueColumnName();
@@ -83,12 +55,6 @@ namespace AyaNova.DataList
firstColumnAdded = true; firstColumnAdded = true;
map.Add(CustomFieldSqlColumnName, nOrdinal++); map.Add(CustomFieldSqlColumnName, nOrdinal++);
} }
//if it was already added then can just ignore it
// else
// {
// map.Add(ColumnName, CustomFieldColumnsAddedToQuery[CustomFieldSqlColumnName]);
// }
} }
else else
{ {
@@ -115,16 +81,64 @@ namespace AyaNova.DataList
map.Add(idColumnName, nOrdinal++); map.Add(idColumnName, nOrdinal++);
} }
} }
} }
} }
} }
return new SqlSelectBuilderResult() { map = map, Select = sb.ToString() }; 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<AyaDataListFieldDefinition> objectFieldsList, List<string> listViewFieldList)
{
StringBuilder sb = new StringBuilder();
sb.Append("SELECT ");
//map sql column name to ordinal name
Dictionary<string, int> map = new Dictionary<string, int>();
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 }//eoc