Skip to content

Commit

Permalink
[Fix] Aliases or strips namespace prefixes from segment names when an…
Browse files Browse the repository at this point in the history
…d where applicable (#369)

* Refactor striping and aliasing of namespace prefixes

* Adds new convert setting

* Add function that strips or aliases namespace prefix

* Alias or strip namespace prefixes from operation segments

* Strip or alias namespace prefix for type cast paths

* Update public API doc.

* Update unit test

* Strip or aliases namespace prefix from type cast segments

* Update public API doc.

* Update unit test

* Update release notes

* Update operation segment tests

* Apply suggestions from code review

* - fixes review correction

* Strip namespace prefix independently of aliasing

* PR review suggestions

---------

Co-authored-by: Vincent Biret <vibiret@microsoft.com>
  • Loading branch information
irvinesunday and baywet authored Apr 13, 2023
1 parent ef33618 commit f9e45d0
Show file tree
Hide file tree
Showing 8 changed files with 162 additions and 57 deletions.
50 changes: 50 additions & 0 deletions src/Microsoft.OpenApi.OData.Reader/Common/EdmModelHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime;
using Microsoft.OData.Edm;
using Microsoft.OData.Edm.Csdl;
using Microsoft.OpenApi.Models;
using Microsoft.OpenApi.OData.Edm;
using Microsoft.OpenApi.OData.Vocabulary.Capabilities;
Expand Down Expand Up @@ -341,5 +343,53 @@ internal static string GenerateODataTypeCastPathOperationIdPrefix(ODataPath path

return operationId;
}

/// <summary>
/// Strips or aliases namespace prefixes from an element name.
/// </summary>
/// <param name="element">The target element.</param>
/// <param name="model">Optional: The Edm model. Used for searching for the namespace alias.</param>
/// <param name="settings">The OpenAPI convert settings.</param>
/// <returns>The element name, alias-prefixed or namespace-stripped if applicable.</returns>
internal static string StripOrAliasNamespacePrefix(IEdmSchemaElement element, OpenApiConvertSettings settings, IEdmModel model = null)
{
Utils.CheckArgumentNull(element, nameof(element));
Utils.CheckArgumentNull(settings, nameof(settings));

string namespaceAlias = string.Empty;
string namespaceName = element.Namespace;
string segmentName = element.FullName();

if (!string.IsNullOrEmpty(namespaceName) && model != null)
{
namespaceAlias = model.GetNamespaceAlias(namespaceName);
}

if (element is IEdmStructuredType && settings.EnableAliasForTypeCastSegments && !string.IsNullOrEmpty(namespaceAlias))
{
// Alias type cast segment name
segmentName = namespaceAlias.TrimEnd('.') + "." + element.Name;
}

if (element is IEdmOperation)
{
if (settings.EnableAliasForOperationSegments && !string.IsNullOrEmpty(namespaceAlias))
{
// Alias operation segment name
segmentName = namespaceAlias.TrimEnd('.') + "." + element.Name;
}

if (!string.IsNullOrEmpty(settings.NamespacePrefixToStripForInMethodPaths) &&
element.Namespace.Equals(settings.NamespacePrefixToStripForInMethodPaths, StringComparison.OrdinalIgnoreCase))
{
// Strip specified namespace from operation segment name.
// If the namespace prefix to strip matches the namespace name,
// and the alias has been appended, the alias will be stripped.
segmentName = element.Name;
}
}

return segmentName;
}
}
}
39 changes: 33 additions & 6 deletions src/Microsoft.OpenApi.OData.Reader/Edm/ODataOperationSegment.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
// ------------------------------------------------------------
// -----
// private readonly IEdmModel _model;-------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License (MIT). See LICENSE in the repo root for license information.
// ------------------------------------------------------------
Expand Down Expand Up @@ -48,6 +49,31 @@ public ODataOperationSegment(IEdmOperation operation, IDictionary<string, string
ParameterMappings = parameterMappings ?? throw Error.ArgumentNull(nameof(parameterMappings));
}

/// <summary>
/// Initializes a new instance of <see cref="ODataOperationSegment"/> class.
/// </summary>
/// <param name="operation">The operation.</param>
/// <param name="model">The Edm model.</param>
public ODataOperationSegment(IEdmOperation operation, IEdmModel model)
: this(operation, false, model)
{
}

