This commit is contained in:
2020-02-11 22:55:58 +00:00
parent aa0c73d415
commit 9625baa8da
10 changed files with 188 additions and 193 deletions

View File

@@ -0,0 +1,22 @@
using System.Collections.Generic;
namespace AyaNova.Api.ControllerHelpers
{
public class ApiDataListResponse
{
public object Data { get; }
public long TotalRecordCount { get; }
public object Columns {get;}
public ApiDataListResponse(object returnItems, long totalRecordCount, Newtonsoft.Json.Linq.JArray columns)
{
Data = returnItems;
TotalRecordCount = totalRecordCount;
Columns = columns;
}
}//eoc
}//eons

View File

@@ -1,38 +0,0 @@
using System.Collections.Generic;
namespace AyaNova.Api.ControllerHelpers
{
public class ApiOkWithPagingResponse<T>
{
public object Data { get; }
public object Paging { get; }
public ApiOkWithPagingResponse(ApiPagedResponse<T> pr)
{
Data = pr.items;
Paging = pr.PageLinks;
}
}//eoc
public class ApiOkWithPagingResponse
{
public object Data { get; }
public object Paging { get; }
public object Columns {get;}
public ApiOkWithPagingResponse(ApiPagedResponse pr)
{
Data = pr.items;
Paging = pr.PageLinks;
Columns = pr.Columns;
}
}//eoc
}//eons

View File

@@ -1,42 +1,42 @@
using System.Collections.Generic; // using System.Collections.Generic;
using AyaNova.Models; // using AyaNova.Models;
namespace AyaNova.Api.ControllerHelpers // namespace AyaNova.Api.ControllerHelpers
{ // {
public class ApiPagedResponse<T> // // public class ApiPagedResponse<T>
{ // // {
public T[] items { get; } // // public T[] items { get; }
public object PageLinks { get; } // // public object PageLinks { get; }
public object Columns { get; } // // public object Columns { get; }
public ApiPagedResponse(T[] returnItems, object pageLinks, object columns = null) // // public ApiPagedResponse(T[] returnItems, object pageLinks, object columns = null)
{ // // {
items = returnItems; // // items = returnItems;
PageLinks = pageLinks; // // PageLinks = pageLinks;
Columns = Columns; // // Columns = Columns;
} // // }
}//eoc // // }//eoc
public class ApiPagedResponse // public class ApiPagedResponse
{ // {
public object items { get; } // public object items { get; }
public object PageLinks { get; } // public object PageLinks { get; }
public Newtonsoft.Json.Linq.JArray Columns { get; } // public Newtonsoft.Json.Linq.JArray Columns { get; }
public ApiPagedResponse(object returnItems, object pageLinks, Newtonsoft.Json.Linq.JArray columns) // public ApiPagedResponse(object returnItems, object pageLinks, Newtonsoft.Json.Linq.JArray columns)
{ // {
items = returnItems; // items = returnItems;
PageLinks = pageLinks; // PageLinks = pageLinks;
Columns = columns; // Columns = columns;
} // }
}//eoc // }//eoc
}//eons // }//eons

View File

@@ -10,25 +10,31 @@ namespace AyaNova.Api.ControllerHelpers
public const int DefaultOffset = 0; public const int DefaultOffset = 0;
public const int DefaultLimit = 25; public const int DefaultLimit = 25;
[FromQuery] [FromBody]
[Range(0, int.MaxValue)] [Range(0, int.MaxValue)]
public int? Offset { get; set; } public int? Offset { get; set; }
[FromQuery] [FromBody]
[Range(1, MaxPageSize, ErrorMessage = "Limit must be greater than 0 and less than 1000.")] [Range(1, MaxPageSize, ErrorMessage = "Limit must be greater than 0 and less than 1000.")]
public int? Limit { get; set; } public int? Limit { get; set; }
//Data filter id to use with this list query // //Data filter id to use with this list query
//0 or less means no filter // //0 or less means no filter
[FromQuery] // [FromBody]
public long DataFilterId { get; set; } // public long DataFilterId { get; set; }
[FromQuery] [FromBody]
public bool Mini { get; set; } public bool Mini { get; set; }
[FromQuery, Required] [FromBody, Required]
public string DataListKey { get; set; } public string DataListKey { get; set; }
[FromBody]
public string FilterJson { get; set; }
[FromBody]
public string SortJson { get; set; }
} }

