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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -263,4 +263,101 @@ public void CreatesComplexPropertyPathsBasedOnTargetPathAnnotations(string reada
Assert.True(pathItem.Operations.ContainsKey(HttpMethod.Get));
}
}

[Theory]
[InlineData(false, 2)]
[InlineData(true, 2)]
public void CreatesComplexPropertyPathItemUsesHttpPutForUpdateWhenSettingIsEnabled(bool useHttpPutForUpdate, int operationCount)
{
// Arrange
var annotation = @"
<Annotation Term=""Org.OData.Capabilities.V1.UpdateRestrictions"">
<Record>
<PropertyValue Property=""Updatable"" Bool=""true"" />
</Record>
</Annotation>
<Annotation Term=""Org.OData.Capabilities.V1.ReadRestrictions"">
<Record>
<PropertyValue Property=""Readable"" Bool=""true"" />
</Record>
</Annotation>";
var target = @"""NS.Customer/BillingAddress""";
var model = EntitySetPathItemHandlerTests.GetEdmModel(annotation: annotation, target: target);
var convertSettings = new OpenApiConvertSettings
{
UseHttpPutForUpdate = useHttpPutForUpdate
};
var context = new ODataContext(model, convertSettings);
var entitySet = model.EntityContainer.FindEntitySet("Customers");
Assert.NotNull(entitySet); // guard
var entityType = entitySet.EntityType;
var property = entityType.FindProperty("BillingAddress");
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 ?? 0);

Assert.True(pathItem.Operations.ContainsKey(HttpMethod.Get));
if (useHttpPutForUpdate)
{
Assert.True(pathItem.Operations.ContainsKey(HttpMethod.Put));
Assert.False(pathItem.Operations.ContainsKey(HttpMethod.Patch));
}
else
{
Assert.True(pathItem.Operations.ContainsKey(HttpMethod.Patch));
Assert.False(pathItem.Operations.ContainsKey(HttpMethod.Put));
}
}

