Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Generate odata.nextLink and odata.deltaLink properties on delta functions response schemas #290

Merged
5 changes: 5 additions & 0 deletions src/Microsoft.OpenApi.OData.Reader/Common/Constants.cs
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,11 @@ internal static class Constants
/// </summary>
public static string BaseCollectionPaginationCountResponse = "BaseCollectionPaginationCountResponse";

/// <summary>
/// Suffix used for the base delta function response schemas.
/// </summary>
public static string BaseDeltaFunctionResponse = "BaseDeltaFunctionResponse";

/// <summary>
/// Name used for reference update.
/// </summary>
Expand Down
22 changes: 22 additions & 0 deletions src/Microsoft.OpenApi.OData.Reader/Edm/EdmOperationExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
using Microsoft.OData.Edm;
millicentachieng marked this conversation as resolved.
Show resolved Hide resolved
baywet marked this conversation as resolved.
Show resolved Hide resolved

namespace Microsoft.OpenApi.OData.Edm
{
/// <summary>
/// Extension methods for <see cref="IEdmOperation"/>
/// </summary>
internal static class EdmOperationExtensions
{
/// <summary>
/// Checks whether the EDM is a delta function
millicentachieng marked this conversation as resolved.
Show resolved Hide resolved
/// </summary>
/// <param name="operation">The EDM operation.</param>
/// <returns></returns>
millicentachieng marked this conversation as resolved.
Show resolved Hide resolved
public static bool IsDeltaFunction(this IEdmOperation operation)
{
if (operation.IsFunction() && operation.Name == "delta")
millicentachieng marked this conversation as resolved.
Show resolved Hide resolved
baywet marked this conversation as resolved.
Show resolved Hide resolved
return true;
return false;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ namespace Microsoft.OpenApi.OData.Edm
public static class EdmTypeExtensions
{
/// <summary>
/// Determines wether a path parameter should be wrapped in quotes based on the type of the parameter.
/// Determines whether a path parameter should be wrapped in quotes based on the type of the parameter.
/// </summary>
/// <param name="edmType">The type of the parameter.</param>
/// <param name="settings">The conversion settings.</param>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -191,18 +191,60 @@ public static OpenApiResponse CreateOperationResponse(this ODataContext context,
OpenApiSchema schema;
if (operation.ReturnType.IsCollection())
{
schema = new OpenApiSchema
OpenApiSchema baseSchema = new()
{
Title = operation.ReturnType.Definition.AsElementType() is not IEdmEntityType entityType
? null : $"Collection of {entityType.Name}",
Type = "object",
Type = Constants.ObjectType,
Properties = new Dictionary<string, OpenApiSchema>
{
{
"value", context.CreateEdmTypeSchema(operation.ReturnType)
}
}
};

if (context.Settings.EnableODataAnnotationReferencesForResponses &&
(operation.IsDeltaFunction() || context.Settings.EnablePagination || context.Settings.EnableCount))
{
schema = new OpenApiSchema
{
AllOf = new List<OpenApiSchema>
{
new OpenApiSchema
{
UnresolvedReference = true,
Reference = new OpenApiReference
{
Type = ReferenceType.Schema,
Id = operation.IsDeltaFunction() ? Constants.BaseDeltaFunctionResponse // @odata.nextLink + @odata.deltaLink
: Constants.BaseCollectionPaginationCountResponse // @odata.nextLink + @odata.count
}
},
baseSchema
}
};
}
else if (operation.IsDeltaFunction())
{
baseSchema.Properties.Add(ODataConstants.OdataNextLink);
baseSchema.Properties.Add(ODataConstants.OdataDeltaLink);
schema = baseSchema;
}
else
{
if (context.Settings.EnablePagination)
{
baseSchema.Properties.Add(ODataConstants.OdataNextLink);
}
if (context.Settings.EnableCount)
{
baseSchema.Properties.Add(ODataConstants.OdataCount);
}
schema = baseSchema;
}

schema.Title = operation.ReturnType.Definition.AsElementType() is not IEdmEntityType entityType
? null : $"Collection of {entityType.Name}";
schema.Type = "object";
}
else if (operation.ReturnType.IsPrimitive())
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -129,20 +129,34 @@ public static IDictionary<string, OpenApiSchema> CreateSchemas(this ODataContext
}
};

// @odata.nextLink + @odata.count
if ((context.Settings.EnablePagination || context.Settings.EnableCount) &&
context.Settings.RefBaseCollectionPaginationCountResponse)
if (context.Settings.EnableODataAnnotationReferencesForResponses)
{
schemas[Constants.BaseCollectionPaginationCountResponse] = new()
// @odata.nextLink + @odata.count
if (context.Settings.EnablePagination || context.Settings.EnableCount)
{
Title = "Base collection pagination and count responses",
Type = Constants.ObjectType,
};
schemas[Constants.BaseCollectionPaginationCountResponse] = new()
{
Title = "Base collection pagination and count responses",
Type = Constants.ObjectType,
};

if (context.Settings.EnableCount)
schemas[Constants.BaseCollectionPaginationCountResponse].Properties.Add(ODataConstants.OdataCount);
if (context.Settings.EnablePagination)
schemas[Constants.BaseCollectionPaginationCountResponse].Properties.Add(ODataConstants.OdataNextLink);
if (context.Settings.EnableCount)
schemas[Constants.BaseCollectionPaginationCountResponse].Properties.Add(ODataConstants.OdataCount);
if (context.Settings.EnablePagination)
schemas[Constants.BaseCollectionPaginationCountResponse].Properties.Add(ODataConstants.OdataNextLink);
}

// @odata.nextLink + @odata.deltaLink
if (context.Model.SchemaElements.OfType<IEdmFunction>().Any(x => x.IsDeltaFunction()))
baywet marked this conversation as resolved.
Show resolved Hide resolved
{
schemas[Constants.BaseDeltaFunctionResponse] = new()
{
Title = "Base delta function response",
Type = Constants.ObjectType
};
schemas[Constants.BaseDeltaFunctionResponse].Properties.Add(ODataConstants.OdataNextLink);
schemas[Constants.BaseDeltaFunctionResponse].Properties.Add(ODataConstants.OdataDeltaLink);
}
}

return schemas;
Expand Down Expand Up @@ -232,10 +246,10 @@ private static OpenApiSchema CreateCollectionSchema(ODataContext context, OpenAp
Properties = properties
};

OpenApiSchema colSchema;
OpenApiSchema collectionSchema;
if (context.Settings.EnablePagination || context.Settings.EnableCount)
{
if (context.Settings.RefBaseCollectionPaginationCountResponse)
if (context.Settings.EnableODataAnnotationReferencesForResponses)
{
// @odata.nextLink + @odata.count
OpenApiSchema paginationCountSchema = new()
Expand All @@ -248,7 +262,7 @@ private static OpenApiSchema CreateCollectionSchema(ODataContext context, OpenAp
}
};

colSchema = new OpenApiSchema
collectionSchema = new OpenApiSchema
{
AllOf = new List<OpenApiSchema>
{
Expand All @@ -265,17 +279,17 @@ private static OpenApiSchema CreateCollectionSchema(ODataContext context, OpenAp
if (context.Settings.EnableCount)
baseSchema.Properties.Add(ODataConstants.OdataCount);

colSchema = baseSchema;
collectionSchema = baseSchema;
}
}
else
{
colSchema = baseSchema;
collectionSchema = baseSchema;
}

colSchema.Title = $"Collection of {typeName}";
colSchema.Type = Constants.ObjectType;
return colSchema;
collectionSchema.Title = $"Collection of {typeName}";
collectionSchema.Type = Constants.ObjectType;
return collectionSchema;
}

/// <summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
<TargetFrameworks>netstandard2.0</TargetFrameworks>
<PackageId>Microsoft.OpenApi.OData</PackageId>
<SignAssembly>true</SignAssembly>
<Version>1.2.0-preview3</Version>
<Version>1.2.0-preview4</Version>
<Description>This package contains the codes you need to convert OData CSDL to Open API Document of Model.</Description>
<Copyright>© Microsoft Corporation. All rights reserved.</Copyright>
<PackageTags>Microsoft OpenApi OData EDM</PackageTags>
Expand All @@ -26,6 +26,7 @@
- Set assembly version used for conversion in OpenApiInfo object #208
- Add support for alternate keys parameters #120
- Adds create and update operations to non-containment navigation properties when restrictions annotations are explicitly set to true #265
- Generate odata.nextLink and odata.deltaLink properties on delta functions response schemas #285
</PackageReleaseNotes>
<AssemblyName>Microsoft.OpenApi.OData.Reader</AssemblyName>
millicentachieng marked this conversation as resolved.
Show resolved Hide resolved
<AssemblyOriginatorKeyFile>..\..\tool\Microsoft.OpenApi.OData.snk</AssemblyOriginatorKeyFile>
Expand Down
6 changes: 5 additions & 1 deletion src/Microsoft.OpenApi.OData.Reader/OData/ODataConstants.cs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,10 @@ internal static class ODataConstants
/// @odata.count KeyValue pair
/// </summary>
public static KeyValuePair<string, OpenApiSchema> OdataCount = new("@odata.count", new OpenApiSchema { Type = "integer", Format = "int64", Nullable = true });


/// <summary>
/// @odata.deltaLink KeyValue pair
/// </summary>
public static KeyValuePair<string, OpenApiSchema> OdataDeltaLink = new("@odata.deltaLink", new OpenApiSchema { Type = Constants.StringType, Nullable = true });
}
}
14 changes: 14 additions & 0 deletions src/Microsoft.OpenApi.OData.Reader/OpenApiConvertSettings.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ namespace Microsoft.OpenApi.OData
/// </summary>
public class OpenApiConvertSettings
{
private bool _enableODataAnnotationReferencesForResponses = true;

/// <summary>
/// Gets/sets the service root.
/// </summary>
Expand Down Expand Up @@ -108,8 +110,19 @@ public class OpenApiConvertSettings
/// <summary>
/// Gets/sets a value indicating whether or not to reference @odata.nextLink and @odata.count in responses
/// </summary>
[Obsolete("Deprecated in favor of EnableODataAnnotationReferencesForResponses. " +
"When both are provided, EnableODataAnnotationReferencesForResponses takes precedence.")]
public bool RefBaseCollectionPaginationCountResponse { get; set; } = true;

/// <summary>
/// Gets/sets a value indicating whether or not to reference @odata.nextLink, @odata.deltaLink and @odata.count in responses
/// </summary>
public bool EnableODataAnnotationReferencesForResponses
{
get => _enableODataAnnotationReferencesForResponses;
millicentachieng marked this conversation as resolved.
Show resolved Hide resolved
set => _enableODataAnnotationReferencesForResponses = value && this.RefBaseCollectionPaginationCountResponse;
}

/// <summary>
/// Gets/sets a value that specifies the name of the operation for retrieving the next page in a collection of entities.
/// </summary>
Expand Down Expand Up @@ -342,6 +355,7 @@ internal OpenApiConvertSettings Clone()
UseSuccessStatusCodeRange = this.UseSuccessStatusCodeRange,
EnableCount = this.EnableCount,
IncludeAssemblyInfo = this.IncludeAssemblyInfo,
EnableODataAnnotationReferencesForResponses = this.EnableODataAnnotationReferencesForResponses,
RefBaseCollectionPaginationCountResponse = this.RefBaseCollectionPaginationCountResponse
millicentachieng marked this conversation as resolved.
Show resolved Hide resolved
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,16 +64,6 @@ protected override void SetRequestBody(OpenApiOperation operation)
protected override void SetExtensions(OpenApiOperation operation)
{
operation.Extensions.Add(Constants.xMsDosOperationType, new OpenApiString("action"));
baywet marked this conversation as resolved.
Show resolved Hide resolved
if (Context.Settings.EnablePagination && EdmOperation.ReturnType?.TypeKind() == EdmTypeKind.Collection)
{
OpenApiObject extension = new OpenApiObject
{
{ "nextLinkName", new OpenApiString("@odata.nextLink")},
{ "operationName", new OpenApiString(Context.Settings.PageableOperationName)}
};

operation.Extensions.Add(Constants.xMsPageable, extension);
}
base.SetExtensions(operation);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,16 +42,6 @@ protected override void SetBasicInfo(OpenApiOperation operation)
protected override void SetExtensions(OpenApiOperation operation)
{
operation.Extensions.Add(Constants.xMsDosOperationType, new OpenApiString("function"));
if (Context.Settings.EnablePagination && EdmOperation.ReturnType?.TypeKind() == EdmTypeKind.Collection)
baywet marked this conversation as resolved.
Show resolved Hide resolved
{
OpenApiObject extension = new OpenApiObject
{
{ "nextLinkName", new OpenApiString("@odata.nextLink")},
{ "operationName", new OpenApiString(Context.Settings.PageableOperationName)}
};

operation.Extensions.Add(Constants.xMsPageable, extension);
}
base.SetExtensions(operation);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
using System.Collections.Generic;
using System.Linq;
using Microsoft.OData.Edm;
using Microsoft.OData.Edm.Vocabularies;
using Microsoft.OpenApi.Any;
using Microsoft.OpenApi.Models;
using Microsoft.OpenApi.OData.Common;
Expand Down Expand Up @@ -165,7 +164,7 @@ protected override void SetParameters(OpenApiOperation operation)
}

/// <inheritdoc/>
protected override void SetResponses(OpenApiOperation operation)
protected override void SetResponses(OpenApiOperation operation)
{
operation.Responses = Context.CreateResponses(EdmOperation);
base.SetResponses(operation);
Expand Down Expand Up @@ -277,5 +276,21 @@ protected override void SetExternalDocs(OpenApiOperation operation)
};
}
}

// <inheritdoc/>
protected override void SetExtensions(OpenApiOperation operation)
{
if (Context.Settings.EnablePagination && EdmOperation.ReturnType?.TypeKind() == EdmTypeKind.Collection)
{
OpenApiObject extension = new OpenApiObject
{
{ "nextLinkName", new OpenApiString("@odata.nextLink")},
{ "operationName", new OpenApiString(Context.Settings.PageableOperationName)}
};

operation.Extensions.Add(Constants.xMsPageable, extension);
}
base.SetExtensions(operation);
}
}
}
2 changes: 2 additions & 0 deletions src/Microsoft.OpenApi.OData.Reader/PublicAPI.Unshipped.txt
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ Microsoft.OpenApi.OData.OpenApiConvertSettings.CustomHttpMethodLinkRelMapping.ge
Microsoft.OpenApi.OData.OpenApiConvertSettings.CustomHttpMethodLinkRelMapping.set -> void
Microsoft.OpenApi.OData.OpenApiConvertSettings.EnableCount.get -> bool
Microsoft.OpenApi.OData.OpenApiConvertSettings.EnableCount.set -> void
Microsoft.OpenApi.OData.OpenApiConvertSettings.EnableODataAnnotationReferencesForResponses.get -> bool
Microsoft.OpenApi.OData.OpenApiConvertSettings.EnableODataAnnotationReferencesForResponses.set -> void
Microsoft.OpenApi.OData.OpenApiConvertSettings.ExpandDerivedTypesNavigationProperties.get -> bool
Microsoft.OpenApi.OData.OpenApiConvertSettings.ExpandDerivedTypesNavigationProperties.set -> void
Microsoft.OpenApi.OData.OpenApiConvertSettings.CustomXMLAttributesMapping.get -> System.Collections.Generic.Dictionary<string, string>
Expand Down
Loading