Skip to content
Open
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
123 changes: 123 additions & 0 deletions src/WebDav.Client.Tests/Methods/ReportTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
using NSubstitute;
using System;
using System.Threading;
using System.Threading.Tasks;
using System.Xml.Linq;
using WebDav.Client.Tests.TestDoubles;
using Xunit;

namespace WebDav.Client.Tests.Methods
{
public class ReportTests
{
[Fact]
public async Task When_RequestIsSuccessfull_Should_ReturnStatusCode200()
{
var dispatcher = Dispatcher.Mock();
var client = new WebDavClient().SetWebDavDispatcher(dispatcher);
var response = await client.Report("http://example.com/", new ReportParameters());

Assert.Equal(200, response.StatusCode);
}

[Fact]
public async Task When_RequestIsFailed_Should_ReturnStatusCode500()
{
var dispatcher = Dispatcher.MockFaulted();
var client = new WebDavClient().SetWebDavDispatcher(dispatcher);
var response = await client.Report("http://example.com/", new ReportParameters());

Assert.Equal(500, response.StatusCode);
}

[Fact]
public async Task When_PassingRequestBody_Should_SendIt()
{
var dispatcher = Dispatcher.Mock();
var client = new WebDavClient().SetWebDavDispatcher(dispatcher);

var requestBody = new XDocument(
new XDeclaration("1.0", "utf-8", null),
new XElement("{DAV:}version-tree",
new XAttribute(XNamespace.Xmlns + "D", "DAV:"),
new XElement("{DAV:}prop",
new XElement("{DAV:}version-name")
)
)
);

await client.Report("http://example.com/", new ReportParameters
{
RequestBody = requestBody
});

const string expectedContent =
@"<?xml version=""1.0"" encoding=""utf-8""?>
<D:version-tree xmlns:D=""DAV:"">
<D:prop>
<D:version-name />
</D:prop>
</D:version-tree>";
await dispatcher.Received(1).Send(
Arg.Any<Uri>(),
WebDavMethod.Report,
Arg.Is(Predicates.CompareRequestContent(expectedContent)),
CancellationToken.None
);
}

[Fact]
public async Task When_PassingApplyTo_Should_SetDepthHeader()
{
var dispatcher = Dispatcher.Mock();
var client = new WebDavClient().SetWebDavDispatcher(dispatcher);
await client.Report("http://example.com/", new ReportParameters
{
ApplyTo = ApplyTo.Report.ResourceOnly
});

await dispatcher.Received(1).Send(
Arg.Any<Uri>(),
WebDavMethod.Report,
Arg.Is(Predicates.CompareHeader("Depth", "0")),
CancellationToken.None
);
}

[Fact]
public async Task When_PassingApplyToResourceAndChildren_Should_SetDepthHeader()
{
var dispatcher = Dispatcher.Mock();
var client = new WebDavClient().SetWebDavDispatcher(dispatcher);
await client.Report("http://example.com/", new ReportParameters
{
ApplyTo = ApplyTo.Report.ResourceAndChildren
});

await dispatcher.Received(1).Send(
Arg.Any<Uri>(),
WebDavMethod.Report,
Arg.Is(Predicates.CompareHeader("Depth", "1")),
CancellationToken.None
);
}

[Fact]
public async Task When_PassingApplyToResourceAndAncestors_Should_SetDepthHeader()
{
var dispatcher = Dispatcher.Mock();
var client = new WebDavClient().SetWebDavDispatcher(dispatcher);
await client.Report("http://example.com/", new ReportParameters
{
ApplyTo = ApplyTo.Report.ResourceAndAncestors
});

await dispatcher.Received(1).Send(
Arg.Any<Uri>(),
WebDavMethod.Report,
Arg.Is(Predicates.CompareHeader("Depth", "infinity")),
CancellationToken.None
);
}
}
}
11 changes: 11 additions & 0 deletions src/WebDav.Client/Core/ApplyTo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -35,5 +35,16 @@ public enum Lock
ResourceOnly,
ResourceAndAncestors
}

/// <summary>
/// Specifies whether the REPORT method is to be applied only to the resource, to the resource and its internal members only, or the resource and all its members.
/// It corresponds to the WebDAV Depth header.
/// </summary>
public enum Report
{
ResourceOnly,
ResourceAndChildren,
ResourceAndAncestors
}
}
}
2 changes: 2 additions & 0 deletions src/WebDav.Client/Core/WebDavMethod.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,5 +19,7 @@ internal static class WebDavMethod
public static readonly HttpMethod Unlock = new HttpMethod("UNLOCK");

public static readonly HttpMethod Search = new HttpMethod("SEARCH");

public static readonly HttpMethod Report = new HttpMethod("REPORT");
}
}
15 changes: 15 additions & 0 deletions src/WebDav.Client/Helpers/DepthHeaderHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -44,5 +44,20 @@ public static string GetValueForLock(ApplyTo.Lock applyTo)
throw new ArgumentOutOfRangeException(nameof(applyTo));
}
}