/// <summary>
/// Initializes a new instance of <see cref="ODataOperationSegment"/> class.
/// </summary>
/// <param name="operation">The operation.</param>
/// <param name="isEscapedFunction">A value indicating this operation is an escaped function.</param>
/// <param name="model">The Edm model.</param>
public ODataOperationSegment(IEdmOperation operation, bool isEscapedFunction, IEdmModel model)
{
Operation = operation ?? throw Error.ArgumentNull(nameof(operation));
IsEscapedFunction = isEscapedFunction;
_model = model ?? throw Error.ArgumentNull(nameof(model));
}

private readonly IEdmModel _model;

/// <summary>
/// Gets the parameter mappings.
/// </summary>
Expand Down Expand Up @@ -116,12 +142,13 @@ private string OperationName(IEdmOperation operation, OpenApiConvertSettings set
{
return operation.Name;
}
else if (_model != null)
{
return EdmModelHelper.StripOrAliasNamespacePrefix(operation, settings, _model);
}
else
{
string selectedName = operation.FullName();
return !string.IsNullOrEmpty(settings.NamespacePrefixToStripForInMethodPaths)
? selectedName.StripNamespacePrefix(settings.NamespacePrefixToStripForInMethodPaths)
: selectedName;
return operation.FullName();
}
}

Expand Down Expand Up @@ -167,5 +194,5 @@ public override IEnumerable<IEdmVocabularyAnnotatable> GetAnnotables()
{
return new IEdmVocabularyAnnotatable[] { Operation };
}
}
}
}
12 changes: 6 additions & 6 deletions src/Microsoft.OpenApi.OData.Reader/Edm/ODataPathProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -781,7 +781,7 @@ secondLastPathSegment is not ODataKeySegment &&
if (lastPathSegment is ODataTypeCastSegment && !convertSettings.AppendBoundOperationsOnDerivedTypeCastSegments) continue;
if (lastPathSegment is ODataKeySegment segment && segment.IsAlternateKey) continue;
ODataPath newPath = subPath.Clone();
newPath.Push(new ODataOperationSegment(edmOperation, isEscapedFunction));
newPath.Push(new ODataOperationSegment(edmOperation, isEscapedFunction, _model));
AppendPath(newPath);
}
}
Expand Down Expand Up @@ -828,7 +828,7 @@ private void AppendBoundOperationOnNavigationPropertyPath(IEdmOperation edmOpera
}

ODataPath newPath = path.Clone();
newPath.Push(new ODataOperationSegment(edmOperation, isEscapedFunction));
newPath.Push(new ODataOperationSegment(edmOperation, isEscapedFunction, _model));
AppendPath(newPath);
}
}
Expand Down Expand Up @@ -861,7 +861,7 @@ private void AppendBoundOperationOnDerived(
if (ns is IEdmEntitySet)
{
ODataPath newPath = new ODataPath(new ODataNavigationSourceSegment(ns), new ODataTypeCastSegment(bindingEntityType, _model),
new ODataOperationSegment(edmOperation, isEscapedFunction));
new ODataOperationSegment(edmOperation, isEscapedFunction, _model));
AppendPath(newPath);
}
}
Expand All @@ -870,14 +870,14 @@ private void AppendBoundOperationOnDerived(
if (ns is IEdmSingleton)
{
ODataPath newPath = new ODataPath(new ODataNavigationSourceSegment(ns), new ODataTypeCastSegment(bindingEntityType, _model),
new ODataOperationSegment(edmOperation, isEscapedFunction));
new ODataOperationSegment(edmOperation, isEscapedFunction, _model));
AppendPath(newPath);
}
else
{
ODataPath newPath = new ODataPath(new ODataNavigationSourceSegment(ns), new ODataKeySegment(ns.EntityType()),
new ODataTypeCastSegment(bindingEntityType , _model),
new ODataOperationSegment(edmOperation, isEscapedFunction));
new ODataOperationSegment(edmOperation, isEscapedFunction, _model));
AppendPath(newPath);
}
}
Expand Down Expand Up @@ -957,7 +957,7 @@ private void AppendBoundOperationOnDerivedNavigationPropertyPath(

ODataPath newPath = path.Clone();
newPath.Push(new ODataTypeCastSegment(bindingEntityType, _model));
newPath.Push(new ODataOperationSegment(edmOperation, isEscapedFunction));
newPath.Push(new ODataOperationSegment(edmOperation, isEscapedFunction, _model));
AppendPath(newPath);
}
}
Expand Down
23 changes: 5 additions & 18 deletions src/Microsoft.OpenApi.OData.Reader/Edm/ODataTypeCastSegment.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,8 @@
// Licensed under the MIT License (MIT). See LICENSE in the repo root for license information.
// ------------------------------------------------------------

