Skip to content

Commit 8a8f3f2

Browse files
committed
Add new validation functions for date and time
1 parent d443951 commit 8a8f3f2

File tree

15 files changed

+458
-127
lines changed

15 files changed

+458
-127
lines changed

JsonSchema/JsonSchema.csproj

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,9 @@
88
<AssemblyName>RelogicLabs.JsonSchema</AssemblyName>
99
<Authors>Relogic Labs</Authors>
1010
<Company>Relogic Labs</Company>
11-
<Version>1.7.0</Version>
12-
<PackageVersion>1.7.0</PackageVersion>
13-
<AssemblyVersion>1.7.0</AssemblyVersion>
11+
<Version>1.8.0</Version>
12+
<PackageVersion>1.8.0</PackageVersion>
13+
<AssemblyVersion>1.8.0</AssemblyVersion>
1414
<PackageTags>JsonSchema;Schema;Json;Validation;Assert;Test</PackageTags>
1515
<Copyright>Copyright © Relogic Labs. All rights reserved.</Copyright>
1616
<NeutralLanguage>en</NeutralLanguage>
@@ -43,4 +43,4 @@
4343
<EmbeddedResource Remove="doc\**"/>
4444
<None Remove="doc\**"/>
4545
</ItemGroup>
46-
</Project>
46+
</Project>

JsonSchema/RelogicLabs/JsonSchema/Functions/CoreFunctions1.cs

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -14,29 +14,29 @@ public bool Length(JString target, JInteger length)
1414
{
1515
var _length = target.Value.Length;
1616
if(_length != length) return FailWith(new JsonSchemaException(
17-
new ErrorDetail(SLEN01, "Invalid string length"),
18-
new ExpectedDetail(Function, $"length {length}"),
19-
new ActualDetail(target, $"found {_length} for {target}")));
17+
new ErrorDetail(SLEN01, $"Invalid length of string {target}"),
18+
new ExpectedDetail(Function, $"a string of length {length}"),
19+
new ActualDetail(target, $"found {_length} which does not match")));
2020
return true;
2121
}
2222

2323
public bool Length(JArray target, JInteger length)
2424
{
2525
var _length = target.Elements.Count;
2626
if(_length != length) return FailWith(new JsonSchemaException(
27-
new ErrorDetail(ALEN01, "Invalid array length"),
28-
new ExpectedDetail(Function, $"length {length}"),
29-
new ActualDetail(target, $"found {_length} for {target.GetOutline()}")));
27+
new ErrorDetail(ALEN01, $"Invalid length of array {target.GetOutline()}"),
28+
new ExpectedDetail(Function, $"an array of length {length}"),
29+
new ActualDetail(target, $"found {_length} which does not match")));
3030
return true;
3131
}
3232

3333
public bool Length(JObject target, JInteger length)
3434
{
3535
var _length = target.Properties.Count;
3636
if(_length != length) return FailWith(new JsonSchemaException(
37-
new ErrorDetail(OLEN01, "Invalid object size or length"),
38-
new ExpectedDetail(Function, $"length {length}"),
39-
new ActualDetail(target, $"found {_length} for {target.GetOutline()}")));
37+
new ErrorDetail(OLEN01, $"Invalid size or length of object {target.GetOutline()}"),
38+
new ExpectedDetail(Function, $"an object of length {length}"),
39+
new ActualDetail(target, $"found {_length} which does not match")));
4040
return true;
4141
}
4242

JsonSchema/RelogicLabs/JsonSchema/Functions/CoreFunctions3.cs

