Skip to content

Commit e5d4a2c

Browse files
authored
Web UI added cut-over dates
Web UI added cut-over dates
1 parent df38127 commit e5d4a2c

13 files changed

Lines changed: 336 additions & 172 deletions

File tree

samples/Rules.Framework.WebUI.Sample/Enums/ContentTypes.cs

Lines changed: 9 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,22 +2,20 @@ namespace Rules.Framework.WebUI.Sample.Enums
22
{
33
public enum ContentTypes
44
{
5-
None = 0,
5+
TestNumber = 0,
66

7-
TestNumber = 1,
7+
TestString = 1,
88

9-
TestString = 2,
9+
TestBoolean = 2,
1010

11-
TestBoolean = 3,
11+
TestDecimal = 3,
1212

13-
TestDecimal = 4,
13+
TestShort = 4,
1414

15-
TestShort = 5,
15+
TestDateTime = 5,
1616

17-
TestDateTime = 6,
17+
TestLong = 6,
1818

19-
TestLong = 7,
20-
21-
TestBlob = 8
19+
TestBlob = 7
2220
}
23-
}
21+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
namespace Rules.Framework.WebUI.Dto
2+
{
3+
using System;
4+
5+
internal sealed class RulesFilterDto
6+
{
7+
public string Content { get; set; }
8+
public string ContentType { get; set; }
9+
public DateTime? DateBegin { get; set; }
10+
public DateTime? DateEnd { get; set; }
11+
public string Name { get; set; }
12+
public RuleStatusDto? Status { get; set; }
13+
}
14+
}

src/Rules.Framework.WebUI/Dto/RuleDto.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,12 @@ namespace Rules.Framework.WebUI.Dto
33
internal sealed class RuleDto
44
{
55
public ConditionNodeDto Conditions { get; internal set; }
6+
public string ContentType { get; internal set; }
67
public string DateBegin { get; internal set; }
78
public string DateEnd { get; internal set; }
89
public string Name { get; internal set; }
910
public int? Priority { get; internal set; }
1011
public string Status { get; internal set; }
1112
public object Value { get; internal set; }
1213
}
13-
}
14+
}

src/Rules.Framework.WebUI/Dto/RuleStatusDto.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ namespace Rules.Framework.WebUI.Dto
22
{
33
internal enum RuleStatusDto : short
44
{
5-
Inactive,
5+
Expired,
66
Active,
77
Pending,
88
Deactivated

src/Rules.Framework.WebUI/Dto/RuleStatusDtoAnalyzer.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,10 @@ public RuleStatusDto Analyze(DateTime dateBegin, DateTime? dateEnd)
1818

1919
if (dateEnd.Value <= DateTime.UtcNow)
2020
{
21-
return RuleStatusDto.Inactive;
21+
return RuleStatusDto.Expired;
2222
}
2323

2424
return RuleStatusDto.Active;
2525
}
2626
}
27-
}
27+
}

src/Rules.Framework.WebUI/Extensions/RuleDtoExtensions.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,11 +41,12 @@ public static ConditionNodeDto ToConditionNodeDto(this GenericConditionNode root
4141
};
4242
}
4343