View File

@@ -1,89 +1,89 @@
using System; // using System;
using Microsoft.AspNetCore.Routing; // using Microsoft.AspNetCore.Routing;
using Microsoft.AspNetCore.Mvc; // using Microsoft.AspNetCore.Mvc;
using Newtonsoft.Json; // using Newtonsoft.Json;
namespace AyaNova.Api.ControllerHelpers // namespace AyaNova.Api.ControllerHelpers
{ // {
public class PaginationLinkBuilder // public class PaginationLinkBuilder
{ //adapted from //https://www.jerriepelser.com/blog/paging-in-aspnet-webapi-pagination-links/ // { //adapted from //https://www.jerriepelser.com/blog/paging-in-aspnet-webapi-pagination-links/
public Uri FirstPage { get; private set; } // public Uri FirstPage { get; private set; }
public Uri LastPage { get; private set; } // public Uri LastPage { get; private set; }
public Uri NextPage { get; private set; } // public Uri NextPage { get; private set; }
public Uri PreviousPage { get; private set; } // public Uri PreviousPage { get; private set; }
public ListOptions PagingOptions { get; } // public ListOptions PagingOptions { get; }
public long TotalRecordCount { get; } // public long TotalRecordCount { get; }
public PaginationLinkBuilder(IUrlHelper urlHelper, string routeName, object routeValues, ListOptions pagingOptions, long totalRecordCount) // public PaginationLinkBuilder(IUrlHelper urlHelper, string routeName, object routeValues, ListOptions pagingOptions, long totalRecordCount)
{ // {
PagingOptions = pagingOptions; // PagingOptions = pagingOptions;
TotalRecordCount = totalRecordCount; // TotalRecordCount = totalRecordCount;
// Determine total number of pages // // Determine total number of pages
var pageCount = totalRecordCount > 0 // var pageCount = totalRecordCount > 0
? (int)Math.Ceiling(totalRecordCount / (double)pagingOptions.Limit) // ? (int)Math.Ceiling(totalRecordCount / (double)pagingOptions.Limit)
: 0; // : 0;
// Create page links // // Create page links
FirstPage = new Uri(urlHelper.Link(routeName, new RouteValueDictionary(routeValues) // FirstPage = new Uri(urlHelper.Link(routeName, new RouteValueDictionary(routeValues)
{ // {
{"pageNo", 1}, // {"pageNo", 1},
{"pageSize", pagingOptions.Limit} // {"pageSize", pagingOptions.Limit}
})); // }));
LastPage = new Uri(urlHelper.Link(routeName, new RouteValueDictionary(routeValues) // LastPage = new Uri(urlHelper.Link(routeName, new RouteValueDictionary(routeValues)
{ // {
{"pageNo", pageCount}, // {"pageNo", pageCount},
{"pageSize", pagingOptions.Limit} // {"pageSize", pagingOptions.Limit}
})); // }));
if (pagingOptions.Offset > 1) // if (pagingOptions.Offset > 1)
{ // {
PreviousPage = new Uri(urlHelper.Link(routeName, new RouteValueDictionary(routeValues) // PreviousPage = new Uri(urlHelper.Link(routeName, new RouteValueDictionary(routeValues)
{ // {
{"pageNo", pagingOptions.Offset - 1}, // {"pageNo", pagingOptions.Offset - 1},
{"pageSize", pagingOptions.Limit} // {"pageSize", pagingOptions.Limit}
})); // }));
} // }
if (pagingOptions.Offset < pageCount) // if (pagingOptions.Offset < pageCount)
{ // {
NextPage = new Uri(urlHelper.Link(routeName, new RouteValueDictionary(routeValues) // NextPage = new Uri(urlHelper.Link(routeName, new RouteValueDictionary(routeValues)
{ // {
{"pageNo", pagingOptions.Offset + 1}, // {"pageNo", pagingOptions.Offset + 1},
{"pageSize", pagingOptions.Limit} // {"pageSize", pagingOptions.Limit}
})); // }));
} // }
} // }
/// <summary> // /// <summary>
/// Return paging data suitable for API return // /// Return paging data suitable for API return
/// </summary> // /// </summary>
/// <returns></returns> // /// <returns></returns>
public Object PagingLinksObject() // public Object PagingLinksObject()
{ // {
return new // return new
{ // {
Count = TotalRecordCount, // Count = TotalRecordCount,
Offset = PagingOptions.Offset, // Offset = PagingOptions.Offset,
Limit = PagingOptions.Limit, // Limit = PagingOptions.Limit,
First = FirstPage, // First = FirstPage,
Previous = PreviousPage, // Previous = PreviousPage,
Next = NextPage, // Next = NextPage,
Last = LastPage // Last = LastPage
}; // };
} // }
} // }
} // }