Lines changed: 0 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
using System.Text.RegularExpressions;
2-
using RelogicLabs.JsonSchema.Time;
32
using RelogicLabs.JsonSchema.Exceptions;
43
using RelogicLabs.JsonSchema.Message;
54
using RelogicLabs.JsonSchema.Types;
@@ -122,38 +121,4 @@ public bool Phone(JString target)
122121
new ActualDetail(target, $"found {target} that is invalid")));
123122
return true;
124123
}
125-
126-
public bool Date(JString target, JString pattern)
127-
=> DateTime(target, pattern, DateTimeType.DATE_TYPE);
128-
129-
public bool Time(JString target, JString pattern)
130-
=> DateTime(target, pattern, DateTimeType.TIME_TYPE);
131-
132-
private bool DateTime(JString target, JString pattern, DateTimeType type)
133-
{
134-
try
135-
{
136-
var validator = new DateTimeValidator(pattern);
137-
if(type == DateTimeType.DATE_TYPE) validator.ValidateDate(target);
138-
else if(type == DateTimeType.TIME_TYPE) validator.ValidateTime(target);
139-
else throw new ArgumentException($"Invalid {nameof(DateTimeType)} value");
140-
return true;
141-
}
142-
catch(DateTimeLexerException ex)
143-
{
144-
return FailWith(new JsonSchemaException(
145-
new ErrorDetail(ex.Code, ex.Message),
146-
new ExpectedDetail(Function, $"a valid {type} pattern"),
147-
new ActualDetail(target, $"found {pattern} that is invalid"),
148-
ex));
149-
}
150-
catch(InvalidDateTimeException ex)
151-
{
152-
return FailWith(new JsonSchemaException(
153-
new ErrorDetail(ex.Code, ex.Message),
154-
new ExpectedDetail(Function, $"a valid {type} formatted as {pattern}"),
155-
new ActualDetail(target, $"found {target} that is invalid or malformatted"),
156-
ex));
157-
}
158-
}
159124
}
Lines changed: 155 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,155 @@
1+
using RelogicLabs.JsonSchema.Exceptions;
2+
using RelogicLabs.JsonSchema.Message;
3+
using RelogicLabs.JsonSchema.Time;
4+
using RelogicLabs.JsonSchema.Types;
5+
using static RelogicLabs.JsonSchema.Message.ErrorCode;
6+
using static RelogicLabs.JsonSchema.Time.DateTimeType;
7+
8+
namespace RelogicLabs.JsonSchema.Functions;
9+
10+
public sealed partial class CoreFunctions
11+
{
12+
public bool Date(JString target, JString pattern)
13+
=> DateTime(target, pattern, DATE_TYPE);
14+
15+
public bool Time(JString target, JString pattern)
16+
=> DateTime(target, pattern, TIME_TYPE);
17+
18+
private bool DateTime(JString target, JString pattern, DateTimeType type)
19+
=> !ReferenceEquals(new DateTimeFunction(pattern, type).Parse(Function, target), null);
20+
21+
public bool Before(JDateTime target, JString reference)
22+
{
23+
var dateTime = GetDateTime(target.GetParser(), reference);
24+
if(ReferenceEquals(dateTime, null)) return false;
25+
if(target.DateTime.Compare(dateTime.DateTime) < 0) return true;
26+
var type = target.DateTime.Type;
27+
var code = type == DATE_TYPE ? BFOR01 : BFOR02;
28+
return FailWith(new JsonSchemaException(
29+
new ErrorDetail(code, $"{type} is not earlier than specified"),
30+
new ExpectedDetail(reference, $"a {type} before {reference}"),
31+
new ActualDetail(target, $"found {target} which is not inside limit")
32+
));
33+
}
34+
35+
public bool After(JDateTime target, JString reference)
36+
{
37+
var dateTime = GetDateTime(target.GetParser(), reference);
38+
if(ReferenceEquals(dateTime, null)) return false;
39+
if(target.DateTime.Compare(dateTime.DateTime) > 0) return true;
40+
var type = target.DateTime.Type;
41+
var code = type == DATE_TYPE ? AFTR01 : AFTR02;
42+
return FailWith(new JsonSchemaException(
43+
new ErrorDetail(code, $"{type} is not later than specified"),
44+
new ExpectedDetail(reference, $"a {type} after {reference}"),
45+
new ActualDetail(target, $"found {target} which is not inside limit")
46+
));
47+
}
48+
49+
public bool Range(JDateTime target, JString start, JString end)
50+
{
51+
var _start = GetDateTime(target.GetParser(), start);
52+
if(ReferenceEquals(_start, null)) return false;
53+
var _end = GetDateTime(target.GetParser(), end);
54+
if(ReferenceEquals(_end, null)) return false;
55+
if(target.DateTime.Compare(_start.DateTime) < 0)
56+
{
57+
var type = target.DateTime.Type;
58+
var code = type == DATE_TYPE ? DRNG01 : DRNG02;
59+
return FailWith(new JsonSchemaException(
60+
new ErrorDetail(code, $"{type} is earlier than start {type}"),
61+
new ExpectedDetail(_start, $"a {type} from or after {_start}"),
62+
new ActualDetail(target, $"found {target} which is before start {type}")
63+
));
64+
}
65+
if(target.DateTime.Compare(_end.DateTime) > 0)
66+
{
67+
var type = target.DateTime.Type;
68+
var code = type == DATE_TYPE ? DRNG03 : DRNG04;
69+
return FailWith(new JsonSchemaException(
70+
new ErrorDetail(code, $"{type} is later than end {type}"),
71+
new ExpectedDetail(_end, $"a {type} until or before {_end}"),
72+
new ActualDetail(target, $"found {target} which is after end {type}")
73+
));
74+
}
75+
return true;
76+
}
77+
78+
public bool Range(JDateTime target, JUndefined start, JString end)
79+
{
80+
var _end = GetDateTime(target.GetParser(), end);
81+
if(ReferenceEquals(_end, null)) return false;
82+
if(target.DateTime.Compare(_end.DateTime) > 0)
83+
{
84+
var type = target.DateTime.Type;
85+
var code = type == DATE_TYPE ? DRNG05 : DRNG06;
86+
return FailWith(new JsonSchemaException(
87+
new ErrorDetail(code, $"{type} is later than end {type}"),
88+
new ExpectedDetail(_end, $"a {type} until or before {_end}"),
89+
new ActualDetail(target, $"found {target} which is after end {type}")
90+
));
91+
}
92+
return true;
93+
}
94+
95+
public bool Range(JDateTime target, JString start, JUndefined end)
96+
{
97+
var _start = GetDateTime(target.GetParser(), start);
98+
if(ReferenceEquals(_start, null)) return false;
99+
if(target.DateTime.Compare(_start.DateTime) < 0)
100+
{
101+
var type = target.DateTime.Type;
102+
var code = type == DATE_TYPE ? DRNG07 : DRNG08;
103+
return FailWith(new JsonSchemaException(
104+
new ErrorDetail(code, $"{type} is earlier than start {type}"),
105+
new ExpectedDetail(_start, $"a {type} from or after {_start}"),
106+
new ActualDetail(target, $"found {target} which is before start {type}")
107+
));
108+
}
109+
return true;
110+
}
111+
112+
public bool Start(JDateTime target, JString reference)
113+
{
114+
var dateTime = GetDateTime(target.GetParser(), reference);
115+
if(ReferenceEquals(dateTime, null)) return false;
116+
if(target.DateTime.Compare(dateTime.DateTime) < 0)
117+
{
118+
var type = target.DateTime.Type;
119+
var code = type == DATE_TYPE ? STRT01 : STRT02;
120+
return FailWith(new JsonSchemaException(
121+
new ErrorDetail(code, $"{type} is earlier than specified"),
122+
new ExpectedDetail(dateTime, $"a {type} from or after {dateTime}"),
123+
new ActualDetail(target, $"found {target} which is before limit")
124+
));
125+
}
126+
return true;
127+
}
128+
129+
public bool End(JDateTime target, JString reference)
130+
{
131+
var dateTime = GetDateTime(target.GetParser(), reference);
132+
if(ReferenceEquals(dateTime, null)) return false;
133+
if(target.DateTime.Compare(dateTime.DateTime) > 0)
134+
{
135+
var type = target.DateTime.Type;
136+
var code = type == DATE_TYPE ? ENDE01 : ENDE02;
137+
return FailWith(new JsonSchemaException(
138+
new ErrorDetail(code, $"{type} is later than specified"),
139+
new ExpectedDetail(dateTime, $"a {type} until or before {dateTime}"),
140+
new ActualDetail(target, $"found {target} which is after limit")
141+
));
142+
}
143+
return true;
144+
}
145+
146+
private JDateTime? GetDateTime(DateTimeParser parser, JString dateTime)
147+
{
148+
if(dateTime.Derived is JDateTime _result
149+
&& _result.DateTime.Type == parser.Type) return _result;
150+
var _dateTime = new DateTimeFunction(parser).Parse(Function, dateTime);
151+
if(ReferenceEquals(_dateTime, null)) return null;
152+
dateTime.Derived = _dateTime.Create(dateTime);
153+
return (JDateTime) dateTime.Derived;
154+
}
155+
}
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
using RelogicLabs.JsonSchema.Exceptions;
2+
using RelogicLabs.JsonSchema.Message;
3+
using RelogicLabs.JsonSchema.Time;
4+
using RelogicLabs.JsonSchema.Types;
5+
6+
namespace RelogicLabs.JsonSchema.Functions;
7+
8+
internal class DateTimeFunction
9+
{
10+
private DateTimeParser? _parser;
11+
12+
public string Pattern { get; }
13+
public DateTimeType Type { get; }
14+
15+
public DateTimeFunction(string pattern, DateTimeType type)
16+
{
17+
Pattern = pattern;
18+
Type = type;
19+
}
20+
21+
public DateTimeFunction(DateTimeParser parser)
22+
{
23+
Pattern = parser.Pattern;
24+
Type = parser.Type;
25+
_parser = parser;
26+
}
27+
28+
public JsonDateTime? Parse(JFunction function, JString dateTime)
29+
{
30+
try
31+
{
32+
_parser ??= new DateTimeParser(Pattern, Type);
33+
return _parser.Parse(dateTime);
34+
}
35+
catch(DateTimeLexerException ex)
36+
{
37+
function.FailWith(new JsonSchemaException(
38+
new ErrorDetail(ex.Code, ex.Message),
39+
new ExpectedDetail(function, $"a valid {Type} pattern"),
40+
new ActualDetail(dateTime, $"found {Pattern} that is invalid"),
41+
ex));
42+
}
43+
catch(InvalidDateTimeException ex)
44+
{
45+
function.FailWith(new JsonSchemaException(
46+
new ErrorDetail(ex.Code, ex.Message),
47+
new ExpectedDetail(function, $"a valid {Type} formatted as {Pattern}"),
48+
new ActualDetail(dateTime, $"found {dateTime} that is invalid or malformatted"),
49+
ex));
50+
}
51+
return null;
52+
}
53+
}

