Skip to content

Commit 0dd9445

Browse files
gavinbarronCopilot
andauthored
feat: add int32 format configuration for pagination parameters and count responses (#793)
Add two new boolean settings to OpenApiConvertSettings: - UseInt32ForPaginationParameters: when true, generates $top and $skip parameters with int32 format instead of the default int64 - UseInt32ForCountResponses: when true, generates $count response schema and @odata.count properties with int32 format instead of int64 Both settings default to false, preserving existing behavior. They are configured via the HIDI settings config file under OpenApiConvertSettings. Closes #792 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent 4123e85 commit 0dd9445

8 files changed

Lines changed: 104 additions & 12 deletions

File tree

src/Microsoft.OpenApi.OData.Reader/Generator/OpenApiParameterGenerator.cs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -34,8 +34,8 @@ public static void AddParametersToDocument(this ODataContext context, OpenApiDoc
3434

3535
// It allows defining query options and headers that can be reused across operations of the service.
3636
// The value of parameters is a map of Parameter Objects.
37-
document.AddComponent("top", CreateTop(context.Settings.TopExample));
38-
document.AddComponent("skip", CreateSkip());
37+
document.AddComponent("top", CreateTop(context.Settings.TopExample, context.Settings.UseInt32ForPaginationParameters));
38+
document.AddComponent("skip", CreateSkip(context.Settings.UseInt32ForPaginationParameters));
3939
document.AddComponent("count", CreateCount());
4040
document.AddComponent("filter", CreateFilter());
4141
document.AddComponent("search", CreateSearch());
@@ -886,7 +886,7 @@ public static void AppendParameter(this IList<IOpenApiParameter> parameters, IOp
886886
}
887887

888888
// #top
889-
private static OpenApiParameter CreateTop(int topExample)
889+
private static OpenApiParameter CreateTop(int topExample, bool useInt32Format = false)
890890
{
891891
return new OpenApiParameter
892892
{
@@ -896,7 +896,7 @@ private static OpenApiParameter CreateTop(int topExample)
896896
Schema = new OpenApiSchema
897897
{
898898
Type = JsonSchemaType.Number,
899-
Format = "int64",
899+
Format = useInt32Format ? "int32" : "int64",
900900
Minimum = "0",
901901
},
902902
Example = topExample,
@@ -906,7 +906,7 @@ private static OpenApiParameter CreateTop(int topExample)
906906
}
907907

908908
// $skip
909-
private static OpenApiParameter CreateSkip()
909+
private static OpenApiParameter CreateSkip(bool useInt32Format = false)
910910
{
911911
return new OpenApiParameter
912912
{
@@ -916,7 +916,7 @@ private static OpenApiParameter CreateSkip()
916916
Schema = new OpenApiSchema
917917
{
918918
Type = JsonSchemaType.Number,
919-
Format = "int64",
919+
Format = useInt32Format ? "int32" : "int64",
920920
Minimum = "0",
921921
},
922922
Style = ParameterStyle.Form,

src/Microsoft.OpenApi.OData.Reader/Generator/OpenApiResponseGenerator.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -195,7 +195,8 @@ public static OpenApiResponses CreateResponses(this ODataContext context, IEdmOp
195195
}
196196
if (context.Settings.EnableCount)
197197
{
198-
baseSchema.Properties.Add(ODataConstants.OdataCount.Key, ODataConstants.OdataCount.Value);
198+
var odataCount = ODataConstants.CreateOdataCount(context.Settings.UseInt32ForCountResponses);
199+
baseSchema.Properties.Add(odataCount.Key, odataCount.Value);
199200
}
200201
schema = baseSchema;
201202
}

src/Microsoft.OpenApi.OData.Reader/Generator/OpenApiSchemaGenerator.cs

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ public static void AddSchemasToDocument(this ODataContext context, OpenApiDocume
4848
if(context.Settings.EnableDollarCountPath)
4949
document.AddComponent(Constants.DollarCountSchemaName, new OpenApiSchema {
5050
Type = JsonSchemaType.Number,
51-
Format = "int64"
51+
Format = context.Settings.UseInt32ForCountResponses ? "int32" : "int64"
5252
});
5353

5454
if(context.HasAnyNonContainedCollections())
@@ -101,7 +101,10 @@ public static void AddSchemasToDocument(this ODataContext context, OpenApiDocume
101101

102102
responseSchema.Properties ??= new Dictionary<string, IOpenApiSchema>();
103103
if (context.Settings.EnableCount)
104-
responseSchema.Properties.Add(ODataConstants.OdataCount.Key, ODataConstants.OdataCount.Value);
104+
{
105+
var odataCount = ODataConstants.CreateOdataCount(context.Settings.UseInt32ForCountResponses);
106+
responseSchema.Properties.Add(odataCount.Key, odataCount.Value);
107+
}
105108
if (context.Settings.EnablePagination)
106109
responseSchema.Properties.Add(ODataConstants.OdataNextLink.Key, ODataConstants.OdataNextLink.Value);
107110
}
@@ -261,10 +264,13 @@ private static OpenApiSchema CreateCollectionSchema(ODataContext context, IOpenA
261264
baseSchema.Properties.Add(ODataConstants.OdataNextLink.Key, ODataConstants.OdataNextLink.Value);
262265

263266
if (context.Settings.EnableCount)
264-
baseSchema.Properties.Add(ODataConstants.OdataCount.Key, ODataConstants.OdataCount.Value);
267+
{
268+
var odataCount = ODataConstants.CreateOdataCount(context.Settings.UseInt32ForCountResponses);
269+
baseSchema.Properties.Add(odataCount.Key, odataCount.Value);
270+
}
265271

266272
collectionSchema = baseSchema;
267-
}
273+
}
268274
}
269275
else
270276
{

src/Microsoft.OpenApi.OData.Reader/OData/ODataConstants.cs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
// Licensed under the MIT License (MIT). See LICENSE in the repo root for license information.
44
// ------------------------------------------------------------
55

6+
using System;
67
using System.Collections.Generic;
78

89
namespace Microsoft.OpenApi.OData
@@ -27,8 +28,21 @@ internal static class ODataConstants
2728
/// <summary>
2829
/// @odata.count KeyValue pair
2930
/// </summary>
31+
[Obsolete("Use CreateOdataCount instead to specify the format of the count value.")]
3032
public readonly static KeyValuePair<string, IOpenApiSchema> OdataCount = new("@odata.count", new OpenApiSchema { Type = JsonSchemaType.Number | JsonSchemaType.Null, Format = "int64"});
3133

34+
/// <summary>
35+
/// Creates an @odata.count KeyValue pair with the specified format.
36+
/// </summary>
37+
public static KeyValuePair<string, IOpenApiSchema> CreateOdataCount(bool useInt32Format)
38+
{
39+
return new("@odata.count", new OpenApiSchema
40+
{
41+
Type = JsonSchemaType.Number | JsonSchemaType.Null,
42+
Format = useInt32Format ? "int32" : "int64"
43+
});
44+
}
45+
3246
/// <summary>
3347
/// @odata.deltaLink KeyValue pair
3448
/// </summary>

src/Microsoft.OpenApi.OData.Reader/OpenApiConvertSettings.cs

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -337,6 +337,16 @@ public string? PathPrefix
337337
/// </summary>
338338
public int ComposableFunctionsExpansionDepth { get; set; } = 1;
339339

340+
/// <summary>
341+
/// Gets/Sets a value indicating whether common OData number parameters ($top, $skip) should use int32 format instead of int64.
342+
/// </summary>
343+
public bool UseInt32ForPaginationParameters { get; set; } = false;
344+
345+
/// <summary>
346+
/// Gets/Sets a value indicating whether the $count response schema should use int32 format instead of int64.
347+
/// </summary>
348+
public bool UseInt32ForCountResponses { get; set; } = false;
349+
340350
internal OpenApiConvertSettings Clone()
341351
{
342352
var newSettings = new OpenApiConvertSettings
@@ -392,7 +402,9 @@ internal OpenApiConvertSettings Clone()
392402
SemVerVersion = this.SemVerVersion,
393403
EnableAliasForOperationSegments = this.EnableAliasForOperationSegments,
394404
UseStringArrayForQueryOptionsSchema = this.UseStringArrayForQueryOptionsSchema,
395-
ComposableFunctionsExpansionDepth = this.ComposableFunctionsExpansionDepth
405+
ComposableFunctionsExpansionDepth = this.ComposableFunctionsExpansionDepth,
406+
UseInt32ForPaginationParameters = this.UseInt32ForPaginationParameters,
407+
UseInt32ForCountResponses = this.UseInt32ForCountResponses
396408
};
397409

398410
return newSettings;

src/Microsoft.OpenApi.OData.Reader/PublicAPI.Unshipped.txt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -201,6 +201,10 @@ Microsoft.OpenApi.OData.OpenApiConvertSettings.RoutePathPrefixProvider.get -> Mi
201201
Microsoft.OpenApi.OData.OpenApiConvertSettings.RoutePathPrefixProvider.set -> void
202202
Microsoft.OpenApi.OData.OpenApiConvertSettings.SemVerVersion.get -> string!
203203
Microsoft.OpenApi.OData.OpenApiConvertSettings.SemVerVersion.set -> void
204+
Microsoft.OpenApi.OData.OpenApiConvertSettings.UseInt32ForPaginationParameters.get -> bool
205+
Microsoft.OpenApi.OData.OpenApiConvertSettings.UseInt32ForPaginationParameters.set -> void
206+
Microsoft.OpenApi.OData.OpenApiConvertSettings.UseInt32ForCountResponses.get -> bool
207+
Microsoft.OpenApi.OData.OpenApiConvertSettings.UseInt32ForCountResponses.set -> void
204208
Microsoft.OpenApi.OData.OpenApiConvertSettings.ServiceRoot.get -> System.Uri!
205209
Microsoft.OpenApi.OData.OpenApiConvertSettings.ServiceRoot.set -> void
206210
Microsoft.OpenApi.OData.OpenApiConvertSettings.ShowExternalDocs.get -> bool

test/Microsoft.OpenAPI.OData.Reader.Tests/Generator/OpenApiParameterGeneratorTests.cs

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -655,5 +655,35 @@ public static IEdmModel GetEdmModel()
655655
Assert.True(result);
656656
return model;
657657
}
658+
659+
[Theory]
660+
[InlineData(true, "int32")]
661+
[InlineData(false, "int64")]
662+
public async Task CreateParametersRespectsUseInt32ForPaginationParameters(bool useInt32, string expectedFormat)
663+
{
664+
// Arrange
665+
IEdmModel model = EdmCoreModel.Instance;
666+
OpenApiConvertSettings settings = new()
667+
{
668+
UseInt32ForPaginationParameters = useInt32
669+
};
670+
ODataContext context = new ODataContext(model, settings);
671+
OpenApiDocument openApiDocument = new();
672+
673+
// Act
674+
context.AddParametersToDocument(openApiDocument);
675+
var parameters = openApiDocument.Components.Parameters;
676+
677+
// Assert
678+
Assert.NotNull(parameters);
679+
var topParam = parameters.First(p => p.Key == "top").Value;
680+
var skipParam = parameters.First(p => p.Key == "skip").Value;
681+
682+
var topJson = JsonNode.Parse(await topParam.SerializeAsJsonAsync(OpenApiSpecVersion.OpenApi3_0));
683+
var skipJson = JsonNode.Parse(await skipParam.SerializeAsJsonAsync(OpenApiSpecVersion.OpenApi3_0));
684+
685+
Assert.Equal(expectedFormat, topJson["schema"]["format"]?.GetValue<string>());
686+
Assert.Equal(expectedFormat, skipJson["schema"]["format"]?.GetValue<string>());
687+
}
658688
}
659689
}

test/Microsoft.OpenAPI.OData.Reader.Tests/Generator/OpenApiSchemaGeneratorTests.cs

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1161,5 +1161,30 @@ public async Task NonNullableUntypedPropertyWorks()
11611161

11621162
Assert.Equal("{ }", json);
11631163
}
1164+
1165+
[Theory]
1166+
[InlineData(true, "int32")]
1167+
[InlineData(false, "int64")]
1168+
public void DollarCountSchemaRespectsUseInt32ForCountResponses(bool useInt32, string expectedFormat)
1169+
{
1170+
// Arrange
1171+
IEdmModel model = EdmModelHelper.TripServiceModel;
1172+
OpenApiDocument openApiDocument = new();
1173+
OpenApiConvertSettings settings = new()
1174+
{
1175+
EnableDollarCountPath = true,
1176+
UseInt32ForCountResponses = useInt32
1177+
};
1178+
ODataContext context = new(model, settings);
1179+
1180+
// Act
1181+
context.AddSchemasToDocument(openApiDocument);
1182+
1183+
// Assert
1184+
Assert.True(openApiDocument.Components.Schemas.TryGetValue(Constants.DollarCountSchemaName, out var countSchema));
1185+
Assert.NotNull(countSchema);
1186+
Assert.Equal(JsonSchemaType.Number, countSchema.Type);
1187+
Assert.Equal(expectedFormat, countSchema.Format);
1188+
}
11641189
}
11651190
}

0 commit comments

Comments
 (0)