public static string GetValueForReport(ApplyTo.Report applyTo)
{
switch (applyTo)
{
case ApplyTo.Report.ResourceOnly:
return "0";
case ApplyTo.Report.ResourceAndChildren:
return "1";
case ApplyTo.Report.ResourceAndAncestors:
return "infinity";
default:
throw new ArgumentOutOfRangeException(nameof(applyTo));
}
}
}
}
18 changes: 17 additions & 1 deletion src/WebDav.Client/IWebDavClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -416,5 +416,21 @@ public interface IWebDavClient : IDisposable
/// <param name="parameters">Parameters of the SEARCH operation.</param>
/// <returns>An instance of <see cref="PropfindResponse" />.</returns>
Task<PropfindResponse> Search(Uri requestUri, SearchParameters parameters);

/// <summary>
/// Executes a REPORT operation.
/// </summary>
/// <param name="requestUri">A string that represents the request URI.</param>
/// <param name="parameters">Parameters of the REPORT operation.</param>
/// <returns>An instance of <see cref="PropfindResponse" />.</returns>
Task<PropfindResponse> Report(string requestUri, ReportParameters parameters);

/// <summary>
/// Executes a REPORT operation.
/// </summary>
/// <param name="requestUri">The <see cref="Uri"/> to request.</param>
/// <param name="parameters">Parameters of the REPORT operation.</param>
/// <returns>An instance of <see cref="PropfindResponse" />.</returns>
Task<PropfindResponse> Report(Uri requestUri, ReportParameters parameters);
}
}
}
39 changes: 39 additions & 0 deletions src/WebDav.Client/Request/ReportParameters.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
using System.Collections.Generic;
using System.Threading;
using System.Xml.Linq;

namespace WebDav
{
/// <summary>
/// Parameters for the REPORT operation.
/// </summary>
public class ReportParameters
{
public ReportParameters()
{
Headers = new List<KeyValuePair<string, string>>();
CancellationToken = CancellationToken.None;
}

/// <summary>
/// Gets or sets a value indicating whether the method is to be applied only to the resource, to the resource and its internal members only, or the resource and all its members.
/// It corresponds to the WebDAV Depth header.
/// </summary>
public ApplyTo.Report? ApplyTo { get; set; }

/// <summary>
/// Gets or sets the XML request body for the REPORT operation.
/// </summary>
public XDocument? RequestBody { get; set; }

/// <summary>
/// Gets or sets the collection of headers.
/// </summary>
public IReadOnlyCollection<KeyValuePair<string, string>> Headers { get; set; }

/// <summary>
/// Gets or sets the cancellation token.
/// </summary>
public CancellationToken CancellationToken { get; set; }
}
}
42 changes: 42 additions & 0 deletions src/WebDav.Client/WebDavClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -768,6 +768,48 @@ public async Task<PropfindResponse> Search(Uri requestUri, SearchParameters para
return _propfindResponseParser!.Parse(responseContent, (int)response.StatusCode, response.ReasonPhrase);
}

/// <summary>
/// Executes a REPORT operation.
/// </summary>
/// <param name="requestUri">A string that represents the request URI.</param>
/// <param name="parameters">Parameters of the REPORT operation.</param>
/// <returns>An instance of <see cref="PropfindResponse" />.</returns>
public Task<PropfindResponse> Report(string requestUri, ReportParameters parameters)
{
return Report(CreateUri(requestUri), parameters);
}

/// <summary>
/// Executes a REPORT operation.
/// </summary>
/// <param name="requestUri">The <see cref="Uri"/> to request.</param>
/// <param name="parameters">Parameters of the REPORT operation.</param>
/// <returns>An instance of <see cref="PropfindResponse" />.</returns>
public async Task<PropfindResponse> Report(Uri requestUri, ReportParameters parameters)
{
Guard.NotNull(requestUri, "requestUri");

var headerBuilder = new HeaderBuilder();
if (parameters.ApplyTo.HasValue)
headerBuilder.Add(WebDavHeaders.Depth, DepthHeaderHelper.GetValueForReport(parameters.ApplyTo.Value));
var headers = headerBuilder
.AddWithOverwrite(parameters.Headers)
.Build();

var requestParams = new RequestParameters
{
Headers = headers,
Content = parameters.RequestBody != null
? new StringContent(parameters.RequestBody.ToStringWithDeclaration())
: null,
ContentType = new MediaTypeHeaderValue("text/xml")
};

var response = await _dispatcher!.Send(requestUri, WebDavMethod.Report, requestParams, parameters.CancellationToken).ConfigureAwait(false);
var responseContent = await ReadContentAsString(response.Content).ConfigureAwait(false);
return _propfindResponseParser!.Parse(responseContent, (int)response.StatusCode, response.ReasonPhrase);
}

/// <summary>
/// Sets the dispatcher of WebDAV requests.
/// </summary>
Expand Down