Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 @@ -15,6 +15,11 @@ internal static class Constants
/// </summary>
public static string ApplicationJsonMediaType = "application/json";

/// <summary>
/// application/octet-stream
/// </summary>
public static string ApplicationOctetStreamMediaType = "application/octet-stream";

/// <summary>
/// Status code: 200
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -196,7 +196,7 @@ public static IEnumerable<string> GetCollection(this IEdmModel model, IEdmVocabu
}
}

return value.ToList();
return value?.ToList();
});
}

Expand Down
6 changes: 5 additions & 1 deletion src/Microsoft.OpenApi.OData.Reader/Edm/ODataPath.cs
Original file line number Diff line number Diff line change
Expand Up @@ -258,7 +258,11 @@ public int CompareTo(ODataPath other)

private ODataPathKind CalcPathType()
{
if (Segments.Any(c => c.Kind == ODataSegmentKind.Ref))
if (Segments.Any(c => c.Kind == ODataSegmentKind.StreamProperty || c.Kind == ODataSegmentKind.StreamContent))
{
return ODataPathKind.MediaEntity;
}
else if (Segments.Any(c => c.Kind == ODataSegmentKind.Ref))
{
return ODataPathKind.Ref;
}
Expand Down
9 changes: 7 additions & 2 deletions src/Microsoft.OpenApi.OData.Reader/Edm/ODataPathKind.cs
Original file line number Diff line number Diff line change
Expand Up @@ -36,15 +36,20 @@ public enum ODataPathKind
OperationImport,

/// <summary>
/// Represents an navigation propert path, for example: ~/users/{id}/onedrive
/// Represents an navigation property path, for example: ~/users/{id}/onedrive
/// </summary>
NavigationProperty,

/// <summary>
/// Represents an navigation propert $ref path, for example: ~/users/{id}/onedrive/$ref
/// Represents an navigation property $ref path, for example: ~/users/{id}/onedrive/$ref
/// </summary>
Ref,

/// <summary>
/// Represents a media entity path, for example: ~/me/photo/$value or ~/reports/deviceConfigurationUserActivity/Content
/// </summary>
MediaEntity,

/// <summary>
/// Represents an un-supported/unknown path.
/// </summary>
Expand Down
95 changes: 71 additions & 24 deletions src/Microsoft.OpenApi.OData.Reader/Edm/ODataPathProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,7 @@ private void AppendPath(ODataPath path)
case ODataPathKind.Entity:
case ODataPathKind.EntitySet:
case ODataPathKind.Singleton:
case ODataPathKind.MediaEntity:
ODataNavigationSourceSegment navigationSourceSegment = (ODataNavigationSourceSegment)path.FirstSegment;
if (!_allNavigationSourcePaths.TryGetValue(navigationSourceSegment.EntityType, out IList<ODataPath> nsList))
{
Expand Down Expand Up @@ -182,6 +183,9 @@ private void RetrieveNavigationSourcePaths(IEdmNavigationSource navigationSource
AppendPath(path.Clone());
}

// media entity
RetrieveMediaEntityStreamPaths(entityType, path);

// navigation property
foreach (IEdmNavigationProperty np in entityType.DeclaredNavigationProperties())
{
Expand All @@ -200,6 +204,43 @@ private void RetrieveNavigationSourcePaths(IEdmNavigationSource navigationSource
Debug.Assert(path.Any() == false);
}

/// <summary>
/// Retrieves the paths for a media entity stream.
/// </summary>
/// <param name="entityType">The entity type.</param>
/// <param name="currentPath">The current OData path.</param>
private void RetrieveMediaEntityStreamPaths(IEdmEntityType entityType, ODataPath currentPath)
{
Debug.Assert(entityType != null);
Debug.Assert(currentPath != null);

bool createValuePath = true;
foreach (IEdmStructuralProperty sp in entityType.DeclaredStructuralProperties())
{
if (sp.Type.AsPrimitive().IsStream())
{
currentPath.Push(new ODataStreamPropertySegment(sp.Name));
AppendPath(currentPath.Clone());
currentPath.Pop();
}

if (sp.Name.Equals("content", System.StringComparison.OrdinalIgnoreCase))
{
createValuePath = false;
}
}

/* Create a /$value path only if entity has stream and
* does not contain a structural property named Content
*/
if (createValuePath && entityType.HasStream)
{
currentPath.Push(new ODataStreamContentSegment());
AppendPath(currentPath.Clone());
currentPath.Pop();
}
}

/// <summary>
/// Retrieve the path for <see cref="IEdmNavigationProperty"/>.
/// </summary>
Expand All @@ -226,40 +267,45 @@ private void RetrieveNavigationPropertyPaths(IEdmNavigationProperty navigationPr
newPath.Push(ODataRefSegment.Instance); // $ref
AppendPath(newPath);
}

// append a navigation property key.
IEdmEntityType navEntityType = navigationProperty.ToEntityType();
if (navigationProperty.TargetMultiplicity() == EdmMultiplicity.Many)
else
{
currentPath.Push(new ODataKeySegment(navEntityType));
AppendPath(currentPath.Clone());
IEdmEntityType navEntityType = navigationProperty.ToEntityType();

if (!navigationProperty.ContainsTarget)
// append a navigation property key.
if (navigationProperty.TargetMultiplicity() == EdmMultiplicity.Many)
{
// TODO: Shall we add "$ref" after {key}, and only support delete?
// ODataPath newPath = currentPath.Clone();
// newPath.Push(ODataRefSegment.Instance); // $ref
// AppendPath(newPath);
currentPath.Push(new ODataKeySegment(navEntityType));
AppendPath(currentPath.Clone());

if (!navigationProperty.ContainsTarget)
{
// TODO: Shall we add "$ref" after {key}, and only support delete?
// ODataPath newPath = currentPath.Clone();
// newPath.Push(ODataRefSegment.Instance); // $ref
// AppendPath(newPath);
}
}
}

if (shouldExpand)
{
// expand to sub navigation properties
foreach (IEdmNavigationProperty subNavProperty in navEntityType.DeclaredNavigationProperties())
if (shouldExpand)
{
if (CanFilter(subNavProperty))
// expand to sub navigation properties
foreach (IEdmNavigationProperty subNavProperty in navEntityType.DeclaredNavigationProperties())
{
RetrieveNavigationPropertyPaths(subNavProperty, currentPath);
if (CanFilter(subNavProperty))
{
RetrieveNavigationPropertyPaths(subNavProperty, currentPath);
}
}
}
}

if (navigationProperty.TargetMultiplicity() == EdmMultiplicity.Many)
{
currentPath.Pop();
}
// Get possible navigation property stream paths
RetrieveMediaEntityStreamPaths(navEntityType, currentPath);

if (navigationProperty.TargetMultiplicity() == EdmMultiplicity.Many)
{
currentPath.Pop();
}
}
currentPath.Pop();
}

Expand Down Expand Up @@ -369,7 +415,8 @@ private bool AppendBoundOperationOnNavigationSourcePath(IEdmOperation edmOperati
foreach (var subPath in value)
{
if ((isCollection && subPath.Kind == ODataPathKind.EntitySet) ||
(!isCollection && subPath.Kind != ODataPathKind.EntitySet))
(!isCollection && subPath.Kind != ODataPathKind.EntitySet &&
subPath.Kind != ODataPathKind.MediaEntity))
{
ODataPath newPath = subPath.Clone();
newPath.Push(new ODataOperationSegment(edmOperation, isEscapedFunction));
Expand Down
12 changes: 11 additions & 1 deletion src/Microsoft.OpenApi.OData.Reader/Edm/ODataSegment.cs
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,17 @@ public enum ODataSegmentKind
/// <summary>
/// $ref
/// </summary>
Ref
Ref,

/// <summary>
/// Stream content -> $value
/// </summary>
StreamContent,

/// <summary>
/// Stream property
/// </summary>
StreamProperty
}

/// <summary>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
// ------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License (MIT). See LICENSE in the repo root for license information.
// ------------------------------------------------------------

using System.Collections.Generic;

namespace Microsoft.OpenApi.OData.Edm
{
/// <summary>
/// Stream segment.
/// </summary>
public class ODataStreamContentSegment : ODataSegment
{
/// <inheritdoc />
public override ODataSegmentKind Kind => ODataSegmentKind.StreamContent;

/// <inheritdoc />
public override string Identifier => "$value";

/// <inheritdoc />
public override string GetPathItemName(OpenApiConvertSettings settings, HashSet<string> parameters) => "$value";
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License (MIT). See LICENSE in the repo root for license information.
// ------------------------------------------------------------

using System.Collections.Generic;
using Microsoft.OpenApi.OData.Common;

namespace Microsoft.OpenApi.OData.Edm
{
/// <summary>
/// Property Stream segment.
/// </summary>
public class ODataStreamPropertySegment : ODataSegment
{
private readonly string _streamPropertyName;
/// <summary>
/// Initializes a new instance of <see cref="ODataTypeCastSegment"/> class.
/// </summary>
/// <param name="streamPropertyName">The name of the stream property.</param>
public ODataStreamPropertySegment(string streamPropertyName)
{
_streamPropertyName = streamPropertyName ?? throw Error.ArgumentNull(nameof(streamPropertyName));
}

/// <inheritdoc />
public override ODataSegmentKind Kind => ODataSegmentKind.StreamProperty;

/// <inheritdoc />
public override string Identifier { get => _streamPropertyName; }

/// <inheritdoc />
public override string GetPathItemName(OpenApiConvertSettings settings, HashSet<string> parameters) => _streamPropertyName;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ internal static class OpenApiParameterGenerator
public static IDictionary<string, OpenApiParameter> CreateParameters(this ODataContext context)
{
Utils.CheckArgumentNull(context, nameof(context));

// It allows defining query options and headers that can be reused across operations of the service.
// The value of parameters is a map of Parameter Objects.
return new Dictionary<string, OpenApiParameter>
Expand Down Expand Up @@ -134,7 +134,10 @@ public static IList<OpenApiParameter> CreateKeyParameters(this ODataContext cont
if (keys.Count() == 1)
{
string keyName = keys.First().Name;
if (context.Settings.PrefixEntityTypeNameBeforeKey)

// If dictionary parameterNameMapping is defined, there's no need of setting the
// keyName, we will retrieve this from the dictionary key.
if (context.Settings.PrefixEntityTypeNameBeforeKey && parameterNameMapping == null)
{
keyName = entityType.Name + "-" + keys.First().Name;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
// ------------------------------------------------------------

using System.Collections.Generic;
using Microsoft.OData.Edm;
using Microsoft.OpenApi.Models;
using Microsoft.OpenApi.OData.Common;
using Microsoft.OpenApi.OData.Edm;
Expand Down Expand Up @@ -44,7 +45,40 @@ public static IDictionary<string, OpenApiPathItem> CreatePathItems(this ODataCon
pathItems.Add(path.GetPathItemName(settings), handler.CreatePathItem(context, path));
}

if (settings.ShowRootPath)
{
OpenApiPathItem rootPath = new OpenApiPathItem()
{
Operations = new Dictionary<OperationType, OpenApiOperation> {
{
OperationType.Get, new OpenApiOperation {
OperationId = "graphService.GetGraphService",
Responses = new OpenApiResponses()
{
{ "200",new OpenApiResponse() {
Description = "OK",
Links = CreateRootLinks(context.EntityContainer)
}
}
}
}
}
}
};
pathItems.Add("/", rootPath);
}

return pathItems;
}

private static IDictionary<string, OpenApiLink> CreateRootLinks(IEdmEntityContainer entityContainer)
{
var links = new Dictionary<string, OpenApiLink>();
foreach (var element in entityContainer.Elements)
{
links.Add(element.Name, new OpenApiLink());
}
return links;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@

<ItemGroup>
<PackageReference Include="Microsoft.OData.Edm" Version="7.6.1" />
<PackageReference Include="Microsoft.OpenApi" Version="1.1.4" />
<PackageReference Include="Microsoft.OpenApi" Version="1.2.2" />
</ItemGroup>

<ItemGroup>
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 @@ -130,6 +130,11 @@ public class OpenApiConvertSettings
/// </summary>
public bool ShowSchemaExamples { get; set; } = false;

/// <summary>
/// Gets/sets a value indicating whether or not to show the root path of the described API.
/// </summary>
public bool ShowRootPath { get; set; } = false;

internal OpenApiConvertSettings Clone()
{
var newSettings = new OpenApiConvertSettings
Expand All @@ -156,7 +161,8 @@ internal OpenApiConvertSettings Clone()
EnableDerivedTypesReferencesForRequestBody = this.EnableDerivedTypesReferencesForRequestBody,
PathPrefix = this.PathPrefix,
ShowLinks = this.ShowLinks,
ShowSchemaExamples = this.ShowSchemaExamples
ShowSchemaExamples = this.ShowSchemaExamples,
ShowRootPath = this.ShowRootPath
};

return newSettings;
Expand Down
Loading