Skip to content
This repository was archived by the owner on Jun 20, 2022. It is now read-only.
Merged
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
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
31 changes: 30 additions & 1 deletion 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,33 @@ private void RetrieveNavigationSourcePaths(IEdmNavigationSource navigationSource
AppendPath(path.Clone());
}

// media entity
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();
}

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)
{
path.Push(new ODataStreamContentSegment());
AppendPath(path.Clone());
path.Pop();
}

// navigation property
foreach (IEdmNavigationProperty np in entityType.DeclaredNavigationProperties())
{
Expand Down Expand Up @@ -369,7 +397,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
@@ -0,0 +1,109 @@
// ------------------------------------------------------------
// 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.Models;
using Microsoft.OpenApi.OData.Common;
using Microsoft.OpenApi.OData.Edm;
using Microsoft.OpenApi.OData.Generator;
using Microsoft.OpenApi.OData.Vocabulary.Capabilities;
using System.Collections.Generic;
using System.Linq;

namespace Microsoft.OpenApi.OData.Operation
{
/// <summary>
/// Retrieve a media content for an Entity
/// </summary>
internal class MediaEntityGetOperationHandler : EntitySetOperationHandler
{
/// <inheritdoc/>
public override OperationType OperationType => OperationType.Get;

/// <inheritdoc/>
protected override void SetBasicInfo(OpenApiOperation operation)
{
string typeName = EntitySet.EntityType().Name;

// Summary
operation.Summary = $"Get media content for {typeName} from {EntitySet.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);
}

base.SetBasicInfo(operation);
}

/// <inheritdoc/>
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
{
{
Constants.StatusCode200,
new OpenApiResponse
{
Description = "Retrieved media content",
Content = new Dictionary<string, OpenApiMediaType>
{
{
Constants.ApplicationOctetStreamMediaType,
new OpenApiMediaType
{
Schema = schema
}
}
}
}
}
};
operation.Responses.Add(Constants.StatusCodeDefault, Constants.StatusCodeDefault.GetResponse());

base.SetResponses(operation);
}
/// <inheritdoc/>
protected override void SetSecurity(OpenApiOperation operation)
{
ReadRestrictionsType read = Context.Model.GetRecord<ReadRestrictionsType>(EntitySet, CapabilitiesConstants.ReadRestrictions);
if (read == null)
{
return;
}

ReadRestrictionsBase readBase = read;
if (read.ReadByKeyRestrictions != null)
{
readBase = read.ReadByKeyRestrictions;
}

if (readBase == null && readBase.Permissions == null)
{
return;
}

operation.Security = Context.CreateSecurityRequirements(readBase.Permissions).ToList();
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
// ------------------------------------------------------------
// 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 System.Linq;
using Microsoft.OData.Edm;
using Microsoft.OpenApi.Models;
using Microsoft.OpenApi.OData.Common;
using Microsoft.OpenApi.OData.Edm;
using Microsoft.OpenApi.OData.Generator;
using Microsoft.OpenApi.OData.Vocabulary.Capabilities;

namespace Microsoft.OpenApi.OData.Operation
{
/// <summary>
/// Update a media content for an Entity
/// </summary>
internal class MediaEntityPutOperationHandler : EntitySetOperationHandler
{
/// <inheritdoc/>
public override OperationType OperationType => OperationType.Put;

/// <inheritdoc/>
protected override void SetBasicInfo(OpenApiOperation operation)
{
string typeName = EntitySet.EntityType().Name;

// Summary
operation.Summary = $"Update media content for {typeName} in {EntitySet.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);
}

base.SetBasicInfo(operation);
}

/// <inheritdoc/>
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
{
Type = "string",
Format = "binary"
};
}

operation.RequestBody = new OpenApiRequestBody
{
Required = true,
Description = "New media content.",
Content = new Dictionary<string, OpenApiMediaType>
{
{
Constants.ApplicationOctetStreamMediaType, new OpenApiMediaType
{
Schema = schema
}
}
}
};

base.SetRequestBody(operation);
}

/// <inheritdoc/>
protected override void SetResponses(OpenApiOperation operation)
{
operation.Responses = new OpenApiResponses
{
{ Constants.StatusCode204, Constants.StatusCode204.GetResponse() },
{ Constants.StatusCodeDefault, Constants.StatusCodeDefault.GetResponse() }
};

base.SetResponses(operation);
}

/// <inheritdoc/>
protected override void SetSecurity(OpenApiOperation operation)
{
UpdateRestrictionsType update = Context.Model.GetRecord<UpdateRestrictionsType>(EntitySet, CapabilitiesConstants.UpdateRestrictions);
if (update == null || update.Permissions == null)
{
return;
}

operation.Security = Context.CreateSecurityRequirements(update.Permissions).ToList();
}
}
}
Loading