44-
public static RuleDto ToRuleDto(this GenericRule rule, IRuleStatusDtoAnalyzer ruleStatusDtoAnalyzer)
44+
public static RuleDto ToRuleDto(this GenericRule rule, string ContentType, IRuleStatusDtoAnalyzer ruleStatusDtoAnalyzer)
4545
{
4646
return new RuleDto
4747
{
4848
Conditions = rule.RootCondition?.ToConditionNodeDto(),
49+
ContentType = ContentType,
4950
Priority = rule.Priority,
5051
Name = rule.Name,
5152
Value = rule.Content,

src/Rules.Framework.WebUI/Handlers/GetContentTypeHandler.cs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,14 +13,14 @@ internal sealed class GetContentTypeHandler : WebUIRequestHandlerBase
1313
{
1414
private static readonly string[] resourcePath = new[] { "/{0}/api/v1/contentTypes" };
1515

16-
private readonly IGenericRulesEngine genericRulesEngineAdapter;
16+
private readonly IGenericRulesEngine genericRulesEngine;
1717
private readonly IRuleStatusDtoAnalyzer ruleStatusDtoAnalyzer;
1818

19-
public GetContentTypeHandler(IGenericRulesEngine rulesEngine,
19+
public GetContentTypeHandler(IGenericRulesEngine genericRulesEngine,
2020
IRuleStatusDtoAnalyzer ruleStatusDtoAnalyzer,
2121
WebUIOptions webUIOptions) : base(resourcePath, webUIOptions)
2222
{
23-
this.genericRulesEngineAdapter = rulesEngine;
23+
this.genericRulesEngine = genericRulesEngine;
2424
this.ruleStatusDtoAnalyzer = ruleStatusDtoAnalyzer;
2525
}
2626

@@ -30,15 +30,15 @@ protected override async Task HandleRequestAsync(HttpRequest httpRequest, HttpRe
3030
{
3131
try
3232
{
33-
var contents = this.genericRulesEngineAdapter.GetContentTypes();
33+
var contents = this.genericRulesEngine.GetContentTypes();
3434

3535
var contentTypes = new List<ContentTypeDto>();
3636
var index = 0;
3737
foreach (var identifier in contents.Select(c => c.Identifier))
3838
{
3939
var genericContentType = new GenericContentType { Identifier = identifier };
4040

41-
var genericRules = await this.genericRulesEngineAdapter
41+
var genericRules = await this.genericRulesEngine
4242
.SearchAsync(new SearchArgs<GenericContentType, GenericConditionType>(genericContentType,
4343
DateTime.MinValue,
4444
DateTime.MaxValue))

src/Rules.Framework.WebUI/Handlers/GetIndexPageHandler.cs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ protected override async Task HandleRequestAsync(HttpRequest httpRequest, HttpRe
2323
var path = httpRequest.Path.Value;
2424
var httpContext = httpRequest.HttpContext;
2525

26-
if (Regex.IsMatch(path, $"^/?{Regex.Escape(this.webUIOptions.RoutePrefix)}/?$", RegexOptions.IgnoreCase))
26+
if (Regex.IsMatch(path, $"^/?{Regex.Escape(this.WebUIOptions.RoutePrefix)}/?$", RegexOptions.IgnoreCase))
2727
{
2828
// Use relative redirect to support proxy environments
2929
var relativeIndexUrl = string.IsNullOrEmpty(path) || path.EndsWith("/")
@@ -33,7 +33,7 @@ protected override async Task HandleRequestAsync(HttpRequest httpRequest, HttpRe
3333
RespondWithRedirect(httpContext.Response, relativeIndexUrl);
3434
}
3535

36-
if (Regex.IsMatch(path, $"^/{Regex.Escape(this.webUIOptions.RoutePrefix)}/?index.html$", RegexOptions.IgnoreCase))
36+
if (Regex.IsMatch(path, $"^/{Regex.Escape(this.WebUIOptions.RoutePrefix)}/?index.html$", RegexOptions.IgnoreCase))
3737
{
3838
await this.RespondWithIndexHtmlAsync(httpContext.Response, next).ConfigureAwait(false);
3939
}
@@ -52,8 +52,8 @@ private IDictionary<string, string> GetIndexArguments()
5252
{
5353
return new Dictionary<string, string>
5454
{
55-
{ "%(DocumentTitle)", this.webUIOptions.DocumentTitle },
56-
{ "%(HeadContent)", this.webUIOptions.HeadContent }
55+
{ "%(DocumentTitle)", this.WebUIOptions.DocumentTitle },
56+
{ "%(HeadContent)", this.WebUIOptions.HeadContent }
5757
};
5858
}
5959

@@ -66,7 +66,7 @@ private async Task RespondWithIndexHtmlAsync(HttpResponse httpResponse, RequestD
6666

6767
var originalBody = httpResponse.Body;
6868

69-
using (var stream = this.webUIOptions.IndexStream())
69+
using (var stream = this.WebUIOptions.IndexStream())
7070
{
7171
httpResponse.Body = stream;
7272
await next(httpResponse.HttpContext).ConfigureAwait(false);
Lines changed: 106 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,63 +1,63 @@
11
namespace Rules.Framework.WebUI.Handlers
22
{
33
using System;
4+
using System.Collections.Generic;
45
using System.Linq;
56
using System.Net;
7+
using System.Text.Json;
68
using System.Threading.Tasks;
9+
using System.Web;
710
using Microsoft.AspNetCore.Http;
811
using Rules.Framework.Generics;
912
using Rules.Framework.WebUI.Dto;
1013
using Rules.Framework.WebUI.Extensions;
1114

1215
internal sealed class GetRulesHandler : WebUIRequestHandlerBase
1316
{
14-
private const string dateFormat = "dd/MM/yyyy HH:mm:ss";
1517
private static readonly string[] resourcePath = new[] { "/{0}/api/v1/rules" };
16-
private readonly IGenericRulesEngine rulesEngine;
18+
private readonly IGenericRulesEngine genericRulesEngine;
1719
private readonly IRuleStatusDtoAnalyzer ruleStatusDtoAnalyzer;
1820

19-
public GetRulesHandler(IGenericRulesEngine rulesEngine, IRuleStatusDtoAnalyzer ruleStatusDtoAnalyzer, WebUIOptions webUIOptions) : base(resourcePath, webUIOptions)
21+
public GetRulesHandler(IGenericRulesEngine genericRulesEngine, IRuleStatusDtoAnalyzer ruleStatusDtoAnalyzer, WebUIOptions webUIOptions) : base(resourcePath, webUIOptions)
2022
{
21-
this.rulesEngine = rulesEngine;
23+
this.genericRulesEngine = genericRulesEngine;
2224
this.ruleStatusDtoAnalyzer = ruleStatusDtoAnalyzer;
2325
}
2426

2527
protected override HttpMethod HttpMethod => HttpMethod.GET;
2628

27-
protected override async Task HandleRequestAsync(HttpRequest httpRequest, HttpResponse httpResponse, RequestDelegate next)
29+
protected override async Task HandleRequestAsync(HttpRequest httpRequest,
30+
HttpResponse httpResponse,
31+
RequestDelegate next)
2832
{
29-
if (!httpRequest.Query.TryGetValue("contentType", out var contentTypeName))
33+
var rulesFilter = this.GetRulesFilterFromRequest(httpRequest);
34+
35+
if (!IsValidFilterDates(rulesFilter))
3036
{
31-
await this.WriteResponseAsync(httpResponse, new { Message = "contentType is required" }, (int)HttpStatusCode.BadRequest)
32-
.ConfigureAwait(false);
37+
await this.WriteResponseAsync(httpResponse, new { Message = "Date begin cannot be greater than after" }, (int)HttpStatusCode.BadRequest)
38+
.ConfigureAwait(false);
3339

3440
return;
3541
}
3642

3743
try
3844
{
39-
var genericRules = await this.rulesEngine.SearchAsync(
40-
new SearchArgs<GenericContentType, GenericConditionType>(
41-
new GenericContentType { Identifier = contentTypeName },
42-
DateTime.MinValue, DateTime.MaxValue))
43-
.ConfigureAwait(false);
44-
45-
var rules = Enumerable.Empty<RuleDto>();
46-
47-
var priorityCriteria = this.rulesEngine.GetPriorityCriteria();
45+
var rules = new List<RuleDto>();
4846

49-
if (genericRules != null && genericRules.Any())
47+
if (rulesFilter.ContentType.Equals("all"))
5048
{
51-
if (priorityCriteria == PriorityCriterias.BottommostRuleWins)
52-
{
53-
genericRules = genericRules.OrderByDescending(r => r.Priority);
54-
}
55-
else
49+
var contents = this.genericRulesEngine.GetContentTypes();
50+
51+
foreach (var identifier in contents.Select(c => c.Identifier))
5652
{
57-
genericRules = genericRules.OrderBy(r => r.Priority);
53+
var rulesForContentType = await this.GetRulesForContentyType(identifier, rulesFilter).ConfigureAwait(false);
54+
rules.AddRange(rulesForContentType);
5855
}
59-
60-
rules = genericRules.Select(g => g.ToRuleDto(this.ruleStatusDtoAnalyzer));
56+
}
57+
else
58+
{
59+
var rulesForContentType = await this.GetRulesForContentyType(rulesFilter.ContentType, rulesFilter).ConfigureAwait(false);
60+
rules.AddRange(rulesForContentType);
6161
}
6262

6363
await this.WriteResponseAsync(httpResponse, rules, (int)HttpStatusCode.OK).ConfigureAwait(false);
@@ -67,5 +67,85 @@ protected override async Task HandleRequestAsync(HttpRequest httpRequest, HttpRe
6767
await this.WriteExceptionResponseAsync(httpResponse, ex).ConfigureAwait(false);
6868
}
6969
}
70+
71+
private static bool IsValidFilterDates(RulesFilterDto rulesFilter)
72+
{
73+
return (rulesFilter.DateBegin is null
74+
|| rulesFilter.DateEnd is null) ||
75+
(rulesFilter.DateBegin <= rulesFilter.DateEnd);
76+
}
77+
78+
private IEnumerable<RuleDto> ApplyFilters(RulesFilterDto rulesFilter, IEnumerable<RuleDto> genericRulesDto)
79+
{
80+
if (!string.IsNullOrWhiteSpace(rulesFilter.Content))
81+
{
82+
genericRulesDto = genericRulesDto.Where(g =>
83+
{
84+
return JsonSerializer.Serialize(g.Value).Contains(rulesFilter.Content, StringComparison.OrdinalIgnoreCase);
85+
});
86+
}
87+
88+
if (!string.IsNullOrWhiteSpace(rulesFilter.Name))
89+
{
90+
genericRulesDto = genericRulesDto.Where(g =>
91+
{
92+
return g.Name.Contains(rulesFilter.Name, StringComparison.OrdinalIgnoreCase);
93+
});
94+
}
95+
if (rulesFilter.Status != null)
96+
{
97+
genericRulesDto = genericRulesDto.Where(g =>
98+
{
99+
return g.Status.Equals(rulesFilter.Status.ToString());
100+
});
101+
}
102+
103+
return genericRulesDto;
104+
}
105+
106+
private RulesFilterDto GetRulesFilterFromRequest(HttpRequest httpRequest)
107+
{
108+
var parseQueryString = HttpUtility.ParseQueryString(httpRequest.QueryString.Value);
109+
110+
var rulesFilterAsString = JsonSerializer.Serialize(parseQueryString.Cast<string>().ToDictionary(k => k, v => string.IsNullOrWhiteSpace(parseQueryString[v]) ? null : parseQueryString[v]));
111+
var rulesFilter = JsonSerializer.Deserialize<RulesFilterDto>(rulesFilterAsString, this.SerializerOptions);
112+
113+
rulesFilter.ContentType = string.IsNullOrWhiteSpace(rulesFilter.ContentType) ? "all" : rulesFilter.ContentType;
114+
115+
rulesFilter.DateEnd ??= DateTime.MaxValue;
116+
117+
rulesFilter.DateBegin ??= DateTime.MinValue;
118+
119+
return rulesFilter;
120+
}
121+
122+
private async Task<IEnumerable<RuleDto>> GetRulesForContentyType(string identifier, RulesFilterDto rulesFilter)
123+
{
124+
var genericRules = await this.genericRulesEngine.SearchAsync(
125+
new SearchArgs<GenericContentType, GenericConditionType>(
126+
new GenericContentType { Identifier = identifier },
127+
rulesFilter.DateBegin.Value, rulesFilter.DateEnd.Value))
128+
.ConfigureAwait(false);
129+
130+
var priorityCriteria = this.genericRulesEngine.GetPriorityCriteria();
131+
132+
if (genericRules != null && genericRules.Any())
133+
{
134+
if (priorityCriteria == PriorityCriterias.BottommostRuleWins)
135+
{
136+
genericRules = genericRules.OrderByDescending(r => r.Priority);
137+
}
138+
else
139+
{
140+
genericRules = genericRules.OrderBy(r => r.Priority);
141+
}
142+
143+
var genericRulesDto = this.ApplyFilters(rulesFilter, genericRules.Select(g => g.ToRuleDto(identifier, this.ruleStatusDtoAnalyzer)));
144+
145+
return genericRulesDto;
146+
}
147+
148+
return Enumerable.Empty<RuleDto>();
149+
}
70150
}
71151
}

src/Rules.Framework.WebUI/WebUIRequestHandlerBase.cs

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,14 +13,13 @@ namespace Rules.Framework.WebUI
1313

1414
internal abstract class WebUIRequestHandlerBase : IHttpRequestHandler
1515
{
16-
protected readonly WebUIOptions webUIOptions;
17-
18-
private readonly JsonSerializerOptions SerializerOptions;
16+
protected readonly JsonSerializerOptions SerializerOptions;
17+
protected readonly WebUIOptions WebUIOptions;
1918

2019
protected WebUIRequestHandlerBase(string[] resourcePath, WebUIOptions webUIOptions)
2120
{
2221
this.ResourcePath = resourcePath;
23-
this.webUIOptions = webUIOptions;
22+
this.WebUIOptions = webUIOptions;
2423
this.SerializerOptions = new JsonSerializerOptions
2524
{
2625
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
@@ -52,7 +51,7 @@ protected bool CanHandle(HttpRequest httpRequest)
5251
{
5352
var resource = httpRequest.Path.ToUriComponent();
5453

55-
var resourcesPath = this.ResourcePath.Select(r => string.Format(r, this.webUIOptions.RoutePrefix));
54+
var resourcesPath = this.ResourcePath.Select(r => string.Format(r, this.WebUIOptions.RoutePrefix));
5655

5756
if (!resourcesPath.Contains(resource))
5857
{
@@ -95,4 +94,4 @@ protected virtual async Task WriteResponseAsync<T>(HttpResponse httpResponse, T
9594
}
9695
}
9796
}
98-
}
97+
}

0 commit comments

Comments
 (0)