using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.OData.Edm;
using Microsoft.OData.Edm.Csdl;
using Microsoft.OData.Edm.Vocabularies;
using Microsoft.OpenApi.OData.Common;

Expand Down Expand Up @@ -53,20 +50,10 @@ public override IEnumerable<IEdmVocabularyAnnotatable> GetAnnotables()
public override string GetPathItemName(OpenApiConvertSettings settings, HashSet<string> parameters)
{
Utils.CheckArgumentNull(settings, nameof(settings));
string namespaceName = string.Empty;
string namespaceAlias = string.Empty;


if (StructuredType is IEdmSchemaElement element)
namespaceName = element.Namespace;

if (!string.IsNullOrEmpty(namespaceName))
namespaceAlias = _model.GetNamespaceAlias(namespaceName);

if(settings.EnableAliasForTypeCastSegments && !string.IsNullOrEmpty(namespaceAlias))
{
return namespaceAlias.TrimEnd('.') + "." + StructuredType.FullTypeName().Split(new char[] { '.' }, StringSplitOptions.RemoveEmptyEntries).Last();
}

return StructuredType.FullTypeName();
return StructuredType is IEdmSchemaElement element && _model != null
? EdmModelHelper.StripOrAliasNamespacePrefix(element, settings, _model)
: StructuredType.FullTypeName();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,14 @@
<TargetFrameworks>netstandard2.0</TargetFrameworks>
<PackageId>Microsoft.OpenApi.OData</PackageId>
<SignAssembly>true</SignAssembly>
<Version>1.4.0-preview1</Version>
<Version>1.4.0-preview2</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>
<RepositoryUrl>https://github.com/Microsoft/OpenAPI.NET.OData</RepositoryUrl>
<PackageReleaseNotes>
- DELETE methods should always return response status code 204
- DELETE methods should always return response status code #204
- Aliases or strips namespace prefixes from segment names when and where applicable #365
</PackageReleaseNotes>
<AssemblyName>Microsoft.OpenApi.OData.Reader</AssemblyName>
<AssemblyOriginatorKeyFile>..\..\tool\Microsoft.OpenApi.OData.snk</AssemblyOriginatorKeyFile>
Expand Down
8 changes: 7 additions & 1 deletion src/Microsoft.OpenApi.OData.Reader/OpenApiConvertSettings.cs
Original file line number Diff line number Diff line change
Expand Up @@ -336,6 +336,11 @@ public string PathPrefix
/// </summary>
public bool EnableAliasForTypeCastSegments { get; set; } = false;

/// <summary>
/// Enables the use of aliases for operation segments to shorten the url path.
/// </summary>
public bool EnableAliasForOperationSegments { get; set; } = false;

internal OpenApiConvertSettings Clone()
{
var newSettings = new OpenApiConvertSettings
Expand Down Expand Up @@ -387,7 +392,8 @@ internal OpenApiConvertSettings Clone()
AddAlternateKeyPaths = this.AddAlternateKeyPaths,
NamespacePrefixToStripForInMethodPaths = this.NamespacePrefixToStripForInMethodPaths,
EnableAliasForTypeCastSegments = this.EnableAliasForTypeCastSegments,
SemVerVersion = this.SemVerVersion
SemVerVersion = this.SemVerVersion,
EnableAliasForOperationSegments = this.EnableAliasForOperationSegments
};

return newSettings;
Expand Down
Loading

0 comments on commit f9e45d0

Please sign in to comment.