diff --git a/src/Microsoft.OpenApi.OData.Reader/Edm/ODataPathProvider.cs b/src/Microsoft.OpenApi.OData.Reader/Edm/ODataPathProvider.cs
index 18c1791e..49ff1ef9 100644
--- a/src/Microsoft.OpenApi.OData.Reader/Edm/ODataPathProvider.cs
+++ b/src/Microsoft.OpenApi.OData.Reader/Edm/ODataPathProvider.cs
@@ -184,14 +184,44 @@ private void RetrieveNavigationSourcePaths(IEdmNavigationSource navigationSource
}
// media entity
+ RetrieveMediaEntityStreamPaths(entityType, path);
+
+ // navigation property
+ foreach (IEdmNavigationProperty np in entityType.DeclaredNavigationProperties())
+ {
+ if (CanFilter(np))
+ {
+ RetrieveNavigationPropertyPaths(np, path);
+ }
+ }
+
+ if (entitySet != null)
+ {
+ path.Pop(); // end of entity
+ }
+
+ path.Pop(); // end of navigation source.
+ Debug.Assert(path.Any() == false);
+ }
+
+ ///
+ /// Retrieves the paths for a media entity stream.
+ ///
+ /// The entity type.
+ /// The current OData path.
+ 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())
{
- path.Push(new ODataStreamPropertySegment(sp.Name));
- AppendPath(path.Clone());
- path.Pop();
+ currentPath.Push(new ODataStreamPropertySegment(sp.Name));
+ AppendPath(currentPath.Clone());
+ currentPath.Pop();
}
if (sp.Name.Equals("content", System.StringComparison.OrdinalIgnoreCase))
@@ -205,27 +235,10 @@ private void RetrieveNavigationSourcePaths(IEdmNavigationSource navigationSource
*/
if (createValuePath && entityType.HasStream)
{
- path.Push(new ODataStreamContentSegment());
- AppendPath(path.Clone());
- path.Pop();
+ currentPath.Push(new ODataStreamContentSegment());
+ AppendPath(currentPath.Clone());
+ currentPath.Pop();
}
-
- // navigation property
- foreach (IEdmNavigationProperty np in entityType.DeclaredNavigationProperties())
- {
- if (CanFilter(np))
- {
- RetrieveNavigationPropertyPaths(np, path);
- }
- }
-
- if (entitySet != null)
- {
- path.Pop(); // end of entity
- }
-
- path.Pop(); // end of navigation source.
- Debug.Assert(path.Any() == false);
}
///
@@ -284,6 +297,10 @@ private void RetrieveNavigationPropertyPaths(IEdmNavigationProperty navigationPr
}
}
}
+
+ // Get possible navigation property stream paths
+ RetrieveMediaEntityStreamPaths(navEntityType, currentPath);
+
if (navigationProperty.TargetMultiplicity() == EdmMultiplicity.Many)
{
currentPath.Pop();
diff --git a/src/Microsoft.OpenApi.OData.Reader/Operation/EntitySetPostOperationHandler.cs b/src/Microsoft.OpenApi.OData.Reader/Operation/EntitySetPostOperationHandler.cs
index 5233ff25..9c87c52f 100644
--- a/src/Microsoft.OpenApi.OData.Reader/Operation/EntitySetPostOperationHandler.cs
+++ b/src/Microsoft.OpenApi.OData.Reader/Operation/EntitySetPostOperationHandler.cs
@@ -43,40 +43,13 @@ protected override void SetBasicInfo(OpenApiOperation operation)
///
protected override void SetRequestBody(OpenApiOperation operation)
{
- OpenApiSchema schema = null;
-
- if (Context.Settings.EnableDerivedTypesReferencesForRequestBody)
- {
- schema = EdmModelHelper.GetDerivedTypesReferenceSchema(EntitySet.EntityType(), Context.Model);
- }
-
- if (schema == null)
- {
- schema = new OpenApiSchema
- {
- Reference = new OpenApiReference
- {
- Type = ReferenceType.Schema,
- Id = EntitySet.EntityType().FullName()
- }
- };
- }
-
// The requestBody field contains a Request Body Object for the request body
// that references the schema of the entity set’s entity type in the global schemas.
operation.RequestBody = new OpenApiRequestBody
{
Required = true,
Description = "New entity",
- Content = new Dictionary
- {
- {
- Constants.ApplicationJsonMediaType, new OpenApiMediaType
- {
- Schema = schema
- }
- }
- }
+ Content = GetContentDescription()
};
base.SetRequestBody(operation);
@@ -85,25 +58,6 @@ protected override void SetRequestBody(OpenApiOperation operation)
///
protected override void SetResponses(OpenApiOperation operation)
{
- OpenApiSchema schema = null;
-
- if (Context.Settings.EnableDerivedTypesReferencesForResponses)
- {
- schema = EdmModelHelper.GetDerivedTypesReferenceSchema(EntitySet.EntityType(), Context.Model);
- }
-
- if (schema == null)
- {
- schema = new OpenApiSchema
- {
- Reference = new OpenApiReference
- {
- Type = ReferenceType.Schema,
- Id = EntitySet.EntityType().FullName()
- }
- };
- }
-
operation.Responses = new OpenApiResponses
{
{
@@ -111,16 +65,7 @@ protected override void SetResponses(OpenApiOperation operation)
new OpenApiResponse
{
Description = "Created entity",
- Content = new Dictionary
- {
- {
- Constants.ApplicationJsonMediaType,
- new OpenApiMediaType
- {
- Schema = schema
- }
- }
- }
+ Content = GetContentDescription()
}
}
};
@@ -159,5 +104,63 @@ protected override void AppendCustomParameters(OpenApiOperation operation)
AppendCustomParameters(operation, insert.CustomHeaders, ParameterLocation.Header);
}
}
+
+ ///
+ /// Get the entity content description.
+ ///
+ /// The entity content description.
+ private IDictionary GetContentDescription()
+ {
+ OpenApiSchema schema = GetEntitySchema();
+ var content = new Dictionary();
+
+ if (EntitySet.EntityType().HasStream)
+ {
+ // TODO: Read the AcceptableMediaType annotation from model
+ content.Add(Constants.ApplicationOctetStreamMediaType, new OpenApiMediaType
+ {
+ Schema = new OpenApiSchema
+ {
+ Type = "string",
+ Format = "binary"
+ }
+ });
+ }
+
+ content.Add(Constants.ApplicationJsonMediaType, new OpenApiMediaType
+ {
+ Schema = schema
+ });
+
+ return content;
+ }
+
+ ///
+ /// Get the entity schema.
+ ///
+ /// The entity schema.
+ private OpenApiSchema GetEntitySchema()
+ {
+ OpenApiSchema schema = null;
+
+ if (Context.Settings.EnableDerivedTypesReferencesForRequestBody)
+ {
+ schema = EdmModelHelper.GetDerivedTypesReferenceSchema(EntitySet.EntityType(), Context.Model);
+ }
+
+ if (schema == null)
+ {
+ schema = new OpenApiSchema
+ {
+ Reference = new OpenApiReference
+ {
+ Type = ReferenceType.Schema,
+ Id = EntitySet.EntityType().FullName()
+ }
+ };
+ }
+
+ return schema;
+ }
}
}
diff --git a/src/Microsoft.OpenApi.OData.Reader/Operation/MediaEntityGetOperationHandler.cs b/src/Microsoft.OpenApi.OData.Reader/Operation/MediaEntityGetOperationHandler.cs
index fa6cf701..d0c8fa41 100644
--- a/src/Microsoft.OpenApi.OData.Reader/Operation/MediaEntityGetOperationHandler.cs
+++ b/src/Microsoft.OpenApi.OData.Reader/Operation/MediaEntityGetOperationHandler.cs
@@ -17,7 +17,7 @@ namespace Microsoft.OpenApi.OData.Operation
///
/// Retrieve a media content for an Entity
///
- internal class MediaEntityGetOperationHandler : EntitySetOperationHandler
+ internal class MediaEntityGetOperationHandler : MediaEntityOperationalHandler
{
///
public override OperationType OperationType => OperationType.Get;
@@ -25,16 +25,31 @@ internal class MediaEntityGetOperationHandler : EntitySetOperationHandler
///
protected override void SetBasicInfo(OpenApiOperation operation)
{
- string typeName = EntitySet.EntityType().Name;
-
// Summary
- operation.Summary = $"Get media content for {typeName} from {EntitySet.Name}";
+ if (EntitySet != null)
+ {
+ string typeName = EntitySet.EntityType().Name;
+ operation.Summary = $"Get media content for {typeName} from {EntitySet.Name}";
+ }
+ else
+ {
+ operation.Summary = $"Get media content for the navigation property {NavigationProperty.Name} from {NavigationSource.Name}";
+ }
// OperationId
if (Context.Settings.EnableOperationId)
{
string identifier = Path.LastSegment.Kind == ODataSegmentKind.StreamContent ? "Content" : Path.LastSegment.Identifier;
- operation.OperationId = EntitySet.Name + "." + typeName + ".Get" + Utils.UpperFirstChar(identifier);
+
+ if (EntitySet != null)
+ {
+ string typeName = EntitySet.EntityType().Name;
+ operation.OperationId = $"{EntitySet.Name}.{typeName}.Get{Utils.UpperFirstChar(identifier)}";
+ }
+ else // Singleton
+ {
+ operation.OperationId = GetOperationId("Get", identifier);
+ }
}
base.SetBasicInfo(operation);
@@ -43,22 +58,6 @@ protected override void SetBasicInfo(OpenApiOperation operation)
///
protected override void SetResponses(OpenApiOperation operation)
{
- OpenApiSchema schema = null;
-
- if (Context.Settings.EnableDerivedTypesReferencesForResponses)
- {
- schema = EdmModelHelper.GetDerivedTypesReferenceSchema(EntitySet.EntityType(), Context.Model);
- }
-
- if (schema == null)
- {
- schema = new OpenApiSchema
- {
- Type = "string",
- Format = "binary"
- };
- }
-
operation.Responses = new OpenApiResponses
{
{
@@ -72,7 +71,11 @@ protected override void SetResponses(OpenApiOperation operation)
Constants.ApplicationOctetStreamMediaType,
new OpenApiMediaType
{
- Schema = schema
+ Schema = new OpenApiSchema
+ {
+ Type = "string",
+ Format = "binary"
+ }
}
}
}
@@ -86,7 +89,9 @@ protected override void SetResponses(OpenApiOperation operation)
///
protected override void SetSecurity(OpenApiOperation operation)
{
- ReadRestrictionsType read = Context.Model.GetRecord(EntitySet, CapabilitiesConstants.ReadRestrictions);
+ ReadRestrictionsType read = EntitySet != null
+ ? Context.Model.GetRecord(EntitySet, CapabilitiesConstants.ReadRestrictions)
+ : Context.Model.GetRecord(Singleton, CapabilitiesConstants.ReadRestrictions);
if (read == null)
{
return;
diff --git a/src/Microsoft.OpenApi.OData.Reader/Operation/MediaEntityOperationalHandler.cs b/src/Microsoft.OpenApi.OData.Reader/Operation/MediaEntityOperationalHandler.cs
new file mode 100644
index 00000000..e1c7afdd
--- /dev/null
+++ b/src/Microsoft.OpenApi.OData.Reader/Operation/MediaEntityOperationalHandler.cs
@@ -0,0 +1,114 @@
+// ------------------------------------------------------------
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License (MIT). See LICENSE in the repo root for license information.
+// ------------------------------------------------------------
+
+using Microsoft.OData.Edm;
+using Microsoft.OpenApi.Any;
+using Microsoft.OpenApi.Models;
+using Microsoft.OpenApi.OData.Common;
+using Microsoft.OpenApi.OData.Edm;
+using System.Collections.Generic;
+using System.Linq;
+
+namespace Microsoft.OpenApi.OData.Operation
+{
+ ///
+ /// Base class for operation of media entity.
+ ///
+ internal abstract class MediaEntityOperationalHandler : NavigationPropertyOperationHandler
+ {
+ ///
+ /// Gets/sets the .
+ ///
+ protected IEdmEntitySet EntitySet { get; private set; }
+
+ ///
+ /// Gets the .
+ ///
+ protected IEdmSingleton Singleton { get; private set; }
+
+ ///
+ protected override void Initialize(ODataContext context, ODataPath path)
+ {
+ // The first segment will either be an entity set navigation source or a singleton navigation source.
+ ODataNavigationSourceSegment navigationSourceSegment = path.FirstSegment as ODataNavigationSourceSegment;
+ EntitySet = navigationSourceSegment.NavigationSource as IEdmEntitySet;
+
+ if (EntitySet == null)
+ {
+ // Singleton
+ base.Initialize(context, path);
+ Singleton = NavigationSource as IEdmSingleton;
+ }
+ }
+
+ ///
+ protected override void SetTags(OpenApiOperation operation)
+ {
+ if (EntitySet == null)
+ {
+ // Singleton
+ base.SetTags(operation);
+ }
+ else // Entityset
+ {
+ string tagIdentifier = EntitySet.Name + "." + EntitySet.EntityType().Name;
+
+ OpenApiTag tag = new OpenApiTag
+ {
+ Name = tagIdentifier
+ };
+
+ // Use an extension for TOC (Table of Content)
+ tag.Extensions.Add(Constants.xMsTocType, new OpenApiString("page"));
+
+ operation.Tags.Add(tag);
+
+ Context.AppendTag(tag);
+ }
+ }
+
+ ///
+ protected override void SetExtensions(OpenApiOperation operation)
+ {
+ base.SetExtensions(operation);
+ }
+
+ ///
+ /// Retrieves the operation Id for a navigation property stream path.
+ ///
+ /// The http method identifier name.
+ /// The stream segment identifier name.
+ ///
+ protected string GetOperationId(string prefix, string identifier)
+ {
+ Utils.CheckArgumentNull(prefix, nameof(prefix));
+ Utils.CheckArgumentNull(identifier, nameof(identifier));
+
+ IList items = new List
+ {
+ NavigationSource.Name
+ };
+
+ var lastpath = Path.Segments.Last(c => c is ODataStreamContentSegment || c is ODataStreamPropertySegment);
+ foreach (var segment in Path.Segments.Skip(1))
+ {
+ if (segment == lastpath)
+ {
+ items.Add(prefix + Utils.UpperFirstChar(identifier));
+ break;
+ }
+ else
+ {
+ if (segment is ODataNavigationPropertySegment npSegment)
+ {
+ items.Add(npSegment.NavigationProperty.Name);
+ }
+ }
+ }
+
+ return string.Join(".", items);
+ }
+ }
+}
diff --git a/src/Microsoft.OpenApi.OData.Reader/Operation/MediaEntityPutOperationHandler.cs b/src/Microsoft.OpenApi.OData.Reader/Operation/MediaEntityPutOperationHandler.cs
index a8ca7cf9..a6a33a68 100644
--- a/src/Microsoft.OpenApi.OData.Reader/Operation/MediaEntityPutOperationHandler.cs
+++ b/src/Microsoft.OpenApi.OData.Reader/Operation/MediaEntityPutOperationHandler.cs
@@ -17,7 +17,7 @@ namespace Microsoft.OpenApi.OData.Operation
///
/// Update a media content for an Entity
///
- internal class MediaEntityPutOperationHandler : EntitySetOperationHandler
+ internal class MediaEntityPutOperationHandler : MediaEntityOperationalHandler
{
///
public override OperationType OperationType => OperationType.Put;
@@ -25,16 +25,31 @@ internal class MediaEntityPutOperationHandler : EntitySetOperationHandler
///
protected override void SetBasicInfo(OpenApiOperation operation)
{
- string typeName = EntitySet.EntityType().Name;
-
// Summary
- operation.Summary = $"Update media content for {typeName} in {EntitySet.Name}";
+ if (EntitySet != null)
+ {
+ string typeName = EntitySet.EntityType().Name;
+ operation.Summary = $"Update media content for {typeName} in {EntitySet.Name}";
+ }
+ else
+ {
+ operation.Summary = $"Update media content for the navigation property {NavigationProperty.Name} in {NavigationSource.Name}";
+ }
// OperationId
if (Context.Settings.EnableOperationId)
{
string identifier = Path.LastSegment.Kind == ODataSegmentKind.StreamContent ? "Content" : Path.LastSegment.Identifier;
- operation.OperationId = EntitySet.Name + "." + typeName + ".Update" + Utils.UpperFirstChar(identifier);
+
+ if (EntitySet != null)
+ {
+ string typeName = EntitySet.EntityType().Name;
+ operation.OperationId = $"{EntitySet.Name}.{typeName}.Update{Utils.UpperFirstChar(identifier)}";
+ }
+ else
+ {
+ operation.OperationId = GetOperationId("Update", identifier);
+ }
}
base.SetBasicInfo(operation);
@@ -54,8 +69,11 @@ protected override void SetRequestBody(OpenApiOperation operation)
{
schema = new OpenApiSchema
{
- Type = "string",
- Format = "binary"
+ Reference = new OpenApiReference
+ {
+ Type = ReferenceType.Schema,
+ Id = EntitySet != null ? EntitySet.EntityType().FullName() : Singleton.EntityType().FullName()
+ }
};
}
@@ -67,6 +85,16 @@ protected override void SetRequestBody(OpenApiOperation operation)
{
{
Constants.ApplicationOctetStreamMediaType, new OpenApiMediaType
+ {
+ Schema = new OpenApiSchema
+ {
+ Type = "string",
+ Format = "binary"
+ }
+ }
+ },
+ {
+ Constants.ApplicationJsonMediaType, new OpenApiMediaType
{
Schema = schema
}
@@ -92,7 +120,9 @@ protected override void SetResponses(OpenApiOperation operation)
///
protected override void SetSecurity(OpenApiOperation operation)
{
- UpdateRestrictionsType update = Context.Model.GetRecord(EntitySet, CapabilitiesConstants.UpdateRestrictions);
+ UpdateRestrictionsType update = EntitySet != null
+ ? Context.Model.GetRecord(EntitySet, CapabilitiesConstants.UpdateRestrictions)
+ : Context.Model.GetRecord(Singleton, CapabilitiesConstants.UpdateRestrictions);
if (update == null || update.Permissions == null)
{
return;
diff --git a/src/Microsoft.OpenApi.OData.Reader/Operation/NavigationPropertyOperationHandler.cs b/src/Microsoft.OpenApi.OData.Reader/Operation/NavigationPropertyOperationHandler.cs
index dab1643f..c183fa26 100644
--- a/src/Microsoft.OpenApi.OData.Reader/Operation/NavigationPropertyOperationHandler.cs
+++ b/src/Microsoft.OpenApi.OData.Reader/Operation/NavigationPropertyOperationHandler.cs
@@ -62,7 +62,8 @@ protected override void Initialize(ODataContext context, ODataPath path)
NavigationProperty = path.OfType().Last().NavigationProperty;
NavigationPropertyPath = string.Join("/",
- Path.Segments.Where(s => !(s is ODataKeySegment || s is ODataNavigationSourceSegment)).Select(e => e.Identifier));
+ Path.Segments.Where(s => !(s is ODataKeySegment || s is ODataNavigationSourceSegment
+ || s is ODataStreamContentSegment || s is ODataStreamPropertySegment)).Select(e => e.Identifier));
IEdmEntitySet entitySet = NavigationSource as IEdmEntitySet;
IEdmSingleton singleton = NavigationSource as IEdmSingleton;
@@ -115,7 +116,7 @@ protected override void SetTags(OpenApiOperation operation)
}
}
}
-
+
string name = string.Join(".", items);
OpenApiTag tag = new OpenApiTag
{
diff --git a/src/Microsoft.OpenApi.OData.Reader/PathItem/MediaEntityPathItemHandler.cs b/src/Microsoft.OpenApi.OData.Reader/PathItem/MediaEntityPathItemHandler.cs
index 24d75c5c..d7ec6c34 100644
--- a/src/Microsoft.OpenApi.OData.Reader/PathItem/MediaEntityPathItemHandler.cs
+++ b/src/Microsoft.OpenApi.OData.Reader/PathItem/MediaEntityPathItemHandler.cs
@@ -3,6 +3,7 @@
// Licensed under the MIT License (MIT). See LICENSE in the repo root for license information.
// ------------------------------------------------------------
+using Microsoft.OData.Edm;
using Microsoft.OpenApi.Models;
using Microsoft.OpenApi.OData.Edm;
using Microsoft.OpenApi.OData.Vocabulary.Capabilities;
@@ -12,15 +13,28 @@ namespace Microsoft.OpenApi.OData.PathItem
///
/// Create a for a media entity.
///
- internal class MediaEntityPathItemHandler : EntitySetPathItemHandler
+ internal class MediaEntityPathItemHandler : PathItemHandler
{
///
protected override ODataPathKind HandleKind => ODataPathKind.MediaEntity;
+ ///
+ /// Gets the entity set.
+ ///
+ protected IEdmEntitySet EntitySet { get; private set; }
+
+ ///
+ /// Gets the singleton.
+ ///
+ protected IEdmSingleton Singleton { get; private set; }
+
///
protected override void SetOperations(OpenApiPathItem item)
{
- ReadRestrictionsType read = Context.Model.GetRecord(EntitySet);
+ ReadRestrictionsType read = EntitySet != null
+ ? Context.Model.GetRecord(EntitySet)
+ : Context.Model.GetRecord(Singleton);
+
if (read == null ||
(read.ReadByKeyRestrictions == null && read.IsReadable) ||
(read.ReadByKeyRestrictions != null && read.ReadByKeyRestrictions.IsReadable))
@@ -28,11 +42,29 @@ protected override void SetOperations(OpenApiPathItem item)
AddOperation(item, OperationType.Get);
}
- UpdateRestrictionsType update = Context.Model.GetRecord(EntitySet);
+ UpdateRestrictionsType update = EntitySet != null
+ ? Context.Model.GetRecord(EntitySet)
+ : Context.Model.GetRecord(Singleton);
+
if (update == null || update.IsUpdatable)
{
AddOperation(item, OperationType.Put);
}
}
+
+ ///
+ protected override void Initialize(ODataContext context, ODataPath path)
+ {
+ base.Initialize(context, path);
+
+ // The first segment could be an entity set segment or a singleton segment.
+ ODataNavigationSourceSegment navigationSourceSegment = path.FirstSegment as ODataNavigationSourceSegment;
+
+ EntitySet = navigationSourceSegment.NavigationSource as IEdmEntitySet;
+ if (EntitySet == null)
+ {
+ Singleton = navigationSourceSegment.NavigationSource as IEdmSingleton;
+ }
+ }
}
}
diff --git a/test/Microsoft.OpenAPI.OData.Reader.Tests/Edm/ODataPathProviderTests.cs b/test/Microsoft.OpenAPI.OData.Reader.Tests/Edm/ODataPathProviderTests.cs
index a740a7d9..e189e94a 100644
--- a/test/Microsoft.OpenAPI.OData.Reader.Tests/Edm/ODataPathProviderTests.cs
+++ b/test/Microsoft.OpenAPI.OData.Reader.Tests/Edm/ODataPathProviderTests.cs
@@ -44,7 +44,7 @@ public void GetPathsForGraphBetaModelReturnsAllPaths()
// Assert
Assert.NotNull(paths);
- Assert.Equal(4457, paths.Count());
+ Assert.Equal(4544, paths.Count());
}
[Fact]
@@ -192,7 +192,7 @@ public void GetPathsWithContainedNavigationPropertytWorks()
";
-
+
string entitySet = @"";
IEdmModel model = GetEdmModel(entityType, entitySet);
ODataPathProvider provider = new ODataPathProvider();
@@ -229,21 +229,21 @@ public void GetPathsWithStreamPropertyAndWithEntityHasStreamWorks(bool hasStream
if (hasStream && !streamPropName.Equals("Content", StringComparison.OrdinalIgnoreCase))
{
- Assert.Equal(4, paths.Count());
- Assert.Equal(new[] { "/Todos", "/Todos({Id})", "/Todos({Id})/$value", "/Todos({Id})/Logo" },
+ Assert.Equal(7, paths.Count());
+ Assert.Equal(new[] { "/me", "/me/photo", "/me/photo/$value", "/Todos", "/Todos({Id})", "/Todos({Id})/$value", "/Todos({Id})/Logo" },
paths.Select(p => p.GetPathItemName()));
}
else if ((hasStream && streamPropName.Equals("Content", StringComparison.OrdinalIgnoreCase)) ||
(!hasStream && streamPropName.Equals("Content", StringComparison.OrdinalIgnoreCase)))
{
- Assert.Equal(3, paths.Count());
- Assert.Equal(new[] { "/Todos", "/Todos({Id})", "/Todos({Id})/Content" },
+ Assert.Equal(6, paths.Count());
+ Assert.Equal(new[] { "/me", "/me/photo", "/me/photo/$value", "/Todos", "/Todos({Id})", "/Todos({Id})/Content" },
paths.Select(p => p.GetPathItemName()));
}
else // !hasStream && !streamPropName.Equals("Content")
{
- Assert.Equal(3, paths.Count());
- Assert.Equal(new[] { "/Todos", "/Todos({Id})", "/Todos({Id})/Logo" },
+ Assert.Equal(6, paths.Count());
+ Assert.Equal(new[] { "/me", "/me/photo", "/me/photo/$value", "/Todos", "/Todos({Id})", "/Todos({Id})/Logo"},
paths.Select(p => p.GetPathItemName()));
}
}
@@ -282,9 +282,17 @@ private static IEdmModel GetEdmModel(bool hasStream, string streamPropName)
-
-
-
+
+
+
+
+
+
+
+
+
+
+
diff --git a/test/Microsoft.OpenAPI.OData.Reader.Tests/Operation/EntitySetGetOperationHandlerTests.cs b/test/Microsoft.OpenAPI.OData.Reader.Tests/Operation/EntitySetGetOperationHandlerTests.cs
index 6529d9f3..e921b39e 100644
--- a/test/Microsoft.OpenAPI.OData.Reader.Tests/Operation/EntitySetGetOperationHandlerTests.cs
+++ b/test/Microsoft.OpenAPI.OData.Reader.Tests/Operation/EntitySetGetOperationHandlerTests.cs
@@ -326,12 +326,12 @@ public void CreateEntitySetGetOperationReturnsSecurityForReadRestrictions(bool e
}
}
- public static IEdmModel GetEdmModel(string annotation)
+ public static IEdmModel GetEdmModel(string annotation, bool hasStream = false)
{
const string template = @"
-
+
@@ -341,12 +341,12 @@ public static IEdmModel GetEdmModel(string annotation)
- {0}
+ {1}
";
- string modelText = string.Format(template, annotation);
+ string modelText = string.Format(template, hasStream, annotation);
IEdmModel model;
IEnumerable errors;
diff --git a/test/Microsoft.OpenAPI.OData.Reader.Tests/Operation/EntitySetPostOperationHandlerTests.cs b/test/Microsoft.OpenAPI.OData.Reader.Tests/Operation/EntitySetPostOperationHandlerTests.cs
index ad15568d..8f5fb295 100644
--- a/test/Microsoft.OpenAPI.OData.Reader.Tests/Operation/EntitySetPostOperationHandlerTests.cs
+++ b/test/Microsoft.OpenAPI.OData.Reader.Tests/Operation/EntitySetPostOperationHandlerTests.cs
@@ -6,6 +6,7 @@
using Microsoft.OData.Edm;
using Microsoft.OpenApi.Extensions;
using Microsoft.OpenApi.Models;
+using Microsoft.OpenApi.OData.Common;
using Microsoft.OpenApi.OData.Edm;
using Microsoft.OpenApi.OData.Tests;
using Xunit;
@@ -17,12 +18,14 @@ public class EntitySetPostOperationHandlerTests
private EntitySetPostOperationHandler _operationHandler = new EntitySetPostOperationHandler();
[Theory]
- [InlineData(true)]
- [InlineData(false)]
- public void CreateEntitySetPostOperationReturnsCorrectOperation(bool enableOperationId)
+ [InlineData(true, true)]
+ [InlineData(false, true)]
+ [InlineData(true, false)]
+ [InlineData(false, false)]
+ public void CreateEntitySetPostOperationReturnsCorrectOperation(bool enableOperationId, bool hasStream)
{
// Arrange
- IEdmModel model = EntitySetGetOperationHandlerTests.GetEdmModel("");
+ IEdmModel model = EntitySetGetOperationHandlerTests.GetEdmModel("", hasStream);
IEdmEntitySet entitySet = model.EntityContainer.FindEntitySet("Customers");
OpenApiConvertSettings settings = new OpenApiConvertSettings
{
@@ -47,6 +50,25 @@ public void CreateEntitySetPostOperationReturnsCorrectOperation(bool enableOpera
Assert.NotNull(post.Responses);
Assert.Equal(2, post.Responses.Count);
+ if (hasStream)
+ {
+ // TODO: Read the AcceptableMediaType annotation from model
+ Assert.True(post.Responses["201"].Content.ContainsKey(Constants.ApplicationOctetStreamMediaType));
+ Assert.True(post.Responses["201"].Content.ContainsKey(Constants.ApplicationJsonMediaType));
+
+ Assert.NotNull(post.RequestBody);
+ // TODO: Read the AcceptableMediaType annotation from model
+ Assert.True(post.RequestBody.Content.ContainsKey(Constants.ApplicationOctetStreamMediaType));
+ Assert.True(post.RequestBody.Content.ContainsKey(Constants.ApplicationJsonMediaType));
+ }
+ else
+ {
+ Assert.True(post.Responses["201"].Content.ContainsKey(Constants.ApplicationJsonMediaType));
+
+ Assert.NotNull(post.RequestBody);
+ Assert.True(post.RequestBody.Content.ContainsKey(Constants.ApplicationJsonMediaType));
+ }
+
if (enableOperationId)
{
Assert.Equal("Customers.Customer.CreateCustomer", post.OperationId);
diff --git a/test/Microsoft.OpenAPI.OData.Reader.Tests/Operation/MediaEntityGetOperationHandlerTests.cs b/test/Microsoft.OpenAPI.OData.Reader.Tests/Operation/MediaEntityGetOperationHandlerTests.cs
index 1e9f2522..9d6bff92 100644
--- a/test/Microsoft.OpenAPI.OData.Reader.Tests/Operation/MediaEntityGetOperationHandlerTests.cs
+++ b/test/Microsoft.OpenAPI.OData.Reader.Tests/Operation/MediaEntityGetOperationHandlerTests.cs
@@ -30,7 +30,9 @@ public void CreateMediaEntityGetOperationReturnsCorrectOperation(bool enableOper
ODataContext context = new ODataContext(model, settings);
IEdmEntitySet todos = model.EntityContainer.FindEntitySet("Todos");
+ IEdmSingleton me = model.EntityContainer.FindSingleton("me");
Assert.NotNull(todos);
+ Assert.NotNull(me);
IEdmEntityType todo = model.SchemaElements.OfType().First(c => c.Name == "Todo");
IEdmStructuralProperty sp = todo.DeclaredStructuralProperties().First(c => c.Name == "Logo");
@@ -38,27 +40,45 @@ public void CreateMediaEntityGetOperationReturnsCorrectOperation(bool enableOper
new ODataKeySegment(todos.EntityType()),
new ODataStreamPropertySegment(sp.Name));
+ IEdmEntityType user = model.SchemaElements.OfType().First(c => c.Name == "user");
+ IEdmNavigationProperty navProperty = user.DeclaredNavigationProperties().First(c => c.Name == "photo");
+ ODataPath path2 = new ODataPath(new ODataNavigationSourceSegment(me),
+ new ODataNavigationPropertySegment(navProperty),
+ new ODataStreamContentSegment());
+
// Act
var getOperation = _operationalHandler.CreateOperation(context, path);
+ var getOperation2 = _operationalHandler.CreateOperation(context, path2);
// Assert
Assert.NotNull(getOperation);
+ Assert.NotNull(getOperation2);
Assert.Equal("Get media content for Todo from Todos", getOperation.Summary);
+ Assert.Equal("Get media content for the navigation property photo from me", getOperation2.Summary);
Assert.NotNull(getOperation.Tags);
+ Assert.NotNull(getOperation2.Tags);
+
var tag = Assert.Single(getOperation.Tags);
+ var tag2 = Assert.Single(getOperation2.Tags);
Assert.Equal("Todos.Todo", tag.Name);
+ Assert.Equal("me.profilePhoto", tag2.Name);
Assert.NotNull(getOperation.Responses);
+ Assert.NotNull(getOperation2.Responses);
Assert.Equal(2, getOperation.Responses.Count);
+ Assert.Equal(2, getOperation2.Responses.Count);
Assert.Equal(new[] { "200", "default" }, getOperation.Responses.Select(r => r.Key));
+ Assert.Equal(new[] { "200", "default" }, getOperation2.Responses.Select(r => r.Key));
if (enableOperationId)
{
Assert.Equal("Todos.Todo.GetLogo", getOperation.OperationId);
+ Assert.Equal("me.photo.GetContent", getOperation2.OperationId);
}
else
{
Assert.Null(getOperation.OperationId);
+ Assert.Null(getOperation2.OperationId);
}
}
@@ -74,9 +94,17 @@ public static IEdmModel GetEdmModel()
-
-
-
+
+
+
+
+
+
+
+
+
+
+
diff --git a/test/Microsoft.OpenAPI.OData.Reader.Tests/Operation/MediaEntityPutOperationHandlerTests.cs b/test/Microsoft.OpenAPI.OData.Reader.Tests/Operation/MediaEntityPutOperationHandlerTests.cs
index 14fe92d4..88533c86 100644
--- a/test/Microsoft.OpenAPI.OData.Reader.Tests/Operation/MediaEntityPutOperationHandlerTests.cs
+++ b/test/Microsoft.OpenAPI.OData.Reader.Tests/Operation/MediaEntityPutOperationHandlerTests.cs
@@ -28,6 +28,7 @@ public void CreateEntityPutOperationReturnsCorrectOperation(bool enableOperation
ODataContext context = new ODataContext(model, settings);
IEdmEntitySet todos = model.EntityContainer.FindEntitySet("Todos");
+ IEdmSingleton me = model.EntityContainer.FindSingleton("me");
Assert.NotNull(todos);
IEdmEntityType todo = model.SchemaElements.OfType().First(c => c.Name == "Todo");
@@ -36,27 +37,45 @@ public void CreateEntityPutOperationReturnsCorrectOperation(bool enableOperation
new ODataKeySegment(todos.EntityType()),
new ODataStreamPropertySegment(sp.Name));
+ IEdmEntityType user = model.SchemaElements.OfType().First(c => c.Name == "user");
+ IEdmNavigationProperty navProperty = user.DeclaredNavigationProperties().First(c => c.Name == "photo");
+ ODataPath path2 = new ODataPath(new ODataNavigationSourceSegment(me),
+ new ODataNavigationPropertySegment(navProperty),
+ new ODataStreamContentSegment());
+
// Act
var getOperation = _operationalHandler.CreateOperation(context, path);
+ var getOperation2 = _operationalHandler.CreateOperation(context, path2);
// Assert
Assert.NotNull(getOperation);
+ Assert.NotNull(getOperation2);
Assert.Equal("Update media content for Todo in Todos", getOperation.Summary);
+ Assert.Equal("Update media content for the navigation property photo in me", getOperation2.Summary);
Assert.NotNull(getOperation.Tags);
+ Assert.NotNull(getOperation2.Tags);
+
var tag = Assert.Single(getOperation.Tags);
+ var tag2 = Assert.Single(getOperation2.Tags);
Assert.Equal("Todos.Todo", tag.Name);
+ Assert.Equal("me.profilePhoto", tag2.Name);
Assert.NotNull(getOperation.Responses);
+ Assert.NotNull(getOperation2.Responses);
Assert.Equal(2, getOperation.Responses.Count);
+ Assert.Equal(2, getOperation2.Responses.Count);
Assert.Equal(new[] { "204", "default" }, getOperation.Responses.Select(r => r.Key));
+ Assert.Equal(new[] { "204", "default" }, getOperation2.Responses.Select(r => r.Key));
if (enableOperationId)
{
Assert.Equal("Todos.Todo.UpdateLogo", getOperation.OperationId);
+ Assert.Equal("me.photo.UpdateContent", getOperation2.OperationId);
}
else
{
Assert.Null(getOperation.OperationId);
+ Assert.Null(getOperation2.OperationId);
}
}
}
diff --git a/test/Microsoft.OpenAPI.OData.Reader.Tests/PathItem/MediaEntityPathItemHandlerTests.cs b/test/Microsoft.OpenAPI.OData.Reader.Tests/PathItem/MediaEntityPathItemHandlerTests.cs
index 53f164f4..ee20c584 100644
--- a/test/Microsoft.OpenAPI.OData.Reader.Tests/PathItem/MediaEntityPathItemHandlerTests.cs
+++ b/test/Microsoft.OpenAPI.OData.Reader.Tests/PathItem/MediaEntityPathItemHandlerTests.cs
@@ -59,8 +59,10 @@ public void CreateMediaEntityPathItemReturnsCorrectItem()
// Arrange
IEdmModel model = GetEdmModel("");
ODataContext context = new ODataContext(model);
- var entitySet = model.EntityContainer.FindEntitySet("Todos");
+ IEdmEntitySet entitySet = model.EntityContainer.FindEntitySet("Todos");
+ IEdmSingleton singleton = model.EntityContainer.FindSingleton("me");
Assert.NotNull(entitySet); // guard
+ Assert.NotNull(singleton);
IEdmEntityType entityType = entitySet.EntityType();
IEdmStructuralProperty sp = entityType.DeclaredStructuralProperties().First(c => c.Name == "Logo");
@@ -68,14 +70,30 @@ public void CreateMediaEntityPathItemReturnsCorrectItem()
new ODataKeySegment(entityType),
new ODataStreamPropertySegment(sp.Name));
+ IEdmEntityType user = model.SchemaElements.OfType().First(c => c.Name == "user");
+ IEdmNavigationProperty navProperty = user.DeclaredNavigationProperties().First(c => c.Name == "photo");
+ ODataPath path2 = new ODataPath(new ODataNavigationSourceSegment(singleton),
+ new ODataNavigationPropertySegment(navProperty),
+ new ODataStreamContentSegment());
+
// Act
var pathItem = _pathItemHandler.CreatePathItem(context, path);
+ var pathItem2 = _pathItemHandler.CreatePathItem(context, path2);
+
+ // Assert
+ Assert.NotNull(pathItem);
+ Assert.NotNull(pathItem2);
Assert.NotNull(pathItem.Operations);
+ Assert.NotNull(pathItem2.Operations);
Assert.NotEmpty(pathItem.Operations);
+ Assert.NotEmpty(pathItem2.Operations);
Assert.Equal(2, pathItem.Operations.Count);
+ Assert.Equal(2, pathItem2.Operations.Count);
Assert.Equal(new OperationType[] { OperationType.Get, OperationType.Put },
pathItem.Operations.Select(o => o.Key));
+ Assert.Equal(new OperationType[] { OperationType.Get, OperationType.Put },
+ pathItem2.Operations.Select(o => o.Key));
}
[Theory]
@@ -127,9 +145,10 @@ private void VerifyPathItemOperationsForStreamPropertySegment(string annotation,
Assert.NotNull(entitySet); // guard
IEdmEntityType entityType = entitySet.EntityType();
+ IEdmStructuralProperty sp = entityType.DeclaredStructuralProperties().First(c => c.Name == "Logo");
ODataPath path = new ODataPath(new ODataNavigationSourceSegment(entitySet),
new ODataKeySegment(entityType),
- new ODataStreamContentSegment());
+ new ODataStreamPropertySegment(sp.Name));
// Act
var pathItem = _pathItemHandler.CreatePathItem(context, path);
@@ -148,23 +167,35 @@ private void VerifyPathItemOperationsForStreamContentSegment(string annotation,
IEdmModel model = GetEdmModel(annotation);
ODataContext context = new ODataContext(model);
IEdmEntitySet entitySet = model.EntityContainer.FindEntitySet("Todos");
+ IEdmSingleton singleton = model.EntityContainer.FindSingleton("me");
Assert.NotNull(entitySet); // guard
+ Assert.NotNull(singleton);
IEdmEntityType entityType = entitySet.EntityType();
- IEdmStructuralProperty sp = entityType.DeclaredStructuralProperties().First(c => c.Name == "Logo");
+ IEdmEntityType user = model.SchemaElements.OfType().First(c => c.Name == "user");
+ IEdmNavigationProperty navProperty = user.DeclaredNavigationProperties().First(c => c.Name == "photo");
+ ODataPath path2 = new ODataPath(new ODataNavigationSourceSegment(singleton),
+ new ODataNavigationPropertySegment(navProperty),
+ new ODataStreamContentSegment());
+
ODataPath path = new ODataPath(new ODataNavigationSourceSegment(entitySet),
new ODataKeySegment(entityType),
- new ODataStreamPropertySegment(sp.Name));
+ new ODataStreamContentSegment());
// Act
var pathItem = _pathItemHandler.CreatePathItem(context, path);
+ var pathItem2 = _pathItemHandler.CreatePathItem(context, path2);
// Assert
Assert.NotNull(pathItem);
+ Assert.NotNull(pathItem2);
Assert.NotNull(pathItem.Operations);
+ Assert.NotNull(pathItem2.Operations);
Assert.NotEmpty(pathItem.Operations);
+ Assert.NotEmpty(pathItem2.Operations);
Assert.Equal(expected, pathItem.Operations.Select(e => e.Key));
+ Assert.Equal(expected, pathItem2.Operations.Select(e => e.Key));
}
private IEdmModel GetEdmModel(string annotation)
@@ -180,11 +211,22 @@ private IEdmModel GetEdmModel(string annotation)
-
-
+
+
+
+
+
+
+
+
+
+
-
- {0}
+
+ {0}
+
+
+ {0}