diff --git a/src/Microsoft.OpenApi.OData.Reader/Edm/EdmAnnotationExtensions.cs b/src/Microsoft.OpenApi.OData.Reader/Edm/EdmAnnotationExtensions.cs index 9da97a5d..1568fa79 100644 --- a/src/Microsoft.OpenApi.OData.Reader/Edm/EdmAnnotationExtensions.cs +++ b/src/Microsoft.OpenApi.OData.Reader/Edm/EdmAnnotationExtensions.cs @@ -67,6 +67,19 @@ internal static class EdmVocabularyAnnotationExtensions }); } + public static bool? GetBoolean(this IEdmModel model, string targetPath, string qualifiedName) + { + Utils.CheckArgumentNull(model, nameof(model)); + Utils.CheckArgumentNull(targetPath, nameof(targetPath)); + Utils.CheckArgumentNull(qualifiedName, nameof(qualifiedName)); + + IEdmTargetPath target = model.GetTargetPath(targetPath); + if (target == null) + return default; + + return model.GetBoolean(target, qualifiedName); + } + /// /// Gets the string term value for the given . /// diff --git a/src/Microsoft.OpenApi.OData.Reader/Edm/ODataPath.cs b/src/Microsoft.OpenApi.OData.Reader/Edm/ODataPath.cs index 254d1325..e3a28619 100644 --- a/src/Microsoft.OpenApi.OData.Reader/Edm/ODataPath.cs +++ b/src/Microsoft.OpenApi.OData.Reader/Edm/ODataPath.cs @@ -145,7 +145,6 @@ public string GetPathItemName(OpenApiConvertSettings settings) Utils.CheckArgumentNull(settings, nameof(settings)); // From Open API spec, parameter name is case sensitive, so don't use the IgnoreCase HashSet. - // HashSet parameters = new HashSet(StringComparer.OrdinalIgnoreCase); HashSet parameters = new(); StringBuilder sb = new(); @@ -238,7 +237,6 @@ internal IDictionary> CalculateParamet IDictionary> parameterMapping = new Dictionary>(); // From Open API spec, parameter name is case sensitive, so don't use the IgnoreCase HashSet. - // HashSet parameters = new HashSet(StringComparer.OrdinalIgnoreCase); HashSet parameters = new HashSet(); foreach (var segment in Segments) @@ -259,6 +257,28 @@ internal IDictionary> CalculateParamet return parameterMapping; } + /// + /// Get string representation of the Edm Target Path for annotations + /// + /// The Edm model. + /// The string representation of the Edm target path. + internal string GetTargetPath(IEdmModel model) + { + Utils.CheckArgumentNull(model, nameof(model)); + + var targetPath = new StringBuilder(model.EntityContainer.FullName()); + + bool skipLastSegment = LastSegment is ODataRefSegment + || LastSegment is ODataDollarCountSegment + || LastSegment is ODataStreamContentSegment; + foreach (var segment in Segments.Where(segment => segment is not ODataKeySegment + && !(skipLastSegment && segment == LastSegment))) + { + targetPath.Append($"/{segment.Identifier}"); + } + return targetPath.ToString(); + } + /// /// Output the path string. /// diff --git a/src/Microsoft.OpenApi.OData.Reader/Edm/ODataPathProvider.cs b/src/Microsoft.OpenApi.OData.Reader/Edm/ODataPathProvider.cs index 7a3e3ba9..0a2197f3 100644 --- a/src/Microsoft.OpenApi.OData.Reader/Edm/ODataPathProvider.cs +++ b/src/Microsoft.OpenApi.OData.Reader/Edm/ODataPathProvider.cs @@ -249,7 +249,9 @@ private void RetrieveNavigationSourcePaths(IEdmNavigationSource navigationSource // for entity set, create a path with key and a $count path if (entitySet != null) { - count = _model.GetRecord(entitySet, CapabilitiesConstants.CountRestrictions); + string targetPath = path.GetTargetPath(_model); + count = _model.GetRecord(targetPath, CapabilitiesConstants.CountRestrictions) + ?? _model.GetRecord(entitySet, CapabilitiesConstants.CountRestrictions); if(count?.Countable ?? true) // ~/entitySet/$count CreateCountPath(path, convertSettings); @@ -310,16 +312,22 @@ private void RetrieveComplexPropertyPaths(IEdmEntityType entityType, ODataPath c .Where(x => x.Type.IsComplex() || x.Type.IsCollection() && x.Type.Definition.AsElementType() is IEdmComplexType)) { - if (!ShouldCreateComplexPropertyPaths(sp, convertSettings)) continue; - currentPath.Push(new ODataComplexPropertySegment(sp)); + var targetPath = currentPath.GetTargetPath(_model); + if (!ShouldCreateComplexPropertyPaths(sp, targetPath, convertSettings)) + { + currentPath.Pop(); + continue; + } AppendPath(currentPath.Clone()); if (sp.Type.IsCollection()) { CreateTypeCastPaths(currentPath, convertSettings, sp.Type.Definition.AsElementType() as IEdmComplexType, sp, true); - var count = _model.GetRecord(sp, CapabilitiesConstants.CountRestrictions); - if (count?.IsCountable ?? true) + var isCountable = _model.GetRecord(targetPath, CapabilitiesConstants.CountRestrictions)?.IsCountable + ?? _model.GetRecord(sp, CapabilitiesConstants.CountRestrictions)?.IsCountable + ?? true; + if (isCountable) CreateCountPath(currentPath, convertSettings); } else @@ -363,7 +371,9 @@ private bool RetrieveComplexTypeNavigationPropertyPaths(IEdmComplexType complexT foreach (var np in navigationProperties) { - var count = _model.GetRecord(np, CapabilitiesConstants.CountRestrictions); + var targetPath = currentPath.GetTargetPath(_model); + var count = _model.GetRecord(targetPath, CapabilitiesConstants.CountRestrictions) + ?? _model.GetRecord(np, CapabilitiesConstants.CountRestrictions); RetrieveNavigationPropertyPaths(np, count, currentPath, convertSettings); } @@ -407,9 +417,10 @@ private void TraverseComplexProperty(IEdmStructuralProperty structuralProperty, /// Evaluates whether or not to create paths for complex properties. /// /// The target complex property. + /// The annotation target path for the complex property. /// The settings for the current conversion. /// true or false. - private bool ShouldCreateComplexPropertyPaths(IEdmStructuralProperty complexProperty, OpenApiConvertSettings convertSettings) + private bool ShouldCreateComplexPropertyPaths(IEdmStructuralProperty complexProperty, string targetPath, OpenApiConvertSettings convertSettings) { Utils.CheckArgumentNull(complexProperty, nameof(complexProperty)); Utils.CheckArgumentNull(convertSettings, nameof(convertSettings)); @@ -417,9 +428,15 @@ private bool ShouldCreateComplexPropertyPaths(IEdmStructuralProperty complexProp if (!convertSettings.RequireRestrictionAnnotationsToGenerateComplexPropertyPaths) return true; - bool isReadable = _model.GetRecord(complexProperty, CapabilitiesConstants.ReadRestrictions)?.Readable ?? false; - bool isUpdatable = _model.GetRecord(complexProperty, CapabilitiesConstants.UpdateRestrictions)?.Updatable ?? false; - bool isInsertable = _model.GetRecord(complexProperty, CapabilitiesConstants.InsertRestrictions)?.Insertable ?? false; + bool isReadable = _model.GetRecord(targetPath, CapabilitiesConstants.ReadRestrictions)?.Readable + ?? _model.GetRecord(complexProperty, CapabilitiesConstants.ReadRestrictions)?.Readable + ?? false; + bool isUpdatable = _model.GetRecord(targetPath, CapabilitiesConstants.UpdateRestrictions)?.Updatable + ??_model.GetRecord(complexProperty, CapabilitiesConstants.UpdateRestrictions)?.Updatable + ?? false; + bool isInsertable = _model.GetRecord(targetPath, CapabilitiesConstants.InsertRestrictions)?.Insertable + ?? _model.GetRecord(complexProperty, CapabilitiesConstants.InsertRestrictions)?.Insertable + ?? false; return isReadable || isUpdatable || isInsertable; } @@ -525,9 +542,13 @@ private void RetrieveNavigationPropertyPaths( AppendPath(currentPath.Clone()); visitedNavigationProperties.Push(navPropFullyQualifiedName); + // For fetching annotations + var targetPath = currentPath.GetTargetPath(_model); + // Check whether a collection-valued navigation property should be indexed by key value(s). // Find indexability annotation annotated directly via NavigationPropertyRestriction. - bool? annotatedIndexability = _model.GetBoolean(navigationProperty, CapabilitiesConstants.IndexableByKey); + bool? annotatedIndexability = _model.GetBoolean(targetPath, CapabilitiesConstants.IndexableByKey) + ?? _model.GetBoolean(navigationProperty, CapabilitiesConstants.IndexableByKey); bool indexableByKey = true; if (restriction?.IndexableByKey != null) @@ -550,7 +571,8 @@ private void RetrieveNavigationPropertyPaths( if (count == null) { // First, get the directly annotated restriction annotation of the navigation property - count = _model.GetRecord(navigationProperty, CapabilitiesConstants.CountRestrictions); + count = _model.GetRecord(targetPath, CapabilitiesConstants.CountRestrictions) + ?? _model.GetRecord(navigationProperty, CapabilitiesConstants.CountRestrictions); createCountPath = count?.Countable; } diff --git a/src/Microsoft.OpenApi.OData.Reader/Generator/OpenApiParameterGenerator.cs b/src/Microsoft.OpenApi.OData.Reader/Generator/OpenApiParameterGenerator.cs index 439dc631..39e152c5 100644 --- a/src/Microsoft.OpenApi.OData.Reader/Generator/OpenApiParameterGenerator.cs +++ b/src/Microsoft.OpenApi.OData.Reader/Generator/OpenApiParameterGenerator.cs @@ -455,7 +455,6 @@ public static OpenApiParameter CreateSearch(this ODataContext context, IEdmVocab return null; } - /// /// Create the $search parameter for Edm target path. /// @@ -558,6 +557,18 @@ public static OpenApiParameter CreateFilter(this ODataContext context, string ta return context.CreateFilter(target); } + public static OpenApiParameter CreateOrderBy(this ODataContext context, string targetPath, IEdmEntityType entityType) + { + Utils.CheckArgumentNull(context, nameof(context)); + Utils.CheckArgumentNull(targetPath, nameof(targetPath)); + + IEdmTargetPath target = context.Model.GetTargetPath(targetPath); + if (target == null) + return null; + + return context.CreateOrderBy(target, entityType); + } + public static OpenApiParameter CreateOrderBy(this ODataContext context, IEdmEntitySet entitySet) { Utils.CheckArgumentNull(context, nameof(context)); @@ -592,7 +603,17 @@ public static OpenApiParameter CreateOrderBy(this ODataContext context, IEdmNavi public static OpenApiParameter CreateOrderBy(this ODataContext context, IEdmVocabularyAnnotatable target, IEdmEntityType entityType) {// patchwork to avoid breaking changes return context.CreateOrderBy(target, entityType as IEdmStructuredType); + } + + public static OpenApiParameter CreateOrderBy(this ODataContext context, string targetPath, IEdmStructuredType structuredType) + { + IEdmTargetPath target = context.Model.GetTargetPath(targetPath); + if (target == null) + return null; + + return context.CreateOrderBy(target, structuredType); } + public static OpenApiParameter CreateOrderBy(this ODataContext context, IEdmVocabularyAnnotatable target, IEdmStructuredType structuredType) { Utils.CheckArgumentNull(context, nameof(context)); @@ -653,6 +674,18 @@ public static OpenApiParameter CreateOrderBy(this ODataContext context, IEdmVoca }; } + public static OpenApiParameter CreateSelect(this ODataContext context, string targetPath, IEdmEntityType entityType) + { + Utils.CheckArgumentNull(context, nameof(context)); + Utils.CheckArgumentNull(targetPath, nameof(targetPath)); + + IEdmTargetPath target = context.Model.GetTargetPath(targetPath); + if (target == null) + return null; + + return context.CreateSelect(target, entityType); + } + public static OpenApiParameter CreateSelect(this ODataContext context, IEdmEntitySet entitySet) { Utils.CheckArgumentNull(context, nameof(context)); @@ -688,6 +721,16 @@ public static OpenApiParameter CreateSelect(this ODataContext context, IEdmVocab { // patchwork to avoid breaking changes return context.CreateSelect(target, entityType as IEdmStructuredType); } + + public static OpenApiParameter CreateSelect(this ODataContext context, string targetPath, IEdmStructuredType structuredType) + { + IEdmTargetPath target = context.Model.GetTargetPath(targetPath); + if (target == null) + return null; + + return context.CreateSelect(target, structuredType); + } + public static OpenApiParameter CreateSelect(this ODataContext context, IEdmVocabularyAnnotatable target, IEdmStructuredType structuredType) { Utils.CheckArgumentNull(context, nameof(context)); @@ -736,6 +779,19 @@ public static OpenApiParameter CreateSelect(this ODataContext context, IEdmVocab Explode = false }; } + + public static OpenApiParameter CreateExpand(this ODataContext context, string targetPath, IEdmEntityType entityType) + { + Utils.CheckArgumentNull(context, nameof(context)); + Utils.CheckArgumentNull(targetPath, nameof(targetPath)); + + IEdmTargetPath target = context.Model.GetTargetPath(targetPath); + if (target == null) + return null; + + return context.CreateExpand(target, entityType); + } + public static OpenApiParameter CreateExpand(this ODataContext context, IEdmEntitySet entitySet) { Utils.CheckArgumentNull(context, nameof(context)); @@ -771,6 +827,16 @@ public static OpenApiParameter CreateExpand(this ODataContext context, IEdmVocab { // patchwork to avoid breaking changes return context.CreateExpand(target, entityType as IEdmStructuredType); } + + public static OpenApiParameter CreateExpand(this ODataContext context, string targetPath, IEdmStructuredType structuredType) + { + IEdmTargetPath target = context.Model.GetTargetPath(targetPath); + if (target == null) + return null; + + return context.CreateExpand(target, structuredType); + } + public static OpenApiParameter CreateExpand(this ODataContext context, IEdmVocabularyAnnotatable target, IEdmStructuredType structuredType) { Utils.CheckArgumentNull(context, nameof(context)); diff --git a/src/Microsoft.OpenApi.OData.Reader/Microsoft.OpenAPI.OData.Reader.csproj b/src/Microsoft.OpenApi.OData.Reader/Microsoft.OpenAPI.OData.Reader.csproj index 1a83e5b0..f0cf95ca 100644 --- a/src/Microsoft.OpenApi.OData.Reader/Microsoft.OpenAPI.OData.Reader.csproj +++ b/src/Microsoft.OpenApi.OData.Reader/Microsoft.OpenAPI.OData.Reader.csproj @@ -15,13 +15,13 @@ netstandard2.0 Microsoft.OpenApi.OData true - 1.6.4 + 1.6.5 This package contains the codes you need to convert OData CSDL to Open API Document of Model. © Microsoft Corporation. All rights reserved. Microsoft OpenApi OData EDM https://github.com/Microsoft/OpenAPI.NET.OData - - Add support for Edm.Untyped #511 + - Use annotations with path as target to dermine whether to add an operation #535 Microsoft.OpenApi.OData.Reader ..\..\tool\Microsoft.OpenApi.OData.snk diff --git a/src/Microsoft.OpenApi.OData.Reader/Operation/ComplexPropertyGetOperationHandler.cs b/src/Microsoft.OpenApi.OData.Reader/Operation/ComplexPropertyGetOperationHandler.cs index 00db6ab1..42a163e2 100644 --- a/src/Microsoft.OpenApi.OData.Reader/Operation/ComplexPropertyGetOperationHandler.cs +++ b/src/Microsoft.OpenApi.OData.Reader/Operation/ComplexPropertyGetOperationHandler.cs @@ -28,15 +28,8 @@ protected override void Initialize(ODataContext context, ODataPath path) _readRestrictions = Context.Model.GetRecord(TargetPath, CapabilitiesConstants.ReadRestrictions); var complexPropertyReadRestrictions = Context.Model.GetRecord(ComplexPropertySegment.Property, CapabilitiesConstants.ReadRestrictions); - - if (_readRestrictions == null) - { - _readRestrictions = complexPropertyReadRestrictions; - } - else - { - _readRestrictions.MergePropertiesIfNull(complexPropertyReadRestrictions); - } + _readRestrictions?.MergePropertiesIfNull(complexPropertyReadRestrictions); + _readRestrictions ??= complexPropertyReadRestrictions; } /// @@ -108,7 +101,8 @@ protected override void SetParameters(OpenApiOperation operation) // of just providing a comma-separated list of properties can be expressed via an array-valued // parameter with an enum constraint // $order - parameter = Context.CreateOrderBy(ComplexPropertySegment.Property, ComplexPropertySegment.ComplexType); + parameter = Context.CreateOrderBy(TargetPath, ComplexPropertySegment.ComplexType) + ?? Context.CreateOrderBy(ComplexPropertySegment.Property, ComplexPropertySegment.ComplexType); if (parameter != null) { operation.Parameters.Add(parameter); @@ -116,14 +110,16 @@ protected override void SetParameters(OpenApiOperation operation) } // $select - parameter = Context.CreateSelect(ComplexPropertySegment.Property, ComplexPropertySegment.ComplexType); + parameter = Context.CreateSelect(TargetPath, ComplexPropertySegment.ComplexType) + ?? Context.CreateSelect(ComplexPropertySegment.Property, ComplexPropertySegment.ComplexType); if (parameter != null) { operation.Parameters.Add(parameter); } // $expand - parameter = Context.CreateExpand(ComplexPropertySegment.Property, ComplexPropertySegment.ComplexType); + parameter = Context.CreateExpand(TargetPath, ComplexPropertySegment.ComplexType) + ?? Context.CreateExpand(ComplexPropertySegment.Property, ComplexPropertySegment.ComplexType); if (parameter != null) { operation.Parameters.Add(parameter); diff --git a/src/Microsoft.OpenApi.OData.Reader/Operation/ComplexPropertyPostOperationHandler.cs b/src/Microsoft.OpenApi.OData.Reader/Operation/ComplexPropertyPostOperationHandler.cs index 4efb8d23..50b58e12 100644 --- a/src/Microsoft.OpenApi.OData.Reader/Operation/ComplexPropertyPostOperationHandler.cs +++ b/src/Microsoft.OpenApi.OData.Reader/Operation/ComplexPropertyPostOperationHandler.cs @@ -28,15 +28,8 @@ protected override void Initialize(ODataContext context, ODataPath path) _insertRestrictions = Context.Model.GetRecord(TargetPath, CapabilitiesConstants.InsertRestrictions); var complexPropertyInsertRestrictions = Context.Model.GetRecord(ComplexPropertySegment.Property, CapabilitiesConstants.InsertRestrictions); - - if (_insertRestrictions == null) - { - _insertRestrictions = complexPropertyInsertRestrictions; - } - else - { - _insertRestrictions.MergePropertiesIfNull(complexPropertyInsertRestrictions); - } + _insertRestrictions?.MergePropertiesIfNull(complexPropertyInsertRestrictions); + _insertRestrictions ??= complexPropertyInsertRestrictions; } /// public override OperationType OperationType => OperationType.Post; diff --git a/src/Microsoft.OpenApi.OData.Reader/Operation/ComplexPropertyUpdateOperationHandler.cs b/src/Microsoft.OpenApi.OData.Reader/Operation/ComplexPropertyUpdateOperationHandler.cs index 8830276d..d3790baa 100644 --- a/src/Microsoft.OpenApi.OData.Reader/Operation/ComplexPropertyUpdateOperationHandler.cs +++ b/src/Microsoft.OpenApi.OData.Reader/Operation/ComplexPropertyUpdateOperationHandler.cs @@ -25,15 +25,8 @@ protected override void Initialize(ODataContext context, ODataPath path) _updateRestrictions = Context.Model.GetRecord(TargetPath, CapabilitiesConstants.UpdateRestrictions); var complexPropertyUpdateRestrictions = Context.Model.GetRecord(ComplexPropertySegment.Property, CapabilitiesConstants.UpdateRestrictions); - - if (_updateRestrictions == null) - { - _updateRestrictions = complexPropertyUpdateRestrictions; - } - else - { - _updateRestrictions.MergePropertiesIfNull(complexPropertyUpdateRestrictions); - } + _updateRestrictions?.MergePropertiesIfNull(complexPropertyUpdateRestrictions); + _updateRestrictions ??= complexPropertyUpdateRestrictions; } /// diff --git a/src/Microsoft.OpenApi.OData.Reader/Operation/DollarCountGetOperationHandler.cs b/src/Microsoft.OpenApi.OData.Reader/Operation/DollarCountGetOperationHandler.cs index 1bf4551d..74082736 100644 --- a/src/Microsoft.OpenApi.OData.Reader/Operation/DollarCountGetOperationHandler.cs +++ b/src/Microsoft.OpenApi.OData.Reader/Operation/DollarCountGetOperationHandler.cs @@ -201,15 +201,8 @@ protected override void AppendCustomParameters(OpenApiOperation operation) ReadRestrictionsType readRestrictions = Context.Model.GetRecord(TargetPath, CapabilitiesConstants.ReadRestrictions); ReadRestrictionsType annotatableReadRestrictions = Context.Model.GetRecord(annotatable, CapabilitiesConstants.ReadRestrictions); - - if (readRestrictions == null) - { - readRestrictions = annotatableReadRestrictions; - } - else - { - readRestrictions.MergePropertiesIfNull(annotatableReadRestrictions); - } + readRestrictions?.MergePropertiesIfNull(annotatableReadRestrictions); + readRestrictions ??= annotatableReadRestrictions; if (readRestrictions == null) { diff --git a/src/Microsoft.OpenApi.OData.Reader/Operation/EdmActionOperationHandler.cs b/src/Microsoft.OpenApi.OData.Reader/Operation/EdmActionOperationHandler.cs index 204642de..e099e371 100644 --- a/src/Microsoft.OpenApi.OData.Reader/Operation/EdmActionOperationHandler.cs +++ b/src/Microsoft.OpenApi.OData.Reader/Operation/EdmActionOperationHandler.cs @@ -28,15 +28,8 @@ protected override void SetBasicInfo(OpenApiOperation operation) InsertRestrictionsType insertRestrictions = Context.Model.GetRecord(TargetPath, CapabilitiesConstants.InsertRestrictions); InsertRestrictionsType operationReadRestrictions = Context.Model.GetRecord(EdmOperation, CapabilitiesConstants.InsertRestrictions); - - if (insertRestrictions == null) - { - insertRestrictions = operationReadRestrictions; - } - else - { - insertRestrictions.MergePropertiesIfNull(operationReadRestrictions); - } + insertRestrictions?.MergePropertiesIfNull(operationReadRestrictions); + insertRestrictions ??= operationReadRestrictions; // Description if (!string.IsNullOrWhiteSpace(insertRestrictions?.LongDescription)) diff --git a/src/Microsoft.OpenApi.OData.Reader/Operation/EdmFunctionOperationHandler.cs b/src/Microsoft.OpenApi.OData.Reader/Operation/EdmFunctionOperationHandler.cs index 85a8f3b1..60184c8f 100644 --- a/src/Microsoft.OpenApi.OData.Reader/Operation/EdmFunctionOperationHandler.cs +++ b/src/Microsoft.OpenApi.OData.Reader/Operation/EdmFunctionOperationHandler.cs @@ -32,15 +32,8 @@ protected override void SetBasicInfo(OpenApiOperation operation) ReadRestrictionsType readRestrictions = Context.Model.GetRecord(TargetPath, CapabilitiesConstants.ReadRestrictions); ReadRestrictionsType operationReadRestrictions = Context.Model.GetRecord(EdmOperation, CapabilitiesConstants.ReadRestrictions); - - if (readRestrictions == null) - { - readRestrictions = operationReadRestrictions; - } - else - { - readRestrictions.MergePropertiesIfNull(operationReadRestrictions); - } + readRestrictions?.MergePropertiesIfNull(operationReadRestrictions); + readRestrictions ??= operationReadRestrictions; // Description if (!string.IsNullOrWhiteSpace(readRestrictions?.LongDescription)) diff --git a/src/Microsoft.OpenApi.OData.Reader/Operation/EdmOperationOperationHandler.cs b/src/Microsoft.OpenApi.OData.Reader/Operation/EdmOperationOperationHandler.cs index 0db40d2c..72ebb225 100644 --- a/src/Microsoft.OpenApi.OData.Reader/Operation/EdmOperationOperationHandler.cs +++ b/src/Microsoft.OpenApi.OData.Reader/Operation/EdmOperationOperationHandler.cs @@ -59,15 +59,8 @@ protected override void Initialize(ODataContext context, ODataPath path) _operationRestriction = Context.Model.GetRecord(TargetPath, CapabilitiesConstants.OperationRestrictions); var operationRestrictions = Context.Model.GetRecord(EdmOperation, CapabilitiesConstants.OperationRestrictions); - - if (_operationRestriction == null) - { - _operationRestriction = operationRestrictions; - } - else - { - _operationRestriction.MergePropertiesIfNull(operationRestrictions); - } + _operationRestriction?.MergePropertiesIfNull(operationRestrictions); + _operationRestriction ??= operationRestrictions; } /// diff --git a/src/Microsoft.OpenApi.OData.Reader/Operation/EntityDeleteOperationHandler.cs b/src/Microsoft.OpenApi.OData.Reader/Operation/EntityDeleteOperationHandler.cs index db909513..b50b78f1 100644 --- a/src/Microsoft.OpenApi.OData.Reader/Operation/EntityDeleteOperationHandler.cs +++ b/src/Microsoft.OpenApi.OData.Reader/Operation/EntityDeleteOperationHandler.cs @@ -31,15 +31,8 @@ protected override void Initialize(ODataContext context, ODataPath path) _deleteRestrictions = Context.Model.GetRecord(TargetPath, CapabilitiesConstants.DeleteRestrictions); var entityDeleteRestrictions = Context.Model.GetRecord(EntitySet, CapabilitiesConstants.DeleteRestrictions); - - if (_deleteRestrictions == null) - { - _deleteRestrictions = entityDeleteRestrictions; - } - else - { - _deleteRestrictions.MergePropertiesIfNull(entityDeleteRestrictions); - } + _deleteRestrictions?.MergePropertiesIfNull(entityDeleteRestrictions); + _deleteRestrictions ??= entityDeleteRestrictions; } /// diff --git a/src/Microsoft.OpenApi.OData.Reader/Operation/EntityGetOperationHandler.cs b/src/Microsoft.OpenApi.OData.Reader/Operation/EntityGetOperationHandler.cs index 75af3544..9a0cfd23 100644 --- a/src/Microsoft.OpenApi.OData.Reader/Operation/EntityGetOperationHandler.cs +++ b/src/Microsoft.OpenApi.OData.Reader/Operation/EntityGetOperationHandler.cs @@ -32,15 +32,8 @@ protected override void Initialize(ODataContext context, ODataPath path) _readRestrictions = Context.Model.GetRecord(TargetPath, CapabilitiesConstants.ReadRestrictions); var entityReadRestrictions = Context.Model.GetRecord(EntitySet, CapabilitiesConstants.ReadRestrictions); - - if (_readRestrictions == null) - { - _readRestrictions = entityReadRestrictions; - } - else - { - _readRestrictions.MergePropertiesIfNull(entityReadRestrictions); - } + _readRestrictions?.MergePropertiesIfNull(entityReadRestrictions); + _readRestrictions ??= entityReadRestrictions; } /// diff --git a/src/Microsoft.OpenApi.OData.Reader/Operation/EntitySetGetOperationHandler.cs b/src/Microsoft.OpenApi.OData.Reader/Operation/EntitySetGetOperationHandler.cs index cb971654..de4f9d30 100644 --- a/src/Microsoft.OpenApi.OData.Reader/Operation/EntitySetGetOperationHandler.cs +++ b/src/Microsoft.OpenApi.OData.Reader/Operation/EntitySetGetOperationHandler.cs @@ -33,15 +33,8 @@ protected override void Initialize(ODataContext context, ODataPath path) _readRestrictions = Context.Model.GetRecord(TargetPath, CapabilitiesConstants.ReadRestrictions); var entityReadRestrictions = Context.Model.GetRecord(EntitySet, CapabilitiesConstants.ReadRestrictions); - - if (_readRestrictions == null) - { - _readRestrictions = entityReadRestrictions; - } - else - { - _readRestrictions.MergePropertiesIfNull(entityReadRestrictions); - } + _readRestrictions?.MergePropertiesIfNull(entityReadRestrictions); + _readRestrictions ??= entityReadRestrictions; } /// @@ -124,21 +117,21 @@ protected override void SetParameters(OpenApiOperation operation) // of just providing a comma-separated list of properties can be expressed via an array-valued // parameter with an enum constraint // $order - parameter = Context.CreateOrderBy(EntitySet); + parameter = Context.CreateOrderBy(TargetPath, EntitySet.EntityType()) ?? Context.CreateOrderBy(EntitySet); if (parameter != null) { operation.Parameters.Add(parameter); } // $select - parameter = Context.CreateSelect(EntitySet); + parameter = Context.CreateSelect(TargetPath, EntitySet.EntityType()) ?? Context.CreateSelect(EntitySet); if (parameter != null) { operation.Parameters.Add(parameter); } // $expand - parameter = Context.CreateExpand(EntitySet); + parameter = Context.CreateExpand(TargetPath, EntitySet.EntityType()) ?? Context.CreateExpand(EntitySet); if (parameter != null) { operation.Parameters.Add(parameter); diff --git a/src/Microsoft.OpenApi.OData.Reader/Operation/EntitySetPostOperationHandler.cs b/src/Microsoft.OpenApi.OData.Reader/Operation/EntitySetPostOperationHandler.cs index 28cb40d7..aabe1057 100644 --- a/src/Microsoft.OpenApi.OData.Reader/Operation/EntitySetPostOperationHandler.cs +++ b/src/Microsoft.OpenApi.OData.Reader/Operation/EntitySetPostOperationHandler.cs @@ -33,15 +33,8 @@ protected override void Initialize(ODataContext context, ODataPath path) _insertRestrictions = Context.Model.GetRecord(TargetPath, CapabilitiesConstants.InsertRestrictions); var entityInsertRestrictions = Context.Model.GetRecord(EntitySet, CapabilitiesConstants.InsertRestrictions); - - if (_insertRestrictions == null) - { - _insertRestrictions = entityInsertRestrictions; - } - else - { - _insertRestrictions.MergePropertiesIfNull(entityInsertRestrictions); - } + _insertRestrictions?.MergePropertiesIfNull(entityInsertRestrictions); + _insertRestrictions ??= entityInsertRestrictions; } /// diff --git a/src/Microsoft.OpenApi.OData.Reader/Operation/EntityUpdateOperationHandler.cs b/src/Microsoft.OpenApi.OData.Reader/Operation/EntityUpdateOperationHandler.cs index fa30d859..7ce526df 100644 --- a/src/Microsoft.OpenApi.OData.Reader/Operation/EntityUpdateOperationHandler.cs +++ b/src/Microsoft.OpenApi.OData.Reader/Operation/EntityUpdateOperationHandler.cs @@ -27,15 +27,8 @@ protected override void Initialize(ODataContext context, ODataPath path) _updateRestrictions = Context.Model.GetRecord(TargetPath, CapabilitiesConstants.UpdateRestrictions); var entityUpdateRestrictions = Context.Model.GetRecord(EntitySet, CapabilitiesConstants.UpdateRestrictions); - - if (_updateRestrictions == null) - { - _updateRestrictions = entityUpdateRestrictions; - } - else - { - _updateRestrictions.MergePropertiesIfNull(entityUpdateRestrictions); - } + _updateRestrictions?.MergePropertiesIfNull(entityUpdateRestrictions); + _updateRestrictions ??= entityUpdateRestrictions; } /// diff --git a/src/Microsoft.OpenApi.OData.Reader/Operation/MediaEntityDeleteOperationHandler.cs b/src/Microsoft.OpenApi.OData.Reader/Operation/MediaEntityDeleteOperationHandler.cs index d8b18ac7..54e712a2 100644 --- a/src/Microsoft.OpenApi.OData.Reader/Operation/MediaEntityDeleteOperationHandler.cs +++ b/src/Microsoft.OpenApi.OData.Reader/Operation/MediaEntityDeleteOperationHandler.cs @@ -26,19 +26,13 @@ protected override void Initialize(ODataContext context, ODataPath path) { var navigationDeleteRestrictions = Context.Model.GetRecord(Property, CapabilitiesConstants.NavigationRestrictions)? .RestrictedProperties?.FirstOrDefault()?.DeleteRestrictions; - if (_deleteRestrictions != null && navigationDeleteRestrictions != null) - { - _deleteRestrictions.MergePropertiesIfNull(navigationDeleteRestrictions); - } + _deleteRestrictions?.MergePropertiesIfNull(navigationDeleteRestrictions); _deleteRestrictions ??= navigationDeleteRestrictions; } else { var propertyDeleteRestrictions = Context.Model.GetRecord(Property, CapabilitiesConstants.DeleteRestrictions); - if (_deleteRestrictions != null && propertyDeleteRestrictions != null) - { - _deleteRestrictions.MergePropertiesIfNull(propertyDeleteRestrictions); - } + _deleteRestrictions?.MergePropertiesIfNull(propertyDeleteRestrictions); _deleteRestrictions ??= propertyDeleteRestrictions; } } diff --git a/src/Microsoft.OpenApi.OData.Reader/Operation/MediaEntityGetOperationHandler.cs b/src/Microsoft.OpenApi.OData.Reader/Operation/MediaEntityGetOperationHandler.cs index a0cd1951..125141e9 100644 --- a/src/Microsoft.OpenApi.OData.Reader/Operation/MediaEntityGetOperationHandler.cs +++ b/src/Microsoft.OpenApi.OData.Reader/Operation/MediaEntityGetOperationHandler.cs @@ -34,19 +34,13 @@ protected override void Initialize(ODataContext context, ODataPath path) { var navigationReadRestrictions = Context.Model.GetRecord(Property, CapabilitiesConstants.NavigationRestrictions)? .RestrictedProperties?.FirstOrDefault()?.ReadRestrictions; - if (_readRestrictions != null && navigationReadRestrictions != null) - { - _readRestrictions.MergePropertiesIfNull(navigationReadRestrictions); - } + _readRestrictions?.MergePropertiesIfNull(navigationReadRestrictions); _readRestrictions ??= navigationReadRestrictions; } else { var propertyReadRestrictions = Context.Model.GetRecord(Property, CapabilitiesConstants.ReadRestrictions); - if (_readRestrictions != null && propertyReadRestrictions != null) - { - _readRestrictions.MergePropertiesIfNull(propertyReadRestrictions); - } + _readRestrictions?.MergePropertiesIfNull(propertyReadRestrictions); _readRestrictions ??= propertyReadRestrictions; } } diff --git a/src/Microsoft.OpenApi.OData.Reader/Operation/MediaEntityPutOperationHandler.cs b/src/Microsoft.OpenApi.OData.Reader/Operation/MediaEntityPutOperationHandler.cs index 722f06ac..20f810b8 100644 --- a/src/Microsoft.OpenApi.OData.Reader/Operation/MediaEntityPutOperationHandler.cs +++ b/src/Microsoft.OpenApi.OData.Reader/Operation/MediaEntityPutOperationHandler.cs @@ -36,19 +36,13 @@ protected override void Initialize(ODataContext context, ODataPath path) { var navigationUpdateRestrictions = Context.Model.GetRecord(Property, CapabilitiesConstants.NavigationRestrictions)? .RestrictedProperties?.FirstOrDefault()?.UpdateRestrictions; - if (_updateRestrictions != null && navigationUpdateRestrictions != null) - { - _updateRestrictions.MergePropertiesIfNull(navigationUpdateRestrictions); - } + _updateRestrictions?.MergePropertiesIfNull(navigationUpdateRestrictions); _updateRestrictions ??= navigationUpdateRestrictions; } else { var propertyUpdateRestrictions = Context.Model.GetRecord(Property, CapabilitiesConstants.UpdateRestrictions); - if (_updateRestrictions != null && propertyUpdateRestrictions != null) - { - _updateRestrictions.MergePropertiesIfNull(propertyUpdateRestrictions); - } + _updateRestrictions?.MergePropertiesIfNull(propertyUpdateRestrictions); _updateRestrictions ??= propertyUpdateRestrictions; } } diff --git a/src/Microsoft.OpenApi.OData.Reader/Operation/NavigationPropertyGetOperationHandler.cs b/src/Microsoft.OpenApi.OData.Reader/Operation/NavigationPropertyGetOperationHandler.cs index 22e6cb03..fd94a4f5 100644 --- a/src/Microsoft.OpenApi.OData.Reader/Operation/NavigationPropertyGetOperationHandler.cs +++ b/src/Microsoft.OpenApi.OData.Reader/Operation/NavigationPropertyGetOperationHandler.cs @@ -165,6 +165,12 @@ protected override void SetParameters(OpenApiOperation operation) { base.SetParameters(operation); + OpenApiParameter selectParameter = Context.CreateSelect(TargetPath, NavigationProperty.ToEntityType()) + ?? Context.CreateSelect(NavigationProperty); + + OpenApiParameter expandParameter = Context.CreateExpand(TargetPath, NavigationProperty.ToEntityType()) + ?? Context.CreateExpand(NavigationProperty); + if (!LastSegmentIsKeySegment && NavigationProperty.TargetMultiplicity() == EdmMultiplicity.Many) { // Need to verify that TopSupported or others should be applied to navigation source. @@ -199,36 +205,32 @@ protected override void SetParameters(OpenApiOperation operation) operation.Parameters.Add(parameter); } - parameter = Context.CreateOrderBy(NavigationProperty); + parameter = Context.CreateOrderBy(TargetPath, NavigationProperty.ToEntityType()) ?? Context.CreateOrderBy(NavigationProperty); if (parameter != null) { operation.Parameters.Add(parameter); } - parameter = Context.CreateSelect(NavigationProperty); - if (parameter != null) + if (selectParameter != null) { - operation.Parameters.Add(parameter); + operation.Parameters.Add(selectParameter); } - parameter = Context.CreateExpand(NavigationProperty); - if (parameter != null) + if (expandParameter != null) { - operation.Parameters.Add(parameter); + operation.Parameters.Add(expandParameter); } } else { - OpenApiParameter parameter = Context.CreateSelect(NavigationProperty); - if (parameter != null) + if (selectParameter != null) { - operation.Parameters.Add(parameter); + operation.Parameters.Add(selectParameter); } - parameter = Context.CreateExpand(NavigationProperty); - if (parameter != null) + if (expandParameter != null) { - operation.Parameters.Add(parameter); + operation.Parameters.Add(expandParameter); } } } diff --git a/src/Microsoft.OpenApi.OData.Reader/Operation/NavigationPropertyOperationHandler.cs b/src/Microsoft.OpenApi.OData.Reader/Operation/NavigationPropertyOperationHandler.cs index d7eb990a..8bdbacc7 100644 --- a/src/Microsoft.OpenApi.OData.Reader/Operation/NavigationPropertyOperationHandler.cs +++ b/src/Microsoft.OpenApi.OData.Reader/Operation/NavigationPropertyOperationHandler.cs @@ -141,65 +141,41 @@ protected IRecord GetRestrictionAnnotation(string annotationTerm) { case CapabilitiesConstants.ReadRestrictions: var readRestrictions = Context.Model.GetRecord(TargetPath, CapabilitiesConstants.ReadRestrictions); - if (readRestrictions != null && Restriction?.ReadRestrictions != null) - { - readRestrictions.MergePropertiesIfNull(Restriction.ReadRestrictions); - } + readRestrictions?.MergePropertiesIfNull(Restriction?.ReadRestrictions); readRestrictions ??= Restriction?.ReadRestrictions; var navPropReadRestrictions = Context.Model.GetRecord(NavigationProperty, CapabilitiesConstants.ReadRestrictions); - if (readRestrictions != null && navPropReadRestrictions != null) - { - readRestrictions.MergePropertiesIfNull(navPropReadRestrictions); - } + readRestrictions?.MergePropertiesIfNull(navPropReadRestrictions); readRestrictions ??= navPropReadRestrictions; return readRestrictions; case CapabilitiesConstants.UpdateRestrictions: var updateRestrictions = Context.Model.GetRecord(TargetPath, CapabilitiesConstants.UpdateRestrictions); - if (updateRestrictions != null && Restriction?.UpdateRestrictions != null) - { - updateRestrictions.MergePropertiesIfNull(Restriction.UpdateRestrictions); - } + updateRestrictions?.MergePropertiesIfNull(Restriction?.UpdateRestrictions); updateRestrictions ??= Restriction?.UpdateRestrictions; var navPropUpdateRestrictions = Context.Model.GetRecord(NavigationProperty, CapabilitiesConstants.UpdateRestrictions); - if (updateRestrictions != null && navPropUpdateRestrictions != null) - { - updateRestrictions.MergePropertiesIfNull(navPropUpdateRestrictions); - } + updateRestrictions?.MergePropertiesIfNull(navPropUpdateRestrictions); updateRestrictions ??= navPropUpdateRestrictions; return updateRestrictions; case CapabilitiesConstants.InsertRestrictions: var insertRestrictions = Context.Model.GetRecord(TargetPath, CapabilitiesConstants.InsertRestrictions); - if (insertRestrictions != null && Restriction?.InsertRestrictions != null) - { - insertRestrictions.MergePropertiesIfNull(Restriction.InsertRestrictions); - } + insertRestrictions?.MergePropertiesIfNull(Restriction?.InsertRestrictions); insertRestrictions ??= Restriction?.InsertRestrictions; var navPropInsertRestrictions = Context.Model.GetRecord(NavigationProperty, CapabilitiesConstants.InsertRestrictions); - if (insertRestrictions != null && navPropInsertRestrictions != null) - { - insertRestrictions.MergePropertiesIfNull(navPropInsertRestrictions); - } + insertRestrictions?.MergePropertiesIfNull(navPropInsertRestrictions); insertRestrictions ??= navPropInsertRestrictions; return insertRestrictions; case CapabilitiesConstants.DeleteRestrictions: var deleteRestrictions = Context.Model.GetRecord(TargetPath, CapabilitiesConstants.DeleteRestrictions); - if (deleteRestrictions != null && Restriction?.DeleteRestrictions != null) - { - deleteRestrictions.MergePropertiesIfNull(Restriction.DeleteRestrictions); - } + deleteRestrictions?.MergePropertiesIfNull(Restriction?.DeleteRestrictions); deleteRestrictions ??= Restriction?.DeleteRestrictions; var navPropDeleteRestrictions = Context.Model.GetRecord(NavigationProperty, CapabilitiesConstants.DeleteRestrictions); - if (deleteRestrictions != null && navPropDeleteRestrictions != null) - { - deleteRestrictions.MergePropertiesIfNull(navPropDeleteRestrictions); - } + deleteRestrictions?.MergePropertiesIfNull(navPropDeleteRestrictions); deleteRestrictions ??= navPropDeleteRestrictions; return deleteRestrictions; diff --git a/src/Microsoft.OpenApi.OData.Reader/Operation/ODataTypeCastGetOperationHandler.cs b/src/Microsoft.OpenApi.OData.Reader/Operation/ODataTypeCastGetOperationHandler.cs index 1a236ae8..c790dbe1 100644 --- a/src/Microsoft.OpenApi.OData.Reader/Operation/ODataTypeCastGetOperationHandler.cs +++ b/src/Microsoft.OpenApi.OData.Reader/Operation/ODataTypeCastGetOperationHandler.cs @@ -310,8 +310,8 @@ protected override void SetParameters(OpenApiOperation operation) if (IsSingleElement) { new OpenApiParameter[] { - Context.CreateSelect(navigationProperty), - Context.CreateExpand(navigationProperty), + Context.CreateSelect(TargetPath, navigationProperty.ToEntityType()) ?? Context.CreateSelect(navigationProperty), + Context.CreateExpand(TargetPath, navigationProperty.ToEntityType()) ?? Context.CreateExpand(navigationProperty), } .Where(x => x != null) .ToList() @@ -322,9 +322,9 @@ protected override void SetParameters(OpenApiOperation operation) GetParametersForAnnotableOfMany(navigationProperty) .Union( new OpenApiParameter[] { - Context.CreateOrderBy(navigationProperty), - Context.CreateSelect(navigationProperty), - Context.CreateExpand(navigationProperty), + Context.CreateOrderBy(TargetPath, navigationProperty.ToEntityType()) ?? Context.CreateOrderBy(navigationProperty), + Context.CreateSelect(TargetPath, navigationProperty.ToEntityType()) ?? Context.CreateSelect(navigationProperty), + Context.CreateExpand(TargetPath, navigationProperty.ToEntityType()) ?? Context.CreateExpand(navigationProperty), }) .Where(x => x != null) .ToList() @@ -336,8 +336,8 @@ protected override void SetParameters(OpenApiOperation operation) if(IsSingleElement) { new OpenApiParameter[] { - Context.CreateSelect(entitySet), - Context.CreateExpand(entitySet), + Context.CreateSelect(TargetPath, entitySet.EntityType()) ?? Context.CreateSelect(entitySet), + Context.CreateExpand(TargetPath, entitySet.EntityType()) ?? Context.CreateExpand(entitySet), } .Where(x => x != null) .ToList() @@ -348,9 +348,9 @@ protected override void SetParameters(OpenApiOperation operation) GetParametersForAnnotableOfMany(entitySet) .Union( new OpenApiParameter[] { - Context.CreateOrderBy(entitySet), - Context.CreateSelect(entitySet), - Context.CreateExpand(entitySet), + Context.CreateOrderBy(TargetPath, entitySet.EntityType()) ?? Context.CreateOrderBy(entitySet), + Context.CreateSelect(TargetPath, entitySet.EntityType()) ?? Context.CreateSelect(entitySet), + Context.CreateExpand(TargetPath, entitySet.EntityType()) ?? Context.CreateExpand(entitySet), }) .Where(x => x != null) .ToList() @@ -360,8 +360,8 @@ protected override void SetParameters(OpenApiOperation operation) else if(singleton != null) { new OpenApiParameter[] { - Context.CreateSelect(singleton), - Context.CreateExpand(singleton), + Context.CreateSelect(TargetPath, singleton.EntityType()) ?? Context.CreateSelect(singleton), + Context.CreateExpand(TargetPath, singleton.EntityType()) ?? Context.CreateExpand(singleton), } .Where(x => x != null) .ToList() @@ -373,7 +373,7 @@ private IEnumerable GetParametersForAnnotableOfMany(IEdmVocabu // Need to verify that TopSupported or others should be applied to navigation source. // So, how about for the navigation property. return new OpenApiParameter[] { - Context.CreateTop(annotable), + Context.CreateTop(annotable), Context.CreateSkip(annotable), Context.CreateSearch(annotable), Context.CreateFilter(annotable), @@ -413,8 +413,11 @@ protected override void AppendCustomParameters(OpenApiOperation operation) { if (annotatable == null) return; - - ReadRestrictionsType readRestrictions = Context.Model.GetRecord(annotatable, CapabilitiesConstants.ReadRestrictions); + + ReadRestrictionsType readRestrictions = Context.Model.GetRecord(TargetPath, CapabilitiesConstants.ReadRestrictions); + var annotatableReadRestrictions = Context.Model.GetRecord(annotatable, CapabilitiesConstants.ReadRestrictions); + readRestrictions?.MergePropertiesIfNull(annotatableReadRestrictions); + readRestrictions ??= annotatableReadRestrictions; if (readRestrictions == null) { diff --git a/src/Microsoft.OpenApi.OData.Reader/Operation/OperationHandler.cs b/src/Microsoft.OpenApi.OData.Reader/Operation/OperationHandler.cs index d7224780..cc864153 100644 --- a/src/Microsoft.OpenApi.OData.Reader/Operation/OperationHandler.cs +++ b/src/Microsoft.OpenApi.OData.Reader/Operation/OperationHandler.cs @@ -128,7 +128,7 @@ private void SetDeprecation(OpenApiOperation operation) protected virtual void Initialize(ODataContext context, ODataPath path) { SetCustomLinkRelType(); - SetTargetPath(); + TargetPath = path.GetTargetPath(context.Model); } /// @@ -298,21 +298,5 @@ protected virtual void SetCustomLinkRelType() } } - /// - /// Set string representation of the Edm Target Path for annotations - /// - protected virtual void SetTargetPath() - { - var targetPath = new StringBuilder(Context.Model.EntityContainer.FullName()); - - bool skipLastSegment = Path.LastSegment is ODataRefSegment || Path.LastSegment is ODataDollarCountSegment; - foreach (var segment in Path.Segments.Where(segment => segment is not ODataKeySegment - && !(skipLastSegment && segment == Path.LastSegment))) - { - targetPath.Append($"/{segment.Identifier}"); - } - TargetPath = targetPath.ToString(); - } - } } diff --git a/src/Microsoft.OpenApi.OData.Reader/Operation/RefGetOperationHandler.cs b/src/Microsoft.OpenApi.OData.Reader/Operation/RefGetOperationHandler.cs index bff78f2c..bf07dcf7 100644 --- a/src/Microsoft.OpenApi.OData.Reader/Operation/RefGetOperationHandler.cs +++ b/src/Microsoft.OpenApi.OData.Reader/Operation/RefGetOperationHandler.cs @@ -145,39 +145,39 @@ protected override void SetParameters(OpenApiOperation operation) if (NavigationProperty.TargetMultiplicity() == EdmMultiplicity.Many) { - // Need to verify that TopSupported or others should be applyed to navigaiton source. + // Need to verify that TopSupported or others should be applied to navigaiton source. // So, how about for the navigation property. - OpenApiParameter parameter = Context.CreateTop(NavigationProperty); + OpenApiParameter parameter = Context.CreateTop(TargetPath) ?? Context.CreateTop(NavigationProperty); if (parameter != null) { operation.Parameters.Add(parameter); } - parameter = Context.CreateSkip(NavigationProperty); + parameter = Context.CreateSkip(TargetPath) ?? Context.CreateSkip(NavigationProperty); if (parameter != null) { operation.Parameters.Add(parameter); } - parameter = Context.CreateSearch(NavigationProperty); + parameter = Context.CreateSearch(TargetPath) ?? Context.CreateSearch(NavigationProperty); if (parameter != null) { operation.Parameters.Add(parameter); } - parameter = Context.CreateFilter(NavigationProperty); + parameter = Context.CreateFilter(TargetPath) ?? Context.CreateFilter(NavigationProperty); if (parameter != null) { operation.Parameters.Add(parameter); } - parameter = Context.CreateCount(NavigationProperty); + parameter = Context.CreateCount(TargetPath) ?? Context.CreateCount(NavigationProperty); if (parameter != null) { operation.Parameters.Add(parameter); } - parameter = Context.CreateOrderBy(NavigationProperty); + parameter = Context.CreateOrderBy(TargetPath, NavigationProperty.ToEntityType()) ?? Context.CreateOrderBy(NavigationProperty); if (parameter != null) { operation.Parameters.Add(parameter); diff --git a/src/Microsoft.OpenApi.OData.Reader/Operation/SingletonGetOperationHandler.cs b/src/Microsoft.OpenApi.OData.Reader/Operation/SingletonGetOperationHandler.cs index e235ac76..0dc90b9e 100644 --- a/src/Microsoft.OpenApi.OData.Reader/Operation/SingletonGetOperationHandler.cs +++ b/src/Microsoft.OpenApi.OData.Reader/Operation/SingletonGetOperationHandler.cs @@ -32,15 +32,8 @@ protected override void Initialize(ODataContext context, ODataPath path) _readRestrictions = Context.Model.GetRecord(TargetPath, CapabilitiesConstants.ReadRestrictions); var singletonReadRestrictions = Context.Model.GetRecord(Singleton, CapabilitiesConstants.ReadRestrictions); - - if (_readRestrictions == null) - { - _readRestrictions = singletonReadRestrictions; - } - else - { - _readRestrictions.MergePropertiesIfNull(singletonReadRestrictions); - } + _readRestrictions?.MergePropertiesIfNull(singletonReadRestrictions); + _readRestrictions ??= singletonReadRestrictions; } /// diff --git a/src/Microsoft.OpenApi.OData.Reader/Operation/SingletonPatchOperationHandler.cs b/src/Microsoft.OpenApi.OData.Reader/Operation/SingletonPatchOperationHandler.cs index dc74fec3..68e3c11a 100644 --- a/src/Microsoft.OpenApi.OData.Reader/Operation/SingletonPatchOperationHandler.cs +++ b/src/Microsoft.OpenApi.OData.Reader/Operation/SingletonPatchOperationHandler.cs @@ -32,15 +32,8 @@ protected override void Initialize(ODataContext context, ODataPath path) _updateRestrictions = Context.Model.GetRecord(TargetPath, CapabilitiesConstants.UpdateRestrictions); var singletonUpdateRestrictions = Context.Model.GetRecord(Singleton, CapabilitiesConstants.UpdateRestrictions); - - if (_updateRestrictions == null) - { - _updateRestrictions = singletonUpdateRestrictions; - } - else - { - _updateRestrictions.MergePropertiesIfNull(singletonUpdateRestrictions); - } + _updateRestrictions?.MergePropertiesIfNull(singletonUpdateRestrictions); + _updateRestrictions ??= singletonUpdateRestrictions; } /// diff --git a/src/Microsoft.OpenApi.OData.Reader/PathItem/ComplexPropertyItemHandler.cs b/src/Microsoft.OpenApi.OData.Reader/PathItem/ComplexPropertyItemHandler.cs index a0019c19..866dcef3 100644 --- a/src/Microsoft.OpenApi.OData.Reader/PathItem/ComplexPropertyItemHandler.cs +++ b/src/Microsoft.OpenApi.OData.Reader/PathItem/ComplexPropertyItemHandler.cs @@ -24,41 +24,65 @@ internal class ComplexPropertyItemHandler : PathItemHandler /// protected override void SetOperations(OpenApiPathItem item) { - bool isReadable = Context.Model.GetRecord(ComplexProperty, CapabilitiesConstants.ReadRestrictions)?.Readable ?? false; - if ((Context.Settings.RequireRestrictionAnnotationsToGenerateComplexPropertyPaths && isReadable) || - !Context.Settings.RequireRestrictionAnnotationsToGenerateComplexPropertyPaths) + AddReadOperation(item); + AddUpdateOperation(item); + AddInsertOperation(item); + } + + public void AddReadOperation(OpenApiPathItem item) + { + ReadRestrictionsType readRestrictions = Context.Model.GetRecord(TargetPath, CapabilitiesConstants.ReadRestrictions); + ReadRestrictionsType complexTypeReadRestrictions = Context.Model.GetRecord(ComplexProperty, CapabilitiesConstants.ReadRestrictions); + readRestrictions?.MergePropertiesIfNull(complexTypeReadRestrictions); + readRestrictions ??= complexTypeReadRestrictions; + bool isReadable = readRestrictions?.Readable ?? false; + if ((Context.Settings.RequireRestrictionAnnotationsToGenerateComplexPropertyPaths && isReadable) || + !Context.Settings.RequireRestrictionAnnotationsToGenerateComplexPropertyPaths) { - AddOperation(item, OperationType.Get); - } + AddOperation(item, OperationType.Get); + } + } - UpdateRestrictionsType update = Context.Model.GetRecord(ComplexProperty, CapabilitiesConstants.UpdateRestrictions); - bool isUpdatable = update?.Updatable ?? false; - if ((Context.Settings.RequireRestrictionAnnotationsToGenerateComplexPropertyPaths && isUpdatable) || - !Context.Settings.RequireRestrictionAnnotationsToGenerateComplexPropertyPaths) - { - if (update != null && update.IsUpdateMethodPut) - { - AddOperation(item, OperationType.Put); - } - else + public void AddInsertOperation(OpenApiPathItem item) + { + if (Path.LastSegment is ODataComplexPropertySegment segment && segment.Property.Type.IsCollection()) + { + InsertRestrictionsType insertRestrictions = Context.Model.GetRecord(TargetPath, CapabilitiesConstants.InsertRestrictions); + InsertRestrictionsType entityInsertRestrictions = Context.Model.GetRecord(ComplexProperty, CapabilitiesConstants.InsertRestrictions); + insertRestrictions?.MergePropertiesIfNull(entityInsertRestrictions); + insertRestrictions ??= entityInsertRestrictions; + bool isInsertable = insertRestrictions?.Insertable ?? false; + if ((Context.Settings.RequireRestrictionAnnotationsToGenerateComplexPropertyPaths && isInsertable) || + !Context.Settings.RequireRestrictionAnnotationsToGenerateComplexPropertyPaths) { - AddOperation(item, OperationType.Patch); - } - } + AddOperation(item, OperationType.Post); + } + } + } - if (Path.LastSegment is ODataComplexPropertySegment segment && segment.Property.Type.IsCollection()) + public void AddUpdateOperation(OpenApiPathItem item) + { + UpdateRestrictionsType updateRestrictions = Context.Model.GetRecord(TargetPath, CapabilitiesConstants.UpdateRestrictions); + UpdateRestrictionsType complexTypeUpdateRestrictions = Context.Model.GetRecord(ComplexProperty, CapabilitiesConstants.UpdateRestrictions); + updateRestrictions?.MergePropertiesIfNull(complexTypeUpdateRestrictions); + updateRestrictions ??= complexTypeUpdateRestrictions; + bool isUpdatable = updateRestrictions?.Updatable ?? false; + if ((Context.Settings.RequireRestrictionAnnotationsToGenerateComplexPropertyPaths && isUpdatable) || + !Context.Settings.RequireRestrictionAnnotationsToGenerateComplexPropertyPaths) { - bool isInsertable = Context.Model.GetRecord(ComplexProperty, CapabilitiesConstants.InsertRestrictions)?.Insertable ?? false; - if ((Context.Settings.RequireRestrictionAnnotationsToGenerateComplexPropertyPaths && isInsertable) || - !Context.Settings.RequireRestrictionAnnotationsToGenerateComplexPropertyPaths) - { - AddOperation(item, OperationType.Post); - } - } - } + if (updateRestrictions != null && updateRestrictions.IsUpdateMethodPut) + { + AddOperation(item, OperationType.Put); + } + else + { + AddOperation(item, OperationType.Patch); + } + } + } - /// - protected override void Initialize(ODataContext context, ODataPath path) + /// + protected override void Initialize(ODataContext context, ODataPath path) { base.Initialize(context, path); diff --git a/src/Microsoft.OpenApi.OData.Reader/PathItem/EntityPathItemHandler.cs b/src/Microsoft.OpenApi.OData.Reader/PathItem/EntityPathItemHandler.cs index cc428cb8..7c66d7d9 100644 --- a/src/Microsoft.OpenApi.OData.Reader/PathItem/EntityPathItemHandler.cs +++ b/src/Microsoft.OpenApi.OData.Reader/PathItem/EntityPathItemHandler.cs @@ -22,19 +22,25 @@ internal class EntityPathItemHandler : EntitySetPathItemHandler /// protected override void SetOperations(OpenApiPathItem item) { - ReadRestrictionsType read = Context.Model.GetRecord(EntitySet); - if (read == null || - (read.ReadByKeyRestrictions == null && read.IsReadable) || - (read.ReadByKeyRestrictions != null && read.ReadByKeyRestrictions.IsReadable)) + ReadRestrictionsType readRestrictions = Context.Model.GetRecord(TargetPath, CapabilitiesConstants.ReadRestrictions); + ReadRestrictionsType entityReadRestrictions = Context.Model.GetRecord(EntitySet, CapabilitiesConstants.ReadRestrictions); + readRestrictions?.MergePropertiesIfNull(entityReadRestrictions); + readRestrictions ??= entityReadRestrictions; + if (readRestrictions == null || + (readRestrictions.ReadByKeyRestrictions == null && readRestrictions.IsReadable) || + (readRestrictions.ReadByKeyRestrictions != null && readRestrictions.ReadByKeyRestrictions.IsReadable)) { // If we don't have Read by key read restriction, we should check the set read restrction. AddOperation(item, OperationType.Get); } - UpdateRestrictionsType update = Context.Model.GetRecord(EntitySet); - if (update == null || update.IsUpdatable) + UpdateRestrictionsType updateRestrictions = Context.Model.GetRecord(TargetPath, CapabilitiesConstants.UpdateRestrictions); + UpdateRestrictionsType entityUpdateRestrictions = Context.Model.GetRecord(EntitySet, CapabilitiesConstants.UpdateRestrictions); + updateRestrictions?.MergePropertiesIfNull(entityUpdateRestrictions); + updateRestrictions ??= entityUpdateRestrictions; + if (updateRestrictions?.IsUpdatable ?? true) { - if (update != null && update.IsUpdateMethodPut) + if (updateRestrictions != null && updateRestrictions.IsUpdateMethodPut) { AddOperation(item, OperationType.Put); } @@ -44,8 +50,11 @@ protected override void SetOperations(OpenApiPathItem item) } } - DeleteRestrictionsType delete = Context.Model.GetRecord(EntitySet); - if (delete == null || delete.IsDeletable) + DeleteRestrictionsType deleteRestrictions = Context.Model.GetRecord(TargetPath, CapabilitiesConstants.DeleteRestrictions); + DeleteRestrictionsType entityDeleteRestrictions = Context.Model.GetRecord(EntitySet, CapabilitiesConstants.DeleteRestrictions); + deleteRestrictions?.MergePropertiesIfNull(entityDeleteRestrictions); + deleteRestrictions ??= entityDeleteRestrictions; + if (deleteRestrictions?.IsDeletable ?? true) { AddOperation(item, OperationType.Delete); } diff --git a/src/Microsoft.OpenApi.OData.Reader/PathItem/EntitySetPathItemHandler.cs b/src/Microsoft.OpenApi.OData.Reader/PathItem/EntitySetPathItemHandler.cs index 29450c37..a6c0019b 100644 --- a/src/Microsoft.OpenApi.OData.Reader/PathItem/EntitySetPathItemHandler.cs +++ b/src/Microsoft.OpenApi.OData.Reader/PathItem/EntitySetPathItemHandler.cs @@ -27,14 +27,20 @@ internal class EntitySetPathItemHandler : PathItemHandler /// protected override void SetOperations(OpenApiPathItem item) { - ReadRestrictionsType read = Context.Model.GetRecord(EntitySet); - if (read == null || read.IsReadable) + ReadRestrictionsType readRestrictions = Context.Model.GetRecord(TargetPath, CapabilitiesConstants.ReadRestrictions); + ReadRestrictionsType entityReadRestrictions = Context.Model.GetRecord(EntitySet, CapabilitiesConstants.ReadRestrictions); + readRestrictions?.MergePropertiesIfNull(entityReadRestrictions); + readRestrictions ??= entityReadRestrictions; + if (readRestrictions?.IsReadable ?? true) { AddOperation(item, OperationType.Get); } - InsertRestrictionsType insert = Context.Model.GetRecord(EntitySet); - if (insert == null || insert.IsInsertable) + InsertRestrictionsType insertRestrictions = Context.Model.GetRecord(TargetPath, CapabilitiesConstants.InsertRestrictions); + InsertRestrictionsType entityInsertRestrictions = Context.Model.GetRecord(EntitySet, CapabilitiesConstants.InsertRestrictions); + insertRestrictions?.MergePropertiesIfNull(entityInsertRestrictions); + insertRestrictions ??= entityInsertRestrictions; + if (insertRestrictions?.IsInsertable ?? true) { AddOperation(item, OperationType.Post); } diff --git a/src/Microsoft.OpenApi.OData.Reader/PathItem/MediaEntityPathItemHandler.cs b/src/Microsoft.OpenApi.OData.Reader/PathItem/MediaEntityPathItemHandler.cs index 1dfd089e..83eeae03 100644 --- a/src/Microsoft.OpenApi.OData.Reader/PathItem/MediaEntityPathItemHandler.cs +++ b/src/Microsoft.OpenApi.OData.Reader/PathItem/MediaEntityPathItemHandler.cs @@ -31,31 +31,34 @@ internal class MediaEntityPathItemHandler : PathItemHandler /// protected override void SetOperations(OpenApiPathItem item) { - ReadRestrictionsType read = EntitySet != null + ReadRestrictionsType readRestrictions = Context.Model.GetRecord(TargetPath, CapabilitiesConstants.ReadRestrictions); + ReadRestrictionsType navSourceReadRestrictions = EntitySet != null ? Context.Model.GetRecord(EntitySet) : Context.Model.GetRecord(Singleton); - - if (read == null || - (read.ReadByKeyRestrictions == null && read.IsReadable) || - (read.ReadByKeyRestrictions != null && read.ReadByKeyRestrictions.IsReadable)) + readRestrictions ??= navSourceReadRestrictions; + if (readRestrictions == null || + (readRestrictions.ReadByKeyRestrictions == null && readRestrictions.IsReadable) || + (readRestrictions.ReadByKeyRestrictions != null && readRestrictions.ReadByKeyRestrictions.IsReadable)) { AddOperation(item, OperationType.Get); } - UpdateRestrictionsType update = EntitySet != null + UpdateRestrictionsType updateRestrictions = Context.Model.GetRecord(TargetPath, CapabilitiesConstants.UpdateRestrictions); + UpdateRestrictionsType navSourceUpdateRestrictions = EntitySet != null ? Context.Model.GetRecord(EntitySet) : Context.Model.GetRecord(Singleton); - - if (update == null || update.IsUpdatable) + updateRestrictions ??= navSourceUpdateRestrictions; + if (updateRestrictions?.IsUpdatable ?? true) { AddOperation(item, OperationType.Put); } - DeleteRestrictionsType delete = EntitySet != null + DeleteRestrictionsType deleteRestrictions = Context.Model.GetRecord(TargetPath, CapabilitiesConstants.DeleteRestrictions); + DeleteRestrictionsType navSourceDeleteRestrictions = EntitySet != null ? Context.Model.GetRecord(EntitySet) : Context.Model.GetRecord(Singleton); - - if (delete == null || delete.IsDeletable) + deleteRestrictions ??= navSourceDeleteRestrictions; + if (deleteRestrictions?.IsDeletable ?? true) { AddOperation(item, OperationType.Delete); } diff --git a/src/Microsoft.OpenApi.OData.Reader/PathItem/NavigationPropertyPathItemHandler.cs b/src/Microsoft.OpenApi.OData.Reader/PathItem/NavigationPropertyPathItemHandler.cs index 37084b61..57b36119 100644 --- a/src/Microsoft.OpenApi.OData.Reader/PathItem/NavigationPropertyPathItemHandler.cs +++ b/src/Microsoft.OpenApi.OData.Reader/PathItem/NavigationPropertyPathItemHandler.cs @@ -54,16 +54,14 @@ protected override void SetOperations(OpenApiPathItem item) { IEdmEntitySet entitySet = NavigationSource as IEdmEntitySet; IEdmVocabularyAnnotatable target = entitySet; - if (target == null) - { - target = NavigationSource as IEdmSingleton; - } + target ??= NavigationSource as IEdmSingleton; + NavigationRestrictionsType targetPathRestrictionType = Context.Model.GetRecord(TargetPath, CapabilitiesConstants.NavigationRestrictions); NavigationRestrictionsType navSourceRestrictionType = Context.Model.GetRecord(target, CapabilitiesConstants.NavigationRestrictions); NavigationRestrictionsType navPropRestrictionType = Context.Model.GetRecord(NavigationProperty, CapabilitiesConstants.NavigationRestrictions); - NavigationPropertyRestriction restriction = navSourceRestrictionType?.RestrictedProperties? - .FirstOrDefault(r => r.NavigationProperty == Path.NavigationPropertyPath()) + NavigationPropertyRestriction restriction = targetPathRestrictionType?.RestrictedProperties?.FirstOrDefault() + ?? navSourceRestrictionType?.RestrictedProperties?.FirstOrDefault(r => r.NavigationProperty == Path.NavigationPropertyPath()) ?? navPropRestrictionType?.RestrictedProperties?.FirstOrDefault(); // Check whether the navigation property should be part of the path @@ -75,23 +73,36 @@ protected override void SetOperations(OpenApiPathItem item) AddGetOperation(item, restriction); - UpdateRestrictionsType navPropUpdateRestriction = restriction?.UpdateRestrictions ?? - Context.Model.GetRecord(NavigationProperty); - InsertRestrictionsType navPropInsertRestriction = restriction?.InsertRestrictions ?? - Context.Model.GetRecord(NavigationProperty); - UpdateRestrictionsType entityUpdateRestriction = Context.Model.GetRecord(_navPropEntityType); + // Update restrictions + UpdateRestrictionsType navPropUpdateRestrictions = Context.Model.GetRecord(TargetPath, CapabilitiesConstants.UpdateRestrictions); + navPropUpdateRestrictions?.MergePropertiesIfNull(restriction?.UpdateRestrictions); + navPropUpdateRestrictions ??= restriction?.UpdateRestrictions; + UpdateRestrictionsType updateRestrictions = Context.Model.GetRecord(NavigationProperty); + navPropUpdateRestrictions?.MergePropertiesIfNull(updateRestrictions); + navPropUpdateRestrictions ??= updateRestrictions; + + // Insert restrictions + InsertRestrictionsType navPropInsertRestrictions = Context.Model.GetRecord(TargetPath, CapabilitiesConstants.InsertRestrictions); + navPropInsertRestrictions?.MergePropertiesIfNull(restriction?.InsertRestrictions); + navPropInsertRestrictions ??= restriction?.InsertRestrictions; + InsertRestrictionsType insertRestrictions = Context.Model.GetRecord(NavigationProperty); + navPropInsertRestrictions?.MergePropertiesIfNull(insertRestrictions); + navPropInsertRestrictions ??= insertRestrictions; + + // Entity insert restrictions + UpdateRestrictionsType entityUpdateRestrictions = Context.Model.GetRecord(_navPropEntityType); // containment: (Post - Collection | Patch/Put - Single) if (NavigationProperty.ContainsTarget) { - UpdateRestrictionsType updateRestrictionType = navPropUpdateRestriction ?? entityUpdateRestriction; + UpdateRestrictionsType updateRestrictionType = navPropUpdateRestrictions ?? entityUpdateRestrictions; if (NavigationProperty.TargetMultiplicity() == EdmMultiplicity.Many) { if (LastSegmentIsKeySegment) { - if ((entityUpdateRestriction?.IsUpdatable ?? true) && - (navPropUpdateRestriction?.IsUpdatable ?? true)) + if ((entityUpdateRestrictions?.IsUpdatable ?? true) && + (navPropUpdateRestrictions?.IsUpdatable ?? true)) { AddUpdateOperation(item, updateRestrictionType); @@ -99,12 +110,12 @@ protected override void SetOperations(OpenApiPathItem item) } else { - InsertRestrictionsType entityInsertRestriction = Context.Model.GetRecord(_navPropEntityType); - bool isInsertableDefault = navPropInsertRestriction == null && entityInsertRestriction == null; + InsertRestrictionsType entityInsertRestrictions = Context.Model.GetRecord(_navPropEntityType); + bool isInsertableDefault = navPropInsertRestrictions == null && entityInsertRestrictions == null; if (isInsertableDefault || - ((entityInsertRestriction?.IsInsertable ?? true) && - (navPropInsertRestriction?.IsInsertable ?? true))) + ((entityInsertRestrictions?.IsInsertable ?? true) && + (navPropInsertRestrictions?.IsInsertable ?? true))) { AddOperation(item, OperationType.Post); } @@ -123,14 +134,14 @@ protected override void SetOperations(OpenApiPathItem item) { if (LastSegmentIsKeySegment) { - if (navPropUpdateRestriction?.Updatable ?? false) + if (navPropUpdateRestrictions?.Updatable ?? false) { - AddUpdateOperation(item, navPropUpdateRestriction); + AddUpdateOperation(item, navPropUpdateRestrictions); } } else { - if (navPropInsertRestriction?.Insertable ?? false) + if (navPropInsertRestrictions?.Insertable ?? false) { AddOperation(item, OperationType.Post); } @@ -138,9 +149,9 @@ protected override void SetOperations(OpenApiPathItem item) } else { - if (navPropUpdateRestriction?.Updatable ?? false) + if (navPropUpdateRestrictions?.Updatable ?? false) { - AddUpdateOperation(item, navPropUpdateRestriction); + AddUpdateOperation(item, navPropUpdateRestrictions); } } } @@ -150,11 +161,15 @@ protected override void SetOperations(OpenApiPathItem item) private void AddGetOperation(OpenApiPathItem item, NavigationPropertyRestriction restriction) { - ReadRestrictionsType navPropReadRestriction = restriction?.ReadRestrictions ?? - Context.Model.GetRecord(NavigationProperty); + ReadRestrictionsType navPropReadRestriction = Context.Model.GetRecord(TargetPath, CapabilitiesConstants.ReadRestrictions); + navPropReadRestriction?.MergePropertiesIfNull(restriction?.ReadRestrictions); + navPropReadRestriction ??= restriction?.ReadRestrictions; + ReadRestrictionsType readRestrictions = Context.Model.GetRecord(NavigationProperty); + navPropReadRestriction?.MergePropertiesIfNull(readRestrictions); + navPropReadRestriction ??= readRestrictions; + ReadRestrictionsType entityReadRestriction = Context.Model.GetRecord(_navPropEntityType); bool isReadableDefault = navPropReadRestriction == null && entityReadRestriction == null; - if (isReadableDefault) { AddOperation(item, OperationType.Get); @@ -196,8 +211,12 @@ private void AddDeleteOperation(OpenApiPathItem item, NavigationPropertyRestrict { Debug.Assert(!LastSegmentIsRefSegment); - DeleteRestrictionsType navPropDeleteRestriction = restriction?.DeleteRestrictions ?? - Context.Model.GetRecord(NavigationProperty); + DeleteRestrictionsType navPropDeleteRestriction = Context.Model.GetRecord(TargetPath, CapabilitiesConstants.DeleteRestrictions); + navPropDeleteRestriction?.MergePropertiesIfNull(restriction?.DeleteRestrictions); + navPropDeleteRestriction ??= restriction?.DeleteRestrictions; + DeleteRestrictionsType insertRestrictions = Context.Model.GetRecord(NavigationProperty); + navPropDeleteRestriction?.MergePropertiesIfNull(insertRestrictions); + navPropDeleteRestriction ??= insertRestrictions; if (!(NavigationProperty.TargetMultiplicity() != EdmMultiplicity.Many || LastSegmentIsKeySegment)) return; diff --git a/src/Microsoft.OpenApi.OData.Reader/PathItem/OperationImportPathItemHandler.cs b/src/Microsoft.OpenApi.OData.Reader/PathItem/OperationImportPathItemHandler.cs index 9ab0bd3b..b447fa1c 100644 --- a/src/Microsoft.OpenApi.OData.Reader/PathItem/OperationImportPathItemHandler.cs +++ b/src/Microsoft.OpenApi.OData.Reader/PathItem/OperationImportPathItemHandler.cs @@ -43,8 +43,11 @@ protected override void SetOperations(OpenApiPathItem item) // how to invoke the function import. // so far, - ReadRestrictionsType read = Context.Model.GetRecord(EdmOperationImport, CapabilitiesConstants.ReadRestrictions); - if (read == null || read.IsReadable) + ReadRestrictionsType readRestrictions = Context.Model.GetRecord(TargetPath, CapabilitiesConstants.ReadRestrictions); + ReadRestrictionsType operationReadRestrictions = Context.Model.GetRecord(EdmOperationImport, CapabilitiesConstants.ReadRestrictions); + readRestrictions?.MergePropertiesIfNull(operationReadRestrictions); + readRestrictions ??= operationReadRestrictions; + if (readRestrictions?.IsReadable ?? true) { AddOperation(item, OperationType.Get); } diff --git a/src/Microsoft.OpenApi.OData.Reader/PathItem/PathItemHandler.cs b/src/Microsoft.OpenApi.OData.Reader/PathItem/PathItemHandler.cs index c9f84c91..5cd00b38 100644 --- a/src/Microsoft.OpenApi.OData.Reader/PathItem/PathItemHandler.cs +++ b/src/Microsoft.OpenApi.OData.Reader/PathItem/PathItemHandler.cs @@ -3,18 +3,13 @@ // 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.OpenApi.Any; -using Microsoft.OpenApi.Interfaces; using Microsoft.OpenApi.Models; using Microsoft.OpenApi.OData.Common; using Microsoft.OpenApi.OData.Edm; using Microsoft.OpenApi.OData.Generator; using Microsoft.OpenApi.OData.Operation; using Microsoft.OpenApi.OData.Properties; +using System; namespace Microsoft.OpenApi.OData.PathItem { @@ -38,6 +33,11 @@ internal abstract class PathItemHandler : IPathItemHandler /// protected ODataPath Path { get; private set; } + /// + /// Gets the string representation of the Edm target path for annotations. + /// + protected string TargetPath; + /// public virtual OpenApiPathItem CreatePathItem(ODataContext context, ODataPath path) { @@ -86,6 +86,7 @@ protected virtual void Initialize(ODataContext context, ODataPath path) { throw Error.InvalidOperation(String.Format(SRResource.InvalidPathKindForPathItemHandler, GetType().Name, path.Kind)); } + TargetPath = path.GetTargetPath(context.Model); } /// diff --git a/src/Microsoft.OpenApi.OData.Reader/PathItem/RefPathItemHandler.cs b/src/Microsoft.OpenApi.OData.Reader/PathItem/RefPathItemHandler.cs index 68751b4b..f4f28342 100644 --- a/src/Microsoft.OpenApi.OData.Reader/PathItem/RefPathItemHandler.cs +++ b/src/Microsoft.OpenApi.OData.Reader/PathItem/RefPathItemHandler.cs @@ -37,32 +37,32 @@ protected override void SetOperations(OpenApiPathItem item) { IEdmEntitySet entitySet = NavigationSource as IEdmEntitySet; IEdmVocabularyAnnotatable target = entitySet; - if (target == null) - { - target = NavigationSource as IEdmSingleton; - } + target ??= NavigationSource as IEdmSingleton; string navigationPropertyPath = String.Join("/", Path.Segments.Where(s => !(s is ODataKeySegment || s is ODataNavigationSourceSegment)).Select(e => e.Identifier)); - - NavigationRestrictionsType navigation = Context.Model.GetRecord(target, CapabilitiesConstants.NavigationRestrictions); - NavigationPropertyRestriction restriction = navigation?.RestrictedProperties?.FirstOrDefault(r => r.NavigationProperty == navigationPropertyPath); + + NavigationRestrictionsType navigationRestrictions = Context.Model.GetRecord(TargetPath, CapabilitiesConstants.NavigationRestrictions); + NavigationRestrictionsType sourceNavigationRestrictions = Context.Model.GetRecord(target, CapabilitiesConstants.NavigationRestrictions); + navigationRestrictions?.MergePropertiesIfNull(sourceNavigationRestrictions); + navigationRestrictions ??= sourceNavigationRestrictions; + NavigationPropertyRestriction restriction = navigationRestrictions?.RestrictedProperties?.FirstOrDefault(r => r.NavigationProperty == navigationPropertyPath); // verify using individual first - if (restriction != null && restriction.Navigability != null && restriction.Navigability.Value == NavigationType.None) + if (restriction?.Navigability != null && restriction.Navigability.Value == NavigationType.None) { return; } - if (restriction == null || restriction.Navigability == null) + // if the individual has not navigability setting, use the global navigability setting + if (restriction?.Navigability == null + && navigationRestrictions != null + && navigationRestrictions.Navigability != null + && navigationRestrictions.Navigability.Value == NavigationType.None) { - // if the individual has not navigability setting, use the global navigability setting - if (navigation != null && navigation.Navigability != null && navigation.Navigability.Value == NavigationType.None) - { - // Default navigability for all navigation properties of the annotation target. - // Individual navigation properties can override this value via `RestrictedProperties/Navigability`. - return; - } + // Default navigability for all navigation properties of the annotation target. + // Individual navigation properties can override this value via `RestrictedProperties/Navigability`. + return; } // Create the ref @@ -92,8 +92,10 @@ protected override void SetOperations(OpenApiPathItem item) private void AddDeleteOperation(OpenApiPathItem item, NavigationPropertyRestriction restriction) { - DeleteRestrictionsType delete = restriction?.DeleteRestrictions; - if (delete == null || delete.IsDeletable) + DeleteRestrictionsType deleteRestrictions = Context.Model.GetRecord(TargetPath, CapabilitiesConstants.DeleteRestrictions); + deleteRestrictions?.MergePropertiesIfNull(restriction?.DeleteRestrictions); + deleteRestrictions ??= restriction?.DeleteRestrictions; + if (deleteRestrictions?.IsDeletable ?? true) { AddOperation(item, OperationType.Delete); } @@ -101,8 +103,10 @@ private void AddDeleteOperation(OpenApiPathItem item, NavigationPropertyRestrict private void AddReadOperation(OpenApiPathItem item, NavigationPropertyRestriction restriction) { - ReadRestrictionsType read = restriction?.ReadRestrictions; - if (read == null || read.IsReadable) + ReadRestrictionsType readRestrictions = Context.Model.GetRecord(TargetPath, CapabilitiesConstants.ReadRestrictions); + readRestrictions?.MergePropertiesIfNull(restriction?.ReadRestrictions); + readRestrictions ??= restriction?.ReadRestrictions; + if (readRestrictions?.IsReadable ?? true) { AddOperation(item, OperationType.Get); } @@ -110,8 +114,10 @@ private void AddReadOperation(OpenApiPathItem item, NavigationPropertyRestrictio private void AddInsertOperation(OpenApiPathItem item, NavigationPropertyRestriction restriction) { - InsertRestrictionsType insert = restriction?.InsertRestrictions; - if (insert == null || insert.IsInsertable) + InsertRestrictionsType insertRestrictions = Context.Model.GetRecord(TargetPath, CapabilitiesConstants.InsertRestrictions); + insertRestrictions?.MergePropertiesIfNull(restriction?.InsertRestrictions); + insertRestrictions ??= restriction?.InsertRestrictions; + if (insertRestrictions?.IsInsertable ?? true) { AddOperation(item, OperationType.Post); } @@ -119,8 +125,10 @@ private void AddInsertOperation(OpenApiPathItem item, NavigationPropertyRestrict private void AddUpdateOperation(OpenApiPathItem item, NavigationPropertyRestriction restriction) { - UpdateRestrictionsType update = restriction?.UpdateRestrictions; - if (update == null || update.IsUpdatable) + UpdateRestrictionsType updateRestrictions = Context.Model.GetRecord(TargetPath, CapabilitiesConstants.UpdateRestrictions); + updateRestrictions?.MergePropertiesIfNull(restriction?.UpdateRestrictions); + updateRestrictions ??= restriction?.UpdateRestrictions; + if (updateRestrictions?.IsUpdatable ?? true) { AddOperation(item, OperationType.Put); } @@ -136,6 +144,7 @@ protected override void Initialize(ODataContext context, ODataPath path) NavigationProperty = path.OfType().Last().NavigationProperty; } + /// protected override void SetBasicInfo(OpenApiPathItem pathItem) { diff --git a/src/Microsoft.OpenApi.OData.Reader/PathItem/SingletonPathItemHandler.cs b/src/Microsoft.OpenApi.OData.Reader/PathItem/SingletonPathItemHandler.cs index 6ffbc66c..9cb2cb48 100644 --- a/src/Microsoft.OpenApi.OData.Reader/PathItem/SingletonPathItemHandler.cs +++ b/src/Microsoft.OpenApi.OData.Reader/PathItem/SingletonPathItemHandler.cs @@ -28,15 +28,21 @@ internal class SingletonPathItemHandler : PathItemHandler protected override void SetOperations(OpenApiPathItem item) { // Retrieve a singleton. - ReadRestrictionsType read = Context.Model.GetRecord(Singleton); - if (read == null || read.IsReadable) + ReadRestrictionsType readRestrictions = Context.Model.GetRecord(TargetPath, CapabilitiesConstants.ReadRestrictions); + ReadRestrictionsType singletonReadRestrictions = Context.Model.GetRecord(Singleton, CapabilitiesConstants.ReadRestrictions); + readRestrictions?.MergePropertiesIfNull(singletonReadRestrictions); + readRestrictions ??= singletonReadRestrictions; + if (readRestrictions?.IsReadable ?? true) { AddOperation(item, OperationType.Get); } // Update a singleton - UpdateRestrictionsType update = Context.Model.GetRecord(Singleton); - if (update == null || update.IsUpdatable) + UpdateRestrictionsType updateRestrictions = Context.Model.GetRecord(TargetPath, CapabilitiesConstants.UpdateRestrictions); + UpdateRestrictionsType singletonUpdateRestrictions = Context.Model.GetRecord(Singleton, CapabilitiesConstants.UpdateRestrictions); + updateRestrictions?.MergePropertiesIfNull(singletonUpdateRestrictions); + updateRestrictions ??= singletonUpdateRestrictions; + if (updateRestrictions?.IsUpdatable ?? true) { AddOperation(item, OperationType.Patch); } diff --git a/src/Microsoft.OpenApi.OData.Reader/Vocabulary/Capabilities/NavigationRestrictionsType.cs b/src/Microsoft.OpenApi.OData.Reader/Vocabulary/Capabilities/NavigationRestrictionsType.cs index 0145a07e..e0ff6696 100644 --- a/src/Microsoft.OpenApi.OData.Reader/Vocabulary/Capabilities/NavigationRestrictionsType.cs +++ b/src/Microsoft.OpenApi.OData.Reader/Vocabulary/Capabilities/NavigationRestrictionsType.cs @@ -3,11 +3,11 @@ // 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.Vocabularies; using Microsoft.OpenApi.OData.Common; using Microsoft.OpenApi.OData.Edm; +using System.Collections.Generic; +using System.Linq; namespace Microsoft.OpenApi.OData.Vocabulary.Capabilities { @@ -239,5 +239,21 @@ public void Initialize(IEdmRecordExpression record) // Referenceable Referenceable = record.GetBoolean("Referenceable"); } + + /// + /// Merges properties of the specified object into this instance if they are null. + /// + /// The object containing properties to merge. + public void MergePropertiesIfNull(NavigationRestrictionsType source) + { + if (source == null) + return; + + Navigability ??= source.Navigability; + + RestrictedProperties ??= source.RestrictedProperties; + + Referenceable ??= source.Referenceable; + } } } diff --git a/test/Microsoft.OpenAPI.OData.Reader.Tests/Operation/MediaEntityGetOperationHandlerTests.cs b/test/Microsoft.OpenAPI.OData.Reader.Tests/Operation/MediaEntityGetOperationHandlerTests.cs index 790d601b..b3e2ec18 100644 --- a/test/Microsoft.OpenAPI.OData.Reader.Tests/Operation/MediaEntityGetOperationHandlerTests.cs +++ b/test/Microsoft.OpenAPI.OData.Reader.Tests/Operation/MediaEntityGetOperationHandlerTests.cs @@ -207,6 +207,7 @@ public void CreateMediaEntityPropertyGetOperationWithTargetPathAnnotationsReturn Assert.NotNull(operation); Assert.Equal("Get photo", operation.Summary); Assert.Equal("Get photo of a specific user", operation.Description); + Assert.Single(operation.Parameters); Assert.NotNull(operation.ExternalDocs); Assert.Equal("Find more info here", operation.ExternalDocs.Description); diff --git a/test/Microsoft.OpenAPI.OData.Reader.Tests/PathItem/ComplexPropertyPathItemHandlerTests.cs b/test/Microsoft.OpenAPI.OData.Reader.Tests/PathItem/ComplexPropertyPathItemHandlerTests.cs index d1d342a3..391acd4b 100644 --- a/test/Microsoft.OpenAPI.OData.Reader.Tests/PathItem/ComplexPropertyPathItemHandlerTests.cs +++ b/test/Microsoft.OpenAPI.OData.Reader.Tests/PathItem/ComplexPropertyPathItemHandlerTests.cs @@ -207,4 +207,64 @@ public void CreateComplexPropertyPathItemAddsCustomAttributeValuesToPathExtensio string isHiddenValue = (isHiddenExtension as OpenApiString)?.Value; Assert.Equal("true", isHiddenValue); } + + [Theory] + [InlineData("true", "true", "true", 3)] + [InlineData("false", "false", "false", 0)] + [InlineData ("false", "false", "true", 1)] + public void CreatesComplexPropertyPathsBasedOnTargetPathAnnotations(string readable, string insertable, string updatable, int operationCount) + { + // Arrange + var annotation = $@" + + + + + + + + + + + + + + +"; + var target = @"""NS.Default/Customers/AlternativeAddresses"""; + var model = EntitySetPathItemHandlerTests.GetEdmModel(annotation, target); + var context = new ODataContext(model, new OpenApiConvertSettings()); + + var entitySet = model.EntityContainer.FindEntitySet("Customers"); + Assert.NotNull(entitySet); // guard + + var entityType = entitySet.EntityType(); + var property = entityType.FindProperty("AlternativeAddresses"); + Assert.NotNull(property); // guard + + var path = new ODataPath( + new ODataNavigationSourceSegment(entitySet), + new ODataKeySegment(entityType), + new ODataComplexPropertySegment(property as IEdmStructuralProperty)); + Assert.Equal(ODataPathKind.ComplexProperty, path.Kind); // guard + + // Act + var pathItem = _pathItemHandler.CreatePathItem(context, path); + + // Assert + Assert.NotNull(pathItem); + Assert.Equal(operationCount, pathItem.Operations.Count); + if (operationCount == 1) + { + Assert.True(pathItem.Operations.ContainsKey(OperationType.Patch)); + Assert.False(pathItem.Operations.ContainsKey(OperationType.Post)); + Assert.False(pathItem.Operations.ContainsKey(OperationType.Get)); + } + else if (operationCount == 3) + { + Assert.True(pathItem.Operations.ContainsKey(OperationType.Patch)); + Assert.True(pathItem.Operations.ContainsKey(OperationType.Post)); + Assert.True(pathItem.Operations.ContainsKey(OperationType.Get)); + } + } } \ No newline at end of file diff --git a/test/Microsoft.OpenAPI.OData.Reader.Tests/PathItem/MediaEntityPathItemHandlerTests.cs b/test/Microsoft.OpenAPI.OData.Reader.Tests/PathItem/MediaEntityPathItemHandlerTests.cs index 7f1d0006..dd7ac1b1 100644 --- a/test/Microsoft.OpenAPI.OData.Reader.Tests/PathItem/MediaEntityPathItemHandlerTests.cs +++ b/test/Microsoft.OpenAPI.OData.Reader.Tests/PathItem/MediaEntityPathItemHandlerTests.cs @@ -137,6 +137,32 @@ public void CreateMediaEntityPathItemWorksForUpdateRestrictionsCapabilities(bool VerifyPathItemOperationsForStreamContentSegment(annotation, expected); } + [Theory] + [InlineData(true, new OperationType[] { OperationType.Get, OperationType.Put, OperationType.Delete })] + [InlineData(false, new OperationType[] { OperationType.Get, OperationType.Delete })] + public void CreateMediaEntityPathItemWorksForUpdateRestrictionsCapabilitiesWithTargetPathAnnotations(bool updatable, OperationType[] expected) + { + string annotation = $@" + + + + +"; + + // Arrange + string streamPropertyTargetPathAnnotation = $@" + + + + + + +"; + + // Assert + VerifyPathItemOperationsForStreamPropertySegment(annotation, expected, streamPropertyTargetPathAnnotation); + } + [Theory] [InlineData(true, new OperationType[] { OperationType.Get, OperationType.Put, OperationType.Delete })] [InlineData(false, new OperationType[] { OperationType.Get, OperationType.Put })] @@ -155,10 +181,39 @@ public void CreateMediaEntityPathItemWorksForDeleteRestrictionsCapabilities(bool VerifyPathItemOperationsForStreamContentSegment(annotation, expected); } - private void VerifyPathItemOperationsForStreamPropertySegment(string annotation, OperationType[] expected) + [Theory] + [InlineData(true, new OperationType[] { OperationType.Get, OperationType.Put, OperationType.Delete })] + [InlineData(false, new OperationType[] { OperationType.Get, OperationType.Put })] + public void CreateMediaEntityPathItemWorksForDeleteRestrictionsCapabilitiesWithTargetPathAnnotations(bool deletable, OperationType[] expected) + { + // Arrange + string annotation = $@" + + + + +"; + + + // Arrange + string streamPropertyTargetPathAnnotation = $@" + + + + + + +"; + + // Assert + VerifyPathItemOperationsForStreamPropertySegment(annotation, expected, streamPropertyTargetPathAnnotation); + } + + private void VerifyPathItemOperationsForStreamPropertySegment(string annotation, OperationType[] expected, string targetPathAnnotations = "") { // Arrange - IEdmModel model = GetEdmModel(annotation); + + IEdmModel model = GetEdmModel(annotation, targetPathAnnotations); ODataContext context = new ODataContext(model); IEdmEntitySet entitySet = model.EntityContainer.FindEntitySet("Todos"); Assert.NotNull(entitySet); // guard @@ -180,10 +235,10 @@ private void VerifyPathItemOperationsForStreamPropertySegment(string annotation, Assert.Equal(expected, pathItem.Operations.Select(e => e.Key)); } - private void VerifyPathItemOperationsForStreamContentSegment(string annotation, OperationType[] expected) + private void VerifyPathItemOperationsForStreamContentSegment(string annotation, OperationType[] expected, string targetPathAnnotations = null) { // Arrange - IEdmModel model = GetEdmModel(annotation); + IEdmModel model = GetEdmModel(annotation, targetPathAnnotations); ODataContext context = new ODataContext(model); IEdmEntitySet entitySet = model.EntityContainer.FindEntitySet("Todos"); IEdmSingleton singleton = model.EntityContainer.FindSingleton("me"); @@ -217,7 +272,7 @@ private void VerifyPathItemOperationsForStreamContentSegment(string annotation, Assert.Equal(expected, pathItem2.Operations.Select(e => e.Key)); } - private IEdmModel GetEdmModel(string annotation) + private IEdmModel GetEdmModel(string annotation, string targetPathAnnotation = "") { const string template = @" @@ -247,10 +302,11 @@ private IEdmModel GetEdmModel(string annotation) {0} + {1} "; - string modelText = string.Format(template, annotation); + string modelText = string.Format(template, annotation, targetPathAnnotation); bool result = CsdlReader.TryParse(XElement.Parse(modelText).CreateReader(), out IEdmModel model, out _); Assert.True(result); return model;