/// <inheritdoc/> protected override void SetParameters(OpenApiOperation operation) { base.SetParameters(operation); operation.Parameters.Add(new OpenApiParameter { Name = "If-Match", In = ParameterLocation.Header, Description = "ETag", Schema = new OpenApiSchema { Type = "string" } }); // for collection, we should have @id in query if (NavigationProperty.TargetMultiplicity() == EdmMultiplicity.Many) { operation.Parameters.Add(new OpenApiParameter { Name = "@id", In = ParameterLocation.Query, Description = "Delete Uri", Schema = new OpenApiSchema { Type = "string" } }); } }
/// <inheritdoc/> protected override void SetOperations(OpenApiPathItem item) { IEdmEntitySet entitySet = NavigationSource as IEdmEntitySet; IEdmVocabularyAnnotatable target = entitySet; if (target == null) { 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 <NavigationRestrictionsType>(target, CapabilitiesConstants.NavigationRestrictions); NavigationPropertyRestriction restriction = navigation?.RestrictedProperties?.FirstOrDefault(r => r.NavigationProperty == navigationPropertyPath); // verify using individual first if (restriction != null && 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 (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; } } // So far, we only consider the non-containment Debug.Assert(!NavigationProperty.ContainsTarget); // Create the ref if (NavigationProperty.TargetMultiplicity() == EdmMultiplicity.Many) { ODataSegment penultimateSegment = Path.Segments.Reverse().Skip(1).First(); if (penultimateSegment is ODataKeySegment) { // Collection-valued: DELETE ~/entityset/{key}/collection-valued-Nav/{key}/$ref AddDeleteOperation(item, restriction); } else { AddReadOperation(item, restriction); AddInsertOperation(item, restriction); } } else { AddReadOperation(item, restriction); AddUpdateOperation(item, restriction); AddDeleteOperation(item, restriction); } }
/// <inheritdoc/> protected override void SetOperations(OpenApiPathItem item) { IEdmEntitySet entitySet = NavigationSource as IEdmEntitySet; IEdmVocabularyAnnotatable target = entitySet; if (target == null) { target = NavigationSource as IEdmSingleton; } NavigationRestrictionsType navSourceRestrictionType = Context.Model.GetRecord <NavigationRestrictionsType>(target, CapabilitiesConstants.NavigationRestrictions); NavigationRestrictionsType navPropRestrictionType = Context.Model.GetRecord <NavigationRestrictionsType>(NavigationProperty, CapabilitiesConstants.NavigationRestrictions); NavigationPropertyRestriction restriction = navSourceRestrictionType?.RestrictedProperties? .FirstOrDefault(r => r.NavigationProperty == Path.NavigationPropertyPath()) ?? navPropRestrictionType?.RestrictedProperties?.FirstOrDefault(); // Check whether the navigation property should be part of the path if (EdmModelHelper.NavigationRestrictionsAllowsNavigability(navSourceRestrictionType, restriction) == false || EdmModelHelper.NavigationRestrictionsAllowsNavigability(navPropRestrictionType, restriction) == false) { return; } // containment: Get / (Post - Collection | Patch - Single) // non-containment: Get AddGetOperation(item, restriction); if (NavigationProperty.ContainsTarget) { if (NavigationProperty.TargetMultiplicity() == EdmMultiplicity.Many) { if (LastSegmentIsKeySegment) { UpdateRestrictionsType updateEntity = Context.Model.GetRecord <UpdateRestrictionsType>(_entityType); if (updateEntity?.IsUpdatable ?? true) { AddUpdateOperation(item, restriction); } } else { InsertRestrictionsType insert = restriction?.InsertRestrictions; if (insert?.IsInsertable ?? true) { AddOperation(item, OperationType.Post); } } } else { AddUpdateOperation(item, restriction); } } AddDeleteOperation(item, restriction); }
/// <inheritdoc/> protected override void SetOperations(OpenApiPathItem item) { IEdmEntitySet entitySet = NavigationSource as IEdmEntitySet; IEdmVocabularyAnnotatable target = entitySet; if (target == null) { target = NavigationSource as IEdmSingleton; } string navigationPropertyPath = String.Join("/", Path.Segments.OfType <ODataNavigationPropertySegment>().Select(e => e.NavigationProperty.Name)); // contaiment: Get / (Post - Collection | Patch - Single) // non-containment: only Get NavigationRestrictions navigation = Context.Model.GetNavigationRestrictions(target); if (navigation == null || !navigation.IsRestrictedProperty(navigationPropertyPath)) { AddOperation(item, OperationType.Get); } if (NavigationProperty.ContainsTarget) { if (NavigationProperty.TargetMultiplicity() == EdmMultiplicity.Many) { if (LastSegmentIsKeySegment) { // Need to check this scenario is valid or not? UpdateRestrictions update = Context.Model.GetUpdateRestrictions(target); if (update == null || !update.IsNonUpdatableNavigationProperty(navigationPropertyPath)) { AddOperation(item, OperationType.Patch); } } else { InsertRestrictions insert = Context.Model.GetInsertRestrictions(target); if (insert == null || !insert.IsNonInsertableNavigationProperty(navigationPropertyPath)) { AddOperation(item, OperationType.Post); } } } else { UpdateRestrictions update = Context.Model.GetUpdateRestrictions(target); if (update == null || !update.IsNonUpdatableNavigationProperty(navigationPropertyPath)) { AddOperation(item, OperationType.Patch); } } } }
private void AddGetOperation(OpenApiPathItem item, NavigationPropertyRestriction restriction) { ReadRestrictionsType read = restriction?.ReadRestrictions; if (read == null) { AddOperation(item, OperationType.Get); return; } if (NavigationProperty.TargetMultiplicity() == EdmMultiplicity.Many) { // TODO: $ref also supports Get ? if (LastSegmentIsKeySegment) { if (read.ReadByKeyRestrictions != null && read.ReadByKeyRestrictions.Readable != null) { if (read.ReadByKeyRestrictions.Readable.Value) { AddOperation(item, OperationType.Get); } } else { ReadRestrictionsType readEntity = Context.Model.GetRecord <ReadRestrictionsType>(_entityType); if (readEntity?.IsReadable ?? true) { AddOperation(item, OperationType.Get); } } } else { if (read.IsReadable) { AddOperation(item, OperationType.Get); } } } else { Debug.Assert(LastSegmentIsKeySegment == false); if (read.IsReadable) { AddOperation(item, OperationType.Get); } } }
private void AddGetOperation(OpenApiPathItem item, NavigationPropertyRestriction restriction) { ReadRestrictionsType read = restriction?.ReadRestrictions; if (read == null) { AddOperation(item, OperationType.Get); return; } if (NavigationProperty.TargetMultiplicity() == EdmMultiplicity.Many) { if (LastSegmentIsKeySegment) { if (read.ReadByKeyRestrictions != null && read.ReadByKeyRestrictions.Readable != null) { if (read.ReadByKeyRestrictions.Readable.Value) { AddOperation(item, OperationType.Get); } } else { if (read.IsReadable) { AddOperation(item, OperationType.Get); } } } else { if (read.IsReadable) { AddOperation(item, OperationType.Get); } } } else { Debug.Assert(LastSegmentIsKeySegment == false); if (read.IsReadable) { AddOperation(item, OperationType.Get); } } }
/// <inheritdoc/> protected override void SetParameters(OpenApiOperation operation) { base.SetParameters(operation); if (NavigationProperty.TargetMultiplicity() == EdmMultiplicity.Many) { // Need to verify that TopSupported or others should be applyed to navigaiton source. // So, how about for the navigation property. OpenApiParameter parameter = Context.CreateTop(NavigationProperty); if (parameter != null) { operation.Parameters.Add(parameter); } parameter = Context.CreateSkip(NavigationProperty); if (parameter != null) { operation.Parameters.Add(parameter); } parameter = Context.CreateSearch(NavigationProperty); if (parameter != null) { operation.Parameters.Add(parameter); } parameter = Context.CreateFilter(NavigationProperty); if (parameter != null) { operation.Parameters.Add(parameter); } parameter = Context.CreateCount(NavigationProperty); if (parameter != null) { operation.Parameters.Add(parameter); } parameter = Context.CreateOrderBy(NavigationProperty); if (parameter != null) { operation.Parameters.Add(parameter); } } }
protected override void SetExtensions(OpenApiOperation operation) { if (Context.Settings.EnablePagination) { if (!LastSegmentIsKeySegment && NavigationProperty.TargetMultiplicity() == EdmMultiplicity.Many) { OpenApiObject extension = new OpenApiObject { { "nextLinkName", new OpenApiString("@odata.nextLink") }, { "operationName", new OpenApiString(Context.Settings.PageableOperationName) } }; operation.Extensions.Add(Constants.xMsPageable, extension); } } base.SetExtensions(operation); }
/// <inheritdoc/> protected override void SetBasicInfo(OpenApiOperation operation) { // Summary operation.Summary = "Get " + NavigationProperty.Name + " from " + NavigationSource.Name; // OperationId if (Context.Settings.EnableOperationId) { string prefix = "Get"; if (!LastSegmentIsKeySegment && NavigationProperty.TargetMultiplicity() == EdmMultiplicity.Many) { prefix = "List"; } operation.OperationId = GetOperationId(prefix); } base.SetBasicInfo(operation); }
private void AddDeleteOperation(OpenApiPathItem item, NavigationPropertyRestriction restriction) { Debug.Assert(!LastSegmentIsRefSegment); if (!NavigationProperty.ContainsTarget) { return; } DeleteRestrictionsType delete = restriction?.DeleteRestrictions; if (delete == null || delete.IsDeletable) { if (NavigationProperty.TargetMultiplicity() != EdmMultiplicity.Many || LastSegmentIsKeySegment) { AddOperation(item, OperationType.Delete); } return; } }
/// <inheritdoc/> protected override void SetBasicInfo(OpenApiOperation operation) { // Summary and Description ReadRestrictionsType readRestriction = Restriction?.ReadRestrictions; string placeHolder = "Get ref of " + NavigationProperty.Name + " from " + NavigationSource.Name; operation.Summary = (LastSegmentIsKeySegment ? readRestriction?.ReadByKeyRestrictions?.Description : readRestriction?.Description) ?? placeHolder; operation.Description = (LastSegmentIsKeySegment ? readRestriction?.ReadByKeyRestrictions?.LongDescription : readRestriction?.LongDescription) ?? Context.Model.GetDescriptionAnnotation(NavigationProperty); // OperationId if (Context.Settings.EnableOperationId) { string prefix = "GetRef"; if (!LastSegmentIsKeySegment && NavigationProperty.TargetMultiplicity() == EdmMultiplicity.Many) { prefix = "ListRef"; } operation.OperationId = GetOperationId(prefix); } }
private void AddDeleteOperation(OpenApiPathItem item, NavigationPropertyRestriction restriction) { Debug.Assert(!LastSegmentIsRefSegment); if (!NavigationProperty.ContainsTarget) { return; } DeleteRestrictionsType delete = restriction?.DeleteRestrictions; DeleteRestrictionsType deleteEntity = Context.Model.GetRecord <DeleteRestrictionsType>(_entityType); bool isDeletableDefault = delete == null && deleteEntity == null; if (isDeletableDefault || (delete?.IsDeletable ?? false) || (deleteEntity?.IsDeletable ?? false)) { if (NavigationProperty.TargetMultiplicity() != EdmMultiplicity.Many || LastSegmentIsKeySegment) { AddOperation(item, OperationType.Delete); } return; } }
/// <inheritdoc/> protected override void SetResponses(OpenApiOperation operation) { OpenApiSchema schema = new OpenApiSchema { // $ref returns string for the Uri? Type = "string" }; if (NavigationProperty.TargetMultiplicity() == EdmMultiplicity.Many) { var properties = new Dictionary <string, OpenApiSchema> { { "value", new OpenApiSchema { Type = "array", Items = schema } } }; if (Context.Settings.EnablePagination) { properties.Add( "@odata.nextLink", new OpenApiSchema { Type = "string" }); } operation.Responses = new OpenApiResponses { { Constants.StatusCode200, new OpenApiResponse { Description = "Retrieved navigation property links", Content = new Dictionary <string, OpenApiMediaType> { { Constants.ApplicationJsonMediaType, new OpenApiMediaType { Schema = new OpenApiSchema { Title = "Collection of links of " + NavigationProperty.ToEntityType().Name, Type = "object", Properties = properties } } } }, Links = Context.CreateLinks(NavigationProperty.ToEntityType(), NavigationProperty.Name) } } }; } else { operation.Responses = new OpenApiResponses { { Constants.StatusCode200, new OpenApiResponse { Description = "Retrieved navigation property link", Content = new Dictionary <string, OpenApiMediaType> { { Constants.ApplicationJsonMediaType, new OpenApiMediaType { Schema = schema } } } } } }; } operation.Responses.Add(Constants.StatusCodeDefault, Constants.StatusCodeDefault.GetResponse()); base.SetResponses(operation); }
/// <inheritdoc/> protected override void SetOperations(OpenApiPathItem item) { IEdmEntitySet entitySet = NavigationSource as IEdmEntitySet; IEdmVocabularyAnnotatable target = entitySet; if (target == null) { 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 <NavigationRestrictionsType>(target, CapabilitiesConstants.NavigationRestrictions); NavigationPropertyRestriction restriction = navigation?.RestrictedProperties?.FirstOrDefault(r => r.NavigationProperty == navigationPropertyPath); // verify using individual first if (restriction != null && 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 (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; } } // So far, we only consider the non-containment Debug.Assert(!NavigationProperty.ContainsTarget); // It seems OData supports to "GetRef, DeleteRef", // Here at this time,let's only consider the "delete" ReadRestrictionsType read = restriction?.ReadRestrictions; if (read == null || read.IsReadable) { AddOperation(item, OperationType.Get); } // Create the ref if (NavigationProperty.TargetMultiplicity() == EdmMultiplicity.Many) { InsertRestrictionsType insert = restriction?.InsertRestrictions; if (insert == null || insert.IsInsertable) { AddOperation(item, OperationType.Post); } } else { UpdateRestrictionsType update = restriction?.UpdateRestrictions; if (update == null || update.IsUpdatable) { AddOperation(item, OperationType.Put); } // delete the link DeleteRestrictionsType delete = restriction?.DeleteRestrictions; if (delete == null || delete.IsDeletable) { AddOperation(item, OperationType.Delete); } } }
/// <inheritdoc/> protected override void SetResponses(OpenApiOperation operation) { if (NavigationProperty.TargetMultiplicity() == EdmMultiplicity.Many) { operation.Responses = new OpenApiResponses { { Context.Settings.UseSuccessStatusCodeRange?Constants.StatusCodeClass2XX : Constants.StatusCode200, new OpenApiResponse { UnresolvedReference = true, Reference = new OpenApiReference { Type = ReferenceType.Response, Id = $"String{Constants.CollectionSchemaSuffix}" }, } } }; } else { OpenApiSchema schema = new() { // $ref returns string for the Uri? Type = "string" }; IDictionary <string, OpenApiLink> links = null; if (Context.Settings.ShowLinks) { string operationId = GetOperationId(); links = Context.CreateLinks(entityType: NavigationProperty.ToEntityType(), entityName: NavigationProperty.Name, entityKind: NavigationProperty.PropertyKind.ToString(), parameters: PathParameters, path: Path, navPropOperationId: operationId); } operation.Responses = new OpenApiResponses { { Context.Settings.UseSuccessStatusCodeRange?Constants.StatusCodeClass2XX : Constants.StatusCode200, new OpenApiResponse { Description = "Retrieved navigation property link", Content = new Dictionary <string, OpenApiMediaType> { { Constants.ApplicationJsonMediaType, new OpenApiMediaType { Schema = schema } } }, Links = links } } }; } operation.AddErrorResponses(Context.Settings, false); base.SetResponses(operation); }
/// <inheritdoc/> protected override void SetResponses(OpenApiOperation operation) { OpenApiSchema schema = null; if (Context.Settings.EnableDerivedTypesReferencesForResponses) { schema = EdmModelHelper.GetDerivedTypesReferenceSchema(NavigationProperty.ToEntityType(), Context.Model); } if (schema == null) { schema = new OpenApiSchema { Reference = new OpenApiReference { Type = ReferenceType.Schema, Id = NavigationProperty.ToEntityType().FullName() } }; } if (!LastSegmentIsKeySegment && NavigationProperty.TargetMultiplicity() == EdmMultiplicity.Many) { var properties = new Dictionary <string, OpenApiSchema> { { "value", new OpenApiSchema { Type = "array", Items = schema } } }; if (Context.Settings.EnablePagination) { properties.Add( "@odata.nextLink", new OpenApiSchema { Type = "string" }); } operation.Responses = new OpenApiResponses { { Constants.StatusCode200, new OpenApiResponse { Description = "Retrieved navigation property", Content = new Dictionary <string, OpenApiMediaType> { { Constants.ApplicationJsonMediaType, new OpenApiMediaType { Schema = new OpenApiSchema { Title = "Collection of " + NavigationProperty.ToEntityType().Name, Type = "object", Properties = properties } } } } } } }; } else { IDictionary <string, OpenApiLink> links = null; if (Context.Settings.ShowLinks) { string operationId = GetOperationId(); links = Context.CreateLinks(entityType: NavigationProperty.ToEntityType(), entityName: NavigationProperty.Name, entityKind: NavigationProperty.PropertyKind.ToString(), parameters: operation.Parameters, navPropOperationId: operationId); } operation.Responses = new OpenApiResponses { { Constants.StatusCode200, new OpenApiResponse { Description = "Retrieved navigation property", Content = new Dictionary <string, OpenApiMediaType> { { Constants.ApplicationJsonMediaType, new OpenApiMediaType { Schema = schema } } }, Links = links } } }; } operation.Responses.Add(Constants.StatusCodeDefault, Constants.StatusCodeDefault.GetResponse()); base.SetResponses(operation); }
/// <inheritdoc/> protected override void SetResponses(OpenApiOperation operation) { IDictionary <string, OpenApiLink> links = null; if (Context.Settings.ShowLinks) { string operationId = GetOperationId(); links = Context.CreateLinks(entityType: NavigationProperty.ToEntityType(), entityName: NavigationProperty.Name, entityKind: NavigationProperty.PropertyKind.ToString(), path: Path, parameters: PathParameters, navPropOperationId: operationId); } if (!LastSegmentIsKeySegment && NavigationProperty.TargetMultiplicity() == EdmMultiplicity.Many) { operation.Responses = new OpenApiResponses { { Context.Settings.UseSuccessStatusCodeRange?Constants.StatusCodeClass2XX : Constants.StatusCode200, new OpenApiResponse { UnresolvedReference = true, Reference = new OpenApiReference() { Type = ReferenceType.Response, Id = $"{NavigationProperty.ToEntityType().FullName()}{Constants.CollectionSchemaSuffix}" }, Links = links } } }; } else { OpenApiSchema schema = null; var entityType = NavigationProperty.ToEntityType(); if (Context.Settings.EnableDerivedTypesReferencesForResponses) { schema = EdmModelHelper.GetDerivedTypesReferenceSchema(entityType, Context.Model); } if (schema == null) { schema = new OpenApiSchema { UnresolvedReference = true, Reference = new OpenApiReference { Type = ReferenceType.Schema, Id = entityType.FullName() } }; } operation.Responses = new OpenApiResponses { { Context.Settings.UseSuccessStatusCodeRange?Constants.StatusCodeClass2XX : Constants.StatusCode200, new OpenApiResponse { Description = "Retrieved navigation property", Content = new Dictionary <string, OpenApiMediaType> { { Constants.ApplicationJsonMediaType, new OpenApiMediaType { Schema = schema } } }, Links = links } } }; } operation.AddErrorResponses(Context.Settings, false); base.SetResponses(operation); }
/// <inheritdoc/> protected override void SetOperations(OpenApiPathItem item) { IEdmEntitySet entitySet = NavigationSource as IEdmEntitySet; IEdmVocabularyAnnotatable target = entitySet; if (target == null) { 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 <NavigationRestrictionsType>(target, CapabilitiesConstants.NavigationRestrictions); NavigationPropertyRestriction restriction = navigation?.RestrictedProperties?.FirstOrDefault(r => r.NavigationProperty == navigationPropertyPath); // verify using individual first if (restriction != null && 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 (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; } } // how about delete? // contaiment: Get / (Post - Collection | Patch - Single) // non-containment: only Get AddGetOperation(item, restriction); if (NavigationProperty.ContainsTarget) { if (NavigationProperty.TargetMultiplicity() == EdmMultiplicity.Many) { if (LastSegmentIsKeySegment) { // Need to check this scenario is valid or not? UpdateRestrictionsType update = restriction?.UpdateRestrictions; if (update == null || update.IsUpdatable) { AddOperation(item, OperationType.Patch); } } else { InsertRestrictionsType insert = restriction?.InsertRestrictions; if (insert == null || insert.IsInsertable) { AddOperation(item, OperationType.Post); } } } else { UpdateRestrictionsType update = restriction?.UpdateRestrictions; if (update == null || update.IsUpdatable) { AddOperation(item, OperationType.Patch); } } } }
/// <inheritdoc/> protected override void SetResponses(OpenApiOperation operation) { if (!LastSegmentIsKeySegment && NavigationProperty.TargetMultiplicity() == EdmMultiplicity.Many) { operation.Responses = new OpenApiResponses { { Constants.StatusCode200, new OpenApiResponse { Description = "Retrieved navigation property", Content = new Dictionary <string, OpenApiMediaType> { { Constants.ApplicationJsonMediaType, new OpenApiMediaType { Schema = new OpenApiSchema { Title = "Collection of " + NavigationProperty.ToEntityType().Name, Type = "object", Properties = new Dictionary <string, OpenApiSchema> { { "value", new OpenApiSchema { Type = "array", Items = new OpenApiSchema { Reference = new OpenApiReference { Type = ReferenceType.Schema, Id = NavigationProperty.ToEntityType().FullName() } } } } } } } } } } } }; } else { operation.Responses = new OpenApiResponses { { Constants.StatusCode200, new OpenApiResponse { Description = "Retrieved navigation property", Content = new Dictionary <string, OpenApiMediaType> { { Constants.ApplicationJsonMediaType, new OpenApiMediaType { Schema = new OpenApiSchema { Reference = new OpenApiReference { Type = ReferenceType.Schema, Id = NavigationProperty.ToEntityType().FullName() } } } } } } } }; } operation.Responses.Add(Constants.StatusCodeDefault, Constants.StatusCodeDefault.GetResponse()); base.SetResponses(operation); }