JsonSchema/RelogicLabs/JsonSchema/Functions/FunctionBase.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,6 @@ public abstract class FunctionBase
1111

1212
protected FunctionBase(RuntimeContext runtime) => Runtime = runtime;
1313

14-
protected bool FailWith(JsonSchemaException exception)
14+
public bool FailWith(JsonSchemaException exception)
1515
=> Runtime.FailWith(exception);
1616
}

JsonSchema/RelogicLabs/JsonSchema/Message/ErrorCode.cs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,16 @@ namespace RelogicLabs.JsonSchema.Message;
22

33
internal static class ErrorCode
44
{
5+
public const string AFTR01 = "AFTR01";
6+
public const string AFTR02 = "AFTR02";
57
public const string ALEN01 = "ALEN01";
68
public const string ALEN02 = "ALEN02";
79
public const string ALEN03 = "ALEN03";
810
public const string ALEN04 = "ALEN04";
911
public const string ALEN05 = "ALEN05";
1012
public const string ARRY01 = "ARRY01";
13+
public const string BFOR01 = "BFOR01";
14+
public const string BFOR02 = "BFOR02";
1115
public const string BOOL01 = "BOOL01";
1216
public const string CLAS01 = "CLAS01";
1317
public const string CLAS02 = "CLAS02";
@@ -58,6 +62,14 @@ internal static class ErrorCode
5862
public const string DMON03 = "DMON03";
5963
public const string DMON04 = "DMON04";
6064
public const string DMON05 = "DMON05";
65+
public const string DRNG01 = "DRNG01";
66+
public const string DRNG02 = "DRNG02";
67+
public const string DRNG03 = "DRNG03";
68+
public const string DRNG04 = "DRNG04";
69+
public const string DRNG05 = "DRNG05";
70+
public const string DRNG06 = "DRNG06";
71+
public const string DRNG07 = "DRNG07";
72+
public const string DRNG08 = "DRNG08";
6173
public const string DSEC01 = "DSEC01";
6274
public const string DSEC02 = "DSEC02";
6375
public const string DSEC03 = "DSEC03";
@@ -87,6 +99,8 @@ internal static class ErrorCode
8799
public const string DYAR03 = "DYAR03";
88100
public const string ELEM01 = "ELEM01";
89101
public const string EMAL01 = "EMAL01";
102+
public const string ENDE01 = "ENDE01";
103+
public const string ENDE02 = "ENDE02";
90104
public const string ENUM01 = "ENUM01";
91105
public const string ENUM02 = "ENUM02";
92106
public const string FLOT01 = "FLOT01";
@@ -140,6 +154,8 @@ internal static class ErrorCode
140154
public const string SLEX01 = "SLEX01";
141155
public const string SPRS01 = "SPRS01";
142156
public const string STRN01 = "STRN01";
157+
public const string STRT01 = "STRT01";
158+
public const string STRT02 = "STRT02";
143159
public const string URLA01 = "URLA01";
144160
public const string URLA02 = "URLA02";
145161
public const string URLA03 = "URLA03";

JsonSchema/RelogicLabs/JsonSchema/Message/ErrorDetail.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
using RelogicLabs.JsonSchema.Utilities;
2+
13
namespace RelogicLabs.JsonSchema.Message;
24

35
public sealed class ErrorDetail
@@ -21,6 +23,6 @@ public sealed class ErrorDetail
2123
public ErrorDetail(string code, string message)
2224
{
2325
Code = code;
24-
Message = message;
26+
Message = message.Capitalize();
2527
}
2628
}

0 commit comments

Comments
 (0)