Skip to content

Commit 556f803

Browse files
committed
Added validation that HTTP verbs should not be present in endpoint action path
1 parent 71b7e18 commit 556f803

10 files changed

Lines changed: 113 additions & 15 deletions

File tree

src/Dibix.Sdk/CodeGeneration/Model/ActionDefinition.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ public sealed class ActionDefinition
1010
public ActionMethod Method { get; set; }
1111
public string OperationId { get; set; }
1212
public string Description { get; set; }
13-
public string ChildRoute { get; set; }
13+
public Token<string> ChildRoute { get; set; }
1414
public ActionRequestBody RequestBody { get; set; }
1515
public ActionFileResponse FileResponse { get; private set; }
1616
public IList<ActionParameter> Parameters { get; }

src/Dibix.Sdk/CodeGeneration/Model/ControllerDefinitionProvider.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -147,7 +147,7 @@ private void ReadControllerAction(string filePath, ControllerDefinition controll
147147
actionDefinition.Method = method;
148148
actionDefinition.OperationId = (string)action.Property("operationId")?.Value ?? actionDefinition.Target.OperationName;
149149
actionDefinition.Description = (string)action.Property("description")?.Value;
150-
actionDefinition.ChildRoute = childRoute;
150+
actionDefinition.ChildRoute = childRouteProperty.ToToken(childRoute, filePath);
151151
actionDefinition.RequestBody = requestBody;
152152

153153
if (TryReadFileResponse(action, out ActionFileResponse fileResponse, out IJsonLineInfo fileResponseLocation))
@@ -170,7 +170,7 @@ private void ReadControllerAction(string filePath, ControllerDefinition controll
170170
.Append(' ')
171171
.Append(controller.Name);
172172

173-
if (!String.IsNullOrEmpty(actionDefinition.ChildRoute))
173+
if (actionDefinition.ChildRoute != null)
174174
sb.Append('/').Append(actionDefinition.ChildRoute);
175175

176176
IJsonLineInfo lineInfo = action;
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
namespace Dibix.Sdk.CodeGeneration
2+
{
3+
public sealed class Token<T>
4+
{
5+
public T Value { get; }
6+
public string Source { get; }
7+
public int Line { get; }
8+
public int Column { get; }
9+
10+
public Token(T value, string source, int line, int column)
11+
{
12+
this.Value = value;
13+
this.Source = source;
14+
this.Line = line;
15+
this.Column = column;
16+
}
17+
18+
public static implicit operator T(Token<T> token) => token != null ? token.Value : default;
19+
20+
public override string ToString() => $"{this.Value} at ({this.Line}, {this.Column})";
21+
}
22+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
using Dibix.Sdk.Json;
2+
using Newtonsoft.Json;
3+
using Newtonsoft.Json.Linq;
4+
5+
namespace Dibix.Sdk.CodeGeneration
6+
{
7+
internal static class TokenExtensions
8+
{
9+
public static Token<T> ToToken<T>(this JToken json, T value, string source)
10+
{
11+
if (json == null)
12+
return null;
13+
14+
IJsonLineInfo lineInfo = json.GetLineInfo();
15+
return new Token<T>(value, source, lineInfo.LineNumber, lineInfo.LinePosition);
16+
}
17+
}
18+
}

src/Dibix.Sdk/CodeGeneration/Output/ApiDescriptionWriter.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -96,8 +96,8 @@ private string WriteBody(CodeGenerationContext context, IList<ControllerDefiniti
9696
if (!String.IsNullOrEmpty(action.Description))
9797
writer.WriteLine($"y.Description = \"{action.Description}\";");
9898

99-
if (!String.IsNullOrEmpty(action.ChildRoute))
100-
writer.WriteLine($"y.ChildRoute = \"{action.ChildRoute}\";");
99+
if (action.ChildRoute != null)
100+
writer.WriteLine($"y.ChildRoute = \"{action.ChildRoute.Value}\";");
101101

102102
// TODO: Involves a breaking change
103103
//if (action.RequestBody != null)

src/Dibix.Sdk/CodeGeneration/Output/CodeGenerationTask.cs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -144,8 +144,9 @@ string projectName
144144

145145
ICodeGenerationModelValidator modelValidator = new CompositeCodeGenerationModelValidator
146146
(
147-
new ContractArtifactModelValidator(logger)
148-
, new ActionParameterPropertySourceModelValidator(actionParameterSourceRegistry, schemaRegistry, logger)
147+
new ActionParameterPropertySourceModelValidator(actionParameterSourceRegistry, schemaRegistry, logger)
148+
, new ContractArtifactModelValidator(logger)
149+
, new EndpointModelValidator(logger)
149150
, new UserDefinedTypeParameterModelValidator(schemaRegistry, logger)
150151
);
151152

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
using System;
2+
using System.Collections.Generic;
3+
4+
namespace Dibix.Sdk.CodeGeneration
5+
{
6+
internal sealed class EndpointModelValidator : ICodeGenerationModelValidator
7+
{
8+
private static readonly ICollection<string> HttpMethods = new HashSet<string>(Enum.GetNames(typeof(ActionMethod)), StringComparer.OrdinalIgnoreCase);
9+
private readonly ILogger _logger;
10+
11+
public EndpointModelValidator(ILogger logger)
12+
{
13+
this._logger = logger;
14+
}
15+
16+
public bool Validate(CodeGenerationModel model)
17+
{
18+
bool isValid = true;
19+
20+
foreach (ControllerDefinition controller in model.Controllers)
21+
{
22+
foreach (ActionDefinition action in controller.Actions)
23+
{
24+
if (!this.ValidateAction(action))
25+
isValid = false;
26+
}
27+
}
28+
29+
return isValid;
30+
}
31+
32+
private bool ValidateAction(ActionDefinition action)
33+
{
34+
if (action.ChildRoute == null)
35+
return true;
36+
37+
bool result = true;
38+
39+
string[] segments = action.ChildRoute.Value.Split('/');
40+
41+
int columnOffset = 0;
42+
foreach (string segment in segments)
43+
{
44+
if (HttpMethods.Contains(segment))
45+
{
46+
this._logger.LogError($"The path segment '{segment}' is a known HTTP verb, which should be indicated by the action method and is therefore redundant: {action.ChildRoute.Value}", action.ChildRoute.Source, action.ChildRoute.Line, columnOffset);
47+
result = false;
48+
}
49+
columnOffset += segment.Length + 1; // Skip segment and slash
50+
}
51+
52+
return result;
53+
}
54+
}
55+
}

tests/Dibix.Sdk.Tests.Database/Dibix.Sdk.Tests.Database.sqlproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -170,7 +170,7 @@
170170
<None Include="Endpoints\GenericEndpoint.json" />
171171
<None Include="Contracts\InputContract.json" />
172172
<None Include="Endpoints\GenericEndpointWithOutputParam.json" />
173-
<None Include="Endpoints\GenericEndpointWithInvalidSource.json" />
173+
<None Include="Endpoints\GenericEndpointWithErrors.json" />
174174
<None Include="Contracts\Request.json" />
175175
<None Include="Contracts\Entry.json" />
176176
<None Include="Contracts\AnotherInputContract.json" />

tests/Dibix.Sdk.Tests.Database/Endpoints/GenericEndpointWithInvalidSource.json renamed to tests/Dibix.Sdk.Tests.Database/Endpoints/GenericEndpointWithErrors.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
"method": "POST",
1414
"target": "EmptyWithParams",
1515
"body": "Request",
16+
"childRoute": "this/get/is/wrong",
1617
"params": {
1718
"v": "BODY.X",
1819
"w": "BODY.Id.Nm",

tests/Dibix.Sdk.Tests/CodeGeneration/CodeGenerationTaskTests.cs

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -533,14 +533,15 @@ public void Endpoint_WithInvalidPropertySource_Error()
533533
, @"Contracts\Entry.json"
534534
, @"Contracts\Request.json"
535535
}
536-
, endpoints: new [] { @"Endpoints\GenericEndpointWithInvalidSource.json" }
536+
, endpoints: new [] { @"Endpoints\GenericEndpointWithErrors.json" }
537537
, isEmbedded: false
538538
, expectedException: @"One or more errors occured during code generation:
539-
Endpoints\GenericEndpointWithInvalidSource.json(8,15,8,15):error:Unknown property source 'WTF'
540-
Endpoints\GenericEndpointWithInvalidSource.json(9,19,9,19):error:Source 'ENV' does not support property 'MachinePassword'
541-
Endpoints\GenericEndpointWithInvalidSource.json(17,20,17,20):error:Property 'X' not found on contract 'Dibix.Sdk.Tests.DomainModel.Request'
542-
Endpoints\GenericEndpointWithInvalidSource.json(18,23,18,23):error:Property 'Nm' not found on contract 'Dibix.Sdk.Tests.DomainModel.Entry'
543-
Endpoints\GenericEndpointWithInvalidSource.json(23,27,23,27):error:Property 'Nm' not found on contract 'Dibix.Sdk.Tests.DomainModel.Entry'"
539+
Endpoints\GenericEndpointWithErrors.json(8,15,8,15):error:Unknown property source 'WTF'
540+
Endpoints\GenericEndpointWithErrors.json(9,19,9,19):error:Source 'ENV' does not support property 'MachinePassword'
541+
Endpoints\GenericEndpointWithErrors.json(18,20,18,20):error:Property 'X' not found on contract 'Dibix.Sdk.Tests.DomainModel.Request'
542+
Endpoints\GenericEndpointWithErrors.json(19,23,19,23):error:Property 'Nm' not found on contract 'Dibix.Sdk.Tests.DomainModel.Entry'
543+
Endpoints\GenericEndpointWithErrors.json(24,27,24,27):error:Property 'Nm' not found on contract 'Dibix.Sdk.Tests.DomainModel.Entry'
544+
Endpoints\GenericEndpointWithErrors.json(16,5,16,5):error:The path segment 'get' is a known HTTP verb, which should be indicated by the action method and is therefore redundant: this/get/is/wrong"
544545
);
545546
}
546547

@@ -573,4 +574,4 @@ public void InvalidContractSchema_Error()
573574
);
574575
}
575576
}
576-
}
577+
}

0 commit comments

Comments
 (0)