Skip to content

Commit 083dd58

Browse files
jcoitinopmoleri
authored andcommitted
Query Builder Endpoint (#52)
**Operations / Data Type Matrix:** | Operation Name | Boolean | Date | DateTime | Time | Number | String | Implemented | |---------------------|---------|------|----------|------|--------|--------|-------------| | null | X | X | X | X | X | X | ✔ | | notNull | X | X | X | X | X | X | ✔ | | in | X | X | X | X | X | X | ✔ | | inQuery | X | X | X | X | X | X | ✔ | | notInQuery | X | X | X | X | X | X | ✔ | | empty | X | X | X | X | X | X | ✔ | | notEmpty | X | X | X | X | X | X | ✔ | | all | X | | | | | | ✔ | | true | X | | | | | | ✔ | | false | X | | | | | | ✔ | | equals | | X | X | | X | X | ✔ | | doesNotEqual | | X | X | | X | X | ✔ | | before | | X | X | X | | | ✔ | | after | | X | X | X | | | ✔ | | today | | X | X | | | | ✔ | | yesterday | | X | X | | | | ✔ | | thisMonth | | X | X | | | | ✔ | | lastMonth | | X | X | | | | ✔ | | nextMonth | | X | X | | | | ✔ | | thisYear | | X | X | | | | ✔ | | lastYear | | X | X | | | | ✔ | | nextYear | | X | X | | | | ✔ | | at | | | | X | | | ✔ | | not_at | | | | X | | | ✔ | | at_before | | | | X | | | ✔ | | at_after | | | | X | | | ✔ | | greaterThan | | | | | X | | ✔ | | lessThan | | | | | X | | ✔ | | greaterThanOrEqualTo| | | | | X | | ✔ | | lessThanOrEqualTo | | | | | X | | ✔ | | contains | | | | | | X | ✔ | | doesNotContain | | | | | | X | ✔ | | startsWith | | | | | | X | ✔ | | endsWith | | | | | | X | ✔ | --------- Co-authored-by: Javier Coitino <[email protected]>
1 parent 296ebac commit 083dd58

File tree

9 files changed

+478
-17
lines changed

9 files changed

+478
-17
lines changed

.github/workflows/build-test-ci.yml

Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -7,24 +7,25 @@ on:
77
pull_request:
88
branches:
99
- main
10+
1011
jobs:
1112
build-and-test:
1213
runs-on: ubuntu-latest
1314

1415
steps:
15-
- name: Checkout code
16-
uses: actions/checkout@v3
16+
- name: Checkout code
17+
uses: actions/checkout@v3
1718

18-
- name: Setup .NET
19-
uses: actions/setup-dotnet@v3
20-
with:
21-
dotnet-version: 6.x
19+
- name: Setup .NET
20+
uses: actions/setup-dotnet@v3
21+
with:
22+
dotnet-version: '6.x'
2223

23-
- name: Restore dependencies
24-
run: dotnet restore
24+
- name: Restore dependencies
25+
run: dotnet restore
2526

26-
- name: Build
27-
run: dotnet build --configuration Release
27+
- name: Build
28+
run: dotnet build --configuration Release
2829

29-
- name: Run tests
30-
run: dotnet test --configuration Release --no-build
30+
- name: Run tests
31+
run: dotnet test --configuration Release --no-build
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
namespace QueryBuilder;
2+
3+
using AutoMapper;
4+
using Microsoft.AspNetCore.Mvc;
5+
using Newtonsoft.Json;
6+
using NorthwindCRUD;
7+
using NorthwindCRUD.Models.Dtos;
8+
using System.Diagnostics.CodeAnalysis;
9+
using System.Globalization;
10+
11+
[SuppressMessage("StyleCop.CSharp.LayoutRules", "SA1516:Elements should be separated by blank line", Justification = "...")]
12+
[SuppressMessage("StyleCop.CSharp.SpacingRules", "SA1025:Code should not contain multiple whitespace in a row", Justification = "...")]
13+
[SuppressMessage("StyleCop.CSharp.ReadabilityRules", "SA1134:Attributes should not share line", Justification = "...")]
14+
[SuppressMessage("StyleCop.CSharp.SpacingRules", "SA1011:Closing square brackets should be spaced correctly", Justification = "...")]
15+
public class QueryBuilderResult
16+
{
17+
[JsonProperty(NullValueHandling = NullValueHandling.Ignore)] public AddressDto[]? Addresses { get; set; }
18+
[JsonProperty(NullValueHandling = NullValueHandling.Ignore)] public CategoryDto[]? Categories { get; set; }
19+
[JsonProperty(NullValueHandling = NullValueHandling.Ignore)] public ProductDto[]? Products { get; set; }
20+
[JsonProperty(NullValueHandling = NullValueHandling.Ignore)] public RegionDto[]? Regions { get; set; }
21+
[JsonProperty(NullValueHandling = NullValueHandling.Ignore)] public TerritoryDto[]? Territories { get; set; }
22+
[JsonProperty(NullValueHandling = NullValueHandling.Ignore)] public EmployeeDto[]? Employees { get; set; }
23+
[JsonProperty(NullValueHandling = NullValueHandling.Ignore)] public CustomerDto[]? Customers { get; set; }
24+
[JsonProperty(NullValueHandling = NullValueHandling.Ignore)] public OrderDto[]? Orders { get; set; }
25+
[JsonProperty(NullValueHandling = NullValueHandling.Ignore)] public OrderDetailDto[]? OrderDetails { get; set; }
26+
[JsonProperty(NullValueHandling = NullValueHandling.Ignore)] public ShipperDto[]? Shippers { get; set; }
27+
[JsonProperty(NullValueHandling = NullValueHandling.Ignore)] public SupplierDto[]? Suppliers { get; set; }
28+
}
29+
30+
[ApiController]
31+
[Route("[controller]")]
32+
[SuppressMessage("StyleCop.CSharp.SpacingRules", "SA1025:Code should not contain multiple whitespace in a row", Justification = "...")]
33+
public class QueryBuilderController : ControllerBase
34+
{
35+
private readonly DataContext dataContext;
36+
private readonly IMapper mapper;
37+
private readonly ILogger<QueryBuilderController> logger;
38+
39+
public QueryBuilderController(DataContext dataContext, IMapper mapper, ILogger<QueryBuilderController> logger)
40+
{
41+
this.dataContext = dataContext;
42+
this.mapper = mapper;
43+
this.logger = logger;
44+
}
45+
46+
[HttpPost("ExecuteQuery")]
47+
[Consumes("application/json")]
48+
[Produces("application/json")]
49+
public ActionResult<QueryBuilderResult> ExecuteQuery(Query query)
50+
{
51+
var sanitizedEntity = query.Entity.Replace("\r", string.Empty).Replace("\n", string.Empty);
52+
logger.LogInformation("Executing query for entity: {Entity}", sanitizedEntity);
53+
var t = query.Entity.ToLower(CultureInfo.InvariantCulture);
54+
return Ok(new QueryBuilderResult
55+
{
56+
Addresses = t == "addresses" ? mapper.Map<AddressDto[]>(dataContext.Addresses.Run(query)) : null,
57+
Categories = t == "categories" ? mapper.Map<CategoryDto[]>(dataContext.Categories.Run(query)) : null,
58+
Products = t == "products" ? mapper.Map<ProductDto[]>(dataContext.Products.Run(query)) : null,
59+
Regions = t == "regions" ? mapper.Map<RegionDto[]>(dataContext.Regions.Run(query)) : null,
60+
Territories = t == "territories" ? mapper.Map<TerritoryDto[]>(dataContext.Territories.Run(query)) : null,
61+
Employees = t == "employees" ? mapper.Map<EmployeeDto[]>(dataContext.Employees.Run(query)) : null,
62+
Customers = t == "customers" ? mapper.Map<CustomerDto[]>(dataContext.Customers.Run(query)) : null,
63+
Orders = t == "orders" ? mapper.Map<OrderDto[]>(dataContext.Orders.Run(query)) : null,
64+
OrderDetails = t == "orderdetails" ? mapper.Map<OrderDetailDto[]>(dataContext.OrderDetails.Run(query)) : null,
65+
Shippers = t == "shippers" ? mapper.Map<ShipperDto[]>(dataContext.Shippers.Run(query)) : null,
66+
Suppliers = t == "suppliers" ? mapper.Map<SupplierDto[]>(dataContext.Suppliers.Run(query)) : null,
67+
});
68+
}
69+
}

NorthwindCRUD/Program.cs

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,9 @@
22
using AutoMapper;
33
using GraphQL.AspNet.Configuration.Mvc;
44
using Microsoft.AspNetCore.Authentication.JwtBearer;
5-
using Microsoft.Data.Sqlite;
6-
using Microsoft.EntityFrameworkCore;
7-
using Microsoft.EntityFrameworkCore.Diagnostics;
8-
using Microsoft.Extensions.Options;
95
using Microsoft.IdentityModel.Tokens;
106
using Microsoft.OpenApi.Models;
7+
using Newtonsoft.Json;
118
using Newtonsoft.Json.Converters;
129
using NorthwindCRUD.Filters;
1310
using NorthwindCRUD.Helpers;
@@ -40,7 +37,8 @@ public static void Main(string[] args)
4037
options.SuppressImplicitRequiredAttributeForNonNullableReferenceTypes = true;
4138
}).AddNewtonsoftJson(options =>
4239
{
43-
options.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore;
40+
options.SerializerSettings.DateParseHandling = DateParseHandling.None;
41+
options.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore;
4442
options.SerializerSettings.Converters.Add(new StringEnumConverter());
4543
});
4644

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
namespace QueryBuilder;
2+
3+
public enum FilterType
4+
{
5+
And = 0,
6+
Or = 1,
7+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
namespace QueryBuilder;
2+
3+
public class Query
4+
{
5+
public string Entity { get; set; }
6+
7+
public string[] ReturnFields { get; set; }
8+
9+
public FilterType Operator { get; set; }
10+
11+
public QueryFilter[] FilteringOperands { get; set; }
12+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
namespace QueryBuilder;
2+
3+
public class QueryFilter
4+
{
5+
// Basic condition
6+
public string? FieldName { get; set; }
7+
8+
public bool? IgnoreCase { get; set; }
9+
10+
public QueryFilterCondition? Condition { get; set; }
11+
12+
public object? SearchVal { get; set; }
13+
14+
public Query? SearchTree { get; set; }
15+
16+
// And/Or
17+
public FilterType? Operator { get; set; }
18+
19+
public QueryFilter[] FilteringOperands { get; set; }
20+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
namespace QueryBuilder;
2+
3+
public class QueryFilterCondition
4+
{
5+
public string Name { get; set; }
6+
7+
public bool IsUnary { get; set; }
8+
9+
public string IconName { get; set; }
10+
}

0 commit comments

Comments
 (0)