[Fact]
public void CreateComplexPropertyPathItemPrefersUpdateMethodAnnotationOverUseHttpPutForUpdateSetting()
{
// Arrange - annotation specifies PUT explicitly, setting is disabled (default PATCH)
var annotation = @"
<Annotation Term=""Org.OData.Capabilities.V1.UpdateRestrictions"">
<Record>
<PropertyValue Property=""UpdateMethod"">
<EnumMember>Org.OData.Capabilities.V1.HttpMethod/PUT</EnumMember>
</PropertyValue>
<PropertyValue Property=""Updatable"" Bool=""true"" />
</Record>
</Annotation>
<Annotation Term=""Org.OData.Capabilities.V1.ReadRestrictions"">
<Record>
<PropertyValue Property=""Readable"" Bool=""true"" />
</Record>
</Annotation>";
var target = @"""NS.Customer/BillingAddress""";
var model = EntitySetPathItemHandlerTests.GetEdmModel(annotation: annotation, target: target);
var convertSettings = new OpenApiConvertSettings
{
UseHttpPutForUpdate = false // Setting says use PATCH (default)
};
var context = new ODataContext(model, convertSettings);
var entitySet = model.EntityContainer.FindEntitySet("Customers");
Assert.NotNull(entitySet); // guard
var entityType = entitySet.EntityType;
var property = entityType.FindProperty("BillingAddress");
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(2, pathItem.Operations?.Count ?? 0);
Assert.True(pathItem.Operations.ContainsKey(HttpMethod.Get));
// Should use PUT from annotation, not PATCH from setting
Assert.True(pathItem.Operations.ContainsKey(HttpMethod.Put));
Assert.False(pathItem.Operations.ContainsKey(HttpMethod.Patch));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,67 @@ public void CreateEntityPathItemWorksForUpdateMethodRestrictionsCapabilities(boo
VerifyPathItemOperations(annotation, expected);
}

[Theory]
[InlineData(false, new string[] { "get", "patch", "delete" })]
[InlineData(true, new string[] { "get", "put", "delete" })]
public void CreateEntityPathItemUsesHttpPutForUpdateWhenSettingIsEnabled(bool useHttpPutForUpdate, string[] expected)
{
// Arrange
IEdmModel model = EntitySetPathItemHandlerTests.GetEdmModel(annotation: "");
OpenApiConvertSettings settings = new OpenApiConvertSettings
{
UseHttpPutForUpdate = useHttpPutForUpdate
};
ODataContext context = new ODataContext(model, settings);
IEdmEntitySet entitySet = model.EntityContainer.FindEntitySet("Customers");
Assert.NotNull(entitySet); // guard
ODataPath path = new ODataPath(new ODataNavigationSourceSegment(entitySet), new ODataKeySegment(entitySet.EntityType));

// Act
var pathItem = _pathItemHandler.CreatePathItem(context, path);

// Assert
Assert.NotNull(pathItem);

Assert.NotNull(pathItem.Operations);
Assert.NotEmpty(pathItem.Operations);
Assert.Equal(expected, pathItem.Operations.Select(e => e.Key.ToString().ToLowerInvariant()));
}

[Fact]
public void CreateEntityPathItemPrefersUpdateMethodAnnotationOverUseHttpPutForUpdateSetting()
{
// Arrange - annotation specifies PUT explicitly, setting is disabled (default PATCH)
string annotation = @"
<Annotation Term=""Org.OData.Capabilities.V1.UpdateRestrictions"">
<Record>
<PropertyValue Property=""UpdateMethod"">
<EnumMember>Org.OData.Capabilities.V1.HttpMethod/PUT</EnumMember>
</PropertyValue>
</Record>
</Annotation>";

IEdmModel model = EntitySetPathItemHandlerTests.GetEdmModel(annotation);
OpenApiConvertSettings settings = new OpenApiConvertSettings
{
UseHttpPutForUpdate = false // Setting says use PATCH (default)
};
ODataContext context = new ODataContext(model, settings);
IEdmEntitySet entitySet = model.EntityContainer.FindEntitySet("Customers");
Assert.NotNull(entitySet); // guard
ODataPath path = new ODataPath(new ODataNavigationSourceSegment(entitySet), new ODataKeySegment(entitySet.EntityType));

// Act
var pathItem = _pathItemHandler.CreatePathItem(context, path);

// Assert
Assert.NotNull(pathItem);
Assert.NotNull(pathItem.Operations);
Assert.NotEmpty(pathItem.Operations);
// Should use PUT from annotation, not PATCH from setting
Assert.Equal(new string[] { "get", "put", "delete" }, pathItem.Operations.Select(e => e.Key.ToString().ToLowerInvariant()));
}

private void VerifyPathItemOperations(string annotation, string[] expected)
{
// Arrange
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -602,6 +602,128 @@ public void CreateNavigationPropertyPathItemAddsCustomAttributeValuesToPathExten
Assert.Equal("true", isHiddenValue);
}

[Theory]
[InlineData(false, new string[] { "get", "patch", "delete" })]
[InlineData(true, new string[] { "get", "put", "delete" })]
public void CreateSingleNavigationPropertyPathItemUsesHttpPutForUpdateWhenSettingIsEnabled(bool useHttpPutForUpdate, string[] expected)
{
// Arrange
IEdmModel model = GetEdmModel("");
OpenApiConvertSettings settings = new OpenApiConvertSettings
{
UseHttpPutForUpdate = useHttpPutForUpdate
};
ODataContext context = new ODataContext(model, settings);
IEdmEntitySet entitySet = model.EntityContainer.FindEntitySet("Customers");
Assert.NotNull(entitySet); // guard
IEdmEntityType entityType = entitySet.EntityType;

IEdmNavigationProperty property = entityType.DeclaredNavigationProperties()
.FirstOrDefault(c => c.ContainsTarget == true && c.TargetMultiplicity() != EdmMultiplicity.Many);
Assert.NotNull(property);

ODataPath path = new ODataPath(new ODataNavigationSourceSegment(entitySet),
new ODataKeySegment(entityType),
new ODataNavigationPropertySegment(property));

// Act
var pathItem = _pathItemHandler.CreatePathItem(context, path);

// Assert
Assert.NotNull(pathItem);
Assert.NotNull(pathItem.Operations);
Assert.NotEmpty(pathItem.Operations);
Assert.Equal(expected, pathItem.Operations.Select(o => o.Key.ToString().ToLowerInvariant()));
}

[Theory]
[InlineData(false, new string[] { "get", "patch", "delete" })]
[InlineData(true, new string[] { "get", "put", "delete" })]
public void CreateCollectionNavigationPropertyPathItemUsesHttpPutForUpdateWhenSettingIsEnabled(bool useHttpPutForUpdate, string[] expected)
{
// Arrange
IEdmModel model = GetEdmModel("");
OpenApiConvertSettings settings = new OpenApiConvertSettings
{
UseHttpPutForUpdate = useHttpPutForUpdate
};
ODataContext context = new ODataContext(model, settings);
IEdmEntitySet entitySet = model.EntityContainer.FindEntitySet("Customers");
Assert.NotNull(entitySet); // guard
IEdmEntityType entityType = entitySet.EntityType;

IEdmNavigationProperty property = entityType.DeclaredNavigationProperties()
.FirstOrDefault(c => c.ContainsTarget == true && c.TargetMultiplicity() == EdmMultiplicity.Many);
Assert.NotNull(property);

ODataPath path = new ODataPath(new ODataNavigationSourceSegment(entitySet),
new ODataKeySegment(entityType),
new ODataNavigationPropertySegment(property),
new ODataKeySegment(property.ToEntityType()));

// Act
var pathItem = _pathItemHandler.CreatePathItem(context, path);

// Assert
Assert.NotNull(pathItem);
Assert.NotNull(pathItem.Operations);
Assert.NotEmpty(pathItem.Operations);
Assert.Equal(expected, pathItem.Operations.Select(o => o.Key.ToString().ToLowerInvariant()));
}

[Fact]
public void CreateNavigationPropertyPathItemPrefersUpdateMethodAnnotationOverUseHttpPutForUpdateSetting()
{
// Arrange - annotation specifies PUT explicitly, setting is disabled (default PATCH)
string annotation = @"
<Annotation Term=""Org.OData.Capabilities.V1.NavigationRestrictions"">
<Record>
<PropertyValue Property=""RestrictedProperties"" >
<Collection>
<Record>
<PropertyValue Property=""NavigationProperty"" NavigationPropertyPath=""ContainedMyOrder"" />
<PropertyValue Property=""UpdateRestrictions"" >
<Record>
<PropertyValue Property=""UpdateMethod"">
<EnumMember>Org.OData.Capabilities.V1.HttpMethod/PUT</EnumMember>
</PropertyValue>
</Record>
</PropertyValue>
</Record>
</Collection>
</PropertyValue>
</Record>
</Annotation>";

IEdmModel model = GetEdmModel(annotation);
OpenApiConvertSettings settings = new OpenApiConvertSettings
{
UseHttpPutForUpdate = false // Setting says use PATCH (default)
};
ODataContext context = new ODataContext(model, settings);
IEdmEntitySet entitySet = model.EntityContainer.FindEntitySet("Customers");
Assert.NotNull(entitySet); // guard
IEdmEntityType entityType = entitySet.EntityType;

IEdmNavigationProperty property = entityType.DeclaredNavigationProperties()
.FirstOrDefault(c => c.ContainsTarget == true && c.Name == "ContainedMyOrder");
Assert.NotNull(property);

ODataPath path = new ODataPath(new ODataNavigationSourceSegment(entitySet),
new ODataKeySegment(entityType),
new ODataNavigationPropertySegment(property));

// Act
var pathItem = _pathItemHandler.CreatePathItem(context, path);

// Assert
Assert.NotNull(pathItem);
Assert.NotNull(pathItem.Operations);
Assert.NotEmpty(pathItem.Operations);
// Should use PUT from annotation, not PATCH from setting
Assert.Equal(new string[] { "get", "put", "delete" }, pathItem.Operations.Select(o => o.Key.ToString().ToLowerInvariant()));
}

public static IEdmModel GetEdmModel(string annotation, string annotation2 = "")
{
const string template = @"<edmx:Edmx Version=""4.0"" xmlns:edmx=""http://docs.oasis-open.org/odata/ns/edmx"" xmlns:ags=""http://aggregator.microsoft.com/internal"">
Expand Down
Loading