View File

@@ -48,8 +48,8 @@ namespace AyaNova.Api.Controllers
/// </summary> /// </summary>
/// <param name="listOptions">List key, Paging, filtering and sorting options</param> /// <param name="listOptions">List key, Paging, filtering and sorting options</param>
/// <returns>Collection with paging data</returns> /// <returns>Collection with paging data</returns>
[HttpGet("List", Name = nameof(List))] [HttpPost("List", Name = nameof(List))]
public async Task<IActionResult> List([FromQuery] ListOptions listOptions) public async Task<IActionResult> List([FromBody] ListOptions listOptions)
{ {
if (serverState.IsClosed) if (serverState.IsClosed)
return StatusCode(503, new ApiErrorResponse(ApiErrorCode.API_CLOSED, null, serverState.Reason)); return StatusCode(503, new ApiErrorResponse(ApiErrorCode.API_CLOSED, null, serverState.Reason));
@@ -62,8 +62,8 @@ namespace AyaNova.Api.Controllers
try try
{ {
ApiPagedResponse pr = await DataListFetcher.GetResponseAsync(listOptions.DataListKey, ct, Url, nameof(List), listOptions, UserId, UserRoles); ApiDataListResponse r = await DataListFetcher.GetResponseAsync(listOptions.DataListKey, ct, Url, nameof(List), listOptions, UserId, UserRoles);
return Ok(new ApiOkWithPagingResponse(pr)); return Ok(r);
} }
catch (System.UnauthorizedAccessException) catch (System.UnauthorizedAccessException)
{ {

View File

@@ -13,7 +13,7 @@ namespace AyaNova.DataList
{ {
internal static class DataListFetcher internal static class DataListFetcher
{ {
internal static async Task<ApiPagedResponse> GetResponseAsync(string DataListKey, AyContext ct, IUrlHelper Url, internal static async Task<ApiDataListResponse> GetResponseAsync(string DataListKey, AyContext ct, IUrlHelper Url,
string routeName, ListOptions listOptions, long UserId, AuthorizationRoles UserRoles) string routeName, ListOptions listOptions, long UserId, AuthorizationRoles UserRoles)
{ {
@@ -78,15 +78,19 @@ namespace AyaNova.DataList
DataListFilter TheFilter = null; DataListFilter TheFilter = null;
var qWhere = string.Empty; var qWhere = string.Empty;
var qOrderBy = string.Empty; var qOrderBy = string.Empty;
if (listOptions.DataFilterId > 0) if (!string.IsNullOrWhiteSpace(listOptions.FilterJson))
{ {
TheFilter = await ct.DataListFilter.FirstOrDefaultAsync(x => x.Id == listOptions.DataFilterId);
//WHERE CLAUSE - FILTER //WHERE CLAUSE - FILTER
qWhere = DataListSqlFilterCriteriaBuilder.DataFilterToSQLCriteria(DataList.FieldDefinitions, TheFilter, DataList.FieldDefinitions, UserId); qWhere = DataListSqlFilterCriteriaBuilder.DataFilterToSQLCriteria(DataList.FieldDefinitions, listOptions.FilterJson, DataList.FieldDefinitions, UserId);
}
if (!string.IsNullOrWhiteSpace(listOptions.SortJson))
{
//ORDER BY CLAUSE - SORT //ORDER BY CLAUSE - SORT
//BUILD ORDER BY //BUILD ORDER BY
qOrderBy = DataListSqlFilterOrderByBuilder.DataFilterToSQLOrderBy(DataList.FieldDefinitions, TheFilter); qOrderBy = DataListSqlFilterOrderByBuilder.DataFilterToSQLOrderBy(DataList.FieldDefinitions, listOptions.SortJson);
}else{ }
else
{
//BUILD DEFAULT ORDER BY IF POSSIBLE //BUILD DEFAULT ORDER BY IF POSSIBLE
qOrderBy = DataListSqlFilterOrderByBuilder.DataFilterToSQLOrderBy(DataList.FieldDefinitions, null); qOrderBy = DataListSqlFilterOrderByBuilder.DataFilterToSQLOrderBy(DataList.FieldDefinitions, null);
} }
@@ -180,38 +184,41 @@ namespace AyaNova.DataList
} }
} }
//RES.PAGING.COUNT is all I need so the rest is moot, particularly since it's a post method now to get a datalist not a GET with query parameters
//below commented stuff can go once it tests
//BUILD THE PAGING LINKS PORTION //BUILD THE PAGING LINKS PORTION
//var pageLinks = new PaginationLinkBuilder(Url, routeName, null, listOptions, totalRecordCount).PagingLinksObject(); //var pageLinks = new PaginationLinkBuilder(Url, routeName, null, listOptions, totalRecordCount).PagingLinksObject();
//http://localhost:7575/api/v8/DataList/List?Offset=2&Limit=3&DataFilterId=2&Mini=true&DataListKey=TestWidgetDataList //http://localhost:7575/api/v8/DataList/List?Offset=2&Limit=3&DataFilterId=2&Mini=true&DataListKey=TestWidgetDataList
object routeValues = null; // object routeValues = null;
//no data filter? // //no data filter?
if (listOptions.DataFilterId == 0) // if (listOptions.DataFilterId == 0)
{ // {
if (listOptions.Mini) // if (listOptions.Mini)
{ // {
routeValues = new { DataListKey = DataListKey, Mini = listOptions.Mini }; // routeValues = new { DataListKey = DataListKey, Mini = listOptions.Mini };
} // }
else // else
{ // {
routeValues = new { DataListKey = DataListKey }; // routeValues = new { DataListKey = DataListKey };
} // }
} // }
else // else
{ // {
if (listOptions.Mini) // if (listOptions.Mini)
{ // {
routeValues = new { DataListKey = DataListKey, DataFilterId = listOptions.DataFilterId, Mini = listOptions.Mini }; // routeValues = new { DataListKey = DataListKey, DataFilterId = listOptions.DataFilterId, Mini = listOptions.Mini };
} // }
else // else
{ // {
routeValues = new { DataListKey = DataListKey, DataFilterId = listOptions.DataFilterId }; // routeValues = new { DataListKey = DataListKey, DataFilterId = listOptions.DataFilterId };
} // }
} // }
var pageLinks = new PaginationLinkBuilder(Url, routeName, routeValues, listOptions, totalRecordCount).PagingLinksObject(); // var pageLinks = new PaginationLinkBuilder(Url, routeName, routeValues, listOptions, totalRecordCount).PagingLinksObject();
//BUILD THE COLUMNS RETURN PROPERTY JSON FRAGMENT //BUILD THE COLUMNS RETURN PROPERTY JSON FRAGMENT
Newtonsoft.Json.Linq.JArray ColumnsJSON = null; Newtonsoft.Json.Linq.JArray ColumnsJSON = null;
@@ -225,8 +232,8 @@ namespace AyaNova.DataList
} }
ApiPagedResponse pr = new ApiPagedResponse(rows, pageLinks, ColumnsJSON); return new ApiDataListResponse(rows, totalRecordCount, ColumnsJSON);
return pr;
} }

View File

@@ -11,10 +11,10 @@ namespace AyaNova.DataList
{ {
public static class DataListSqlFilterCriteriaBuilder public static class DataListSqlFilterCriteriaBuilder
{ {
public static string DataFilterToSQLCriteria(List<AyaDataListFieldDefinition> objectFieldsList, AyaNova.Models.DataListFilter dataFilter, List<AyaDataListFieldDefinition> objectFields, long userId) public static string DataFilterToSQLCriteria(List<AyaDataListFieldDefinition> objectFieldsList, string filterJson, List<AyaDataListFieldDefinition> objectFields, long userId)
{ {
if (string.IsNullOrWhiteSpace(dataFilter.Filter)) if (string.IsNullOrWhiteSpace(filterJson))
{ {
return ""; return "";
} }
@@ -22,7 +22,7 @@ namespace AyaNova.DataList
StringBuilder sb = new StringBuilder(); StringBuilder sb = new StringBuilder();
//iterate the datafilter and concatenate a sql query from it //iterate the datafilter and concatenate a sql query from it
var FilterArray = JArray.Parse(dataFilter.Filter); var FilterArray = JArray.Parse(filterJson);
for (int i = 0; i < FilterArray.Count; i++) for (int i = 0; i < FilterArray.Count; i++)
{ {

View File

@@ -12,10 +12,10 @@ namespace AyaNova.DataList
// public static string DefaultPickListOrderBy => "ORDER BY NAME ASC"; // public static string DefaultPickListOrderBy => "ORDER BY NAME ASC";
public static string DataFilterToSQLOrderBy(List<AyaDataListFieldDefinition> objectFieldsList, AyaNova.Models.DataListFilter dataFilter) public static string DataFilterToSQLOrderBy(List<AyaDataListFieldDefinition> objectFieldsList, string SortJson)
{ {
if (dataFilter == null || string.IsNullOrWhiteSpace(dataFilter.Sort)) if ( string.IsNullOrWhiteSpace(SortJson))
{ {
//sort by default field descending which should in theory always be the id column of the main object in the list //sort by default field descending which should in theory always be the id column of the main object in the list
//which should return the results to user with most recent records at the top if no sort order was specified //which should return the results to user with most recent records at the top if no sort order was specified
@@ -33,7 +33,7 @@ namespace AyaNova.DataList
StringBuilder sb = new StringBuilder(); StringBuilder sb = new StringBuilder();
//iterate the datafilter and concatenate a sql query from it //iterate the datafilter and concatenate a sql query from it
var SortArray = JArray.Parse(dataFilter.Sort); var SortArray = JArray.Parse(SortJson);
for (int i = 0; i < SortArray.Count; i++) for (int i = 0; i < SortArray.Count; i++)
{ {

View File

@@ -220,16 +220,12 @@ namespace AyaNova.Biz
var DataList = DataListFactory.GetAyaDataList(inObj.ListKey); var DataList = DataListFactory.GetAyaDataList(inObj.ListKey);
// List<AyaDataListFieldDefinition> FieldList = null;
//if (!AyaFormFieldDefinitions.IsValidFormFieldDefinitionKey(inObj.ListKey))
if (DataList == null) if (DataList == null)
{ {
AddError(ApiErrorCode.VALIDATION_INVALID_VALUE, "ListKey", $"ListKey \"{inObj.ListKey}\" DataListKey is not valid"); AddError(ApiErrorCode.VALIDATION_INVALID_VALUE, "ListKey", $"ListKey \"{inObj.ListKey}\" DataListKey is not valid");
} }
// else
// {
// FieldList = AyaDataListFieldDefinition.AyaObjectFields(inObj.ListKey);
// }
if (inObj.ListKey.Length > 255) if (inObj.ListKey.Length > 255)
@@ -284,6 +280,8 @@ namespace AyaNova.Biz
AddError(ApiErrorCode.VALIDATION_REQUIRED, "Filter", $"Filter array item {i}, object is missing or is empty the required \"value\" property "); AddError(ApiErrorCode.VALIDATION_REQUIRED, "Filter", $"Filter array item {i}, object is missing or is empty the required \"value\" property ");
else else
{ {
//check if the value is present, not what it is exactly, just that it's present
//value also could contain relative date tokens, not that it checks them anyway but just noting it here
if (filterItem["value"].Type == JTokenType.String && string.IsNullOrWhiteSpace(filterItem["value"].Value<string>())) if (filterItem["value"].Type == JTokenType.String && string.IsNullOrWhiteSpace(filterItem["value"].Value<string>()))
AddError(ApiErrorCode.VALIDATION_REQUIRED, "Filter", $"Filter array item {i}, object is missing or is empty the required \"value\" property "); AddError(ApiErrorCode.VALIDATION_REQUIRED, "Filter", $"Filter array item {i}, object is missing or is empty the required \"value\" property ");