Skip to content

Commit

Permalink
Wrapping up basic search implementation + refactoring
Browse files Browse the repository at this point in the history
  • Loading branch information
skazantsev committed Mar 27, 2022
1 parent e0318b7 commit dd6539d
Show file tree
Hide file tree
Showing 10 changed files with 335 additions and 168 deletions.
183 changes: 162 additions & 21 deletions src/WebDav.Client.Tests/Methods/SearchTests.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
using System;
using System.Collections.Generic;
using System.Text;
using NSubstitute;
using System;
using System.Threading;
using System.Xml.Linq;
using WebDav.Client.Tests.TestDoubles;
using Xunit;
Expand All @@ -12,32 +12,173 @@ public class SearchTests
[Fact]
public async void When_RequestIsSuccessfull_Should_ReturnStatusCode200()
{
var client = new WebDavClient().SetWebDavDispatcher(Dispatcher.Mock());
var response1 = await client.Search(new Uri("http://example.com/"), new Request.SearchParameters()
var dispatcher = Dispatcher.Mock();
var client = new WebDavClient().SetWebDavDispatcher(dispatcher);
var response = await client.Search("http://example.com/", new SearchParameters
{
SearchPath = "/root/",
SearchKeyword = "test%",
SelectProperties = new[]
{
new XElement("{DAV:}displayname"),
new XElement("{DAV:}getcontenttype")
},
WhereProperties = new[]
{
new XElement("{DAV:}displayname")
}
Scope = "/",
SearchProperty = "{DAV:}displayname",
SearchKeyword = "test%"
});

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

[Fact]
public async void When_PassingNoSelectProperties_Should_IncludeAllProp()
{
var dispatcher = Dispatcher.Mock();
var client = new WebDavClient().SetWebDavDispatcher(dispatcher);
await client.Search("http://example.com/", new SearchParameters()
{
Scope = "/root/",
SearchProperty = "{DAV:}displayname",
SearchKeyword = "test%"
});

Assert.Equal(200, response1.StatusCode);
const string expectedContent =
@"<?xml version=""1.0"" encoding=""utf-8""?>
<D:searchrequest xmlns:D=""DAV:"">
<D:basicsearch>
<D:select>
<D:allprop />
</D:select>
<D:from>
<D:scope>
<D:href>/root/</D:href>
<D:depth>infinity</D:depth>
</D:scope>
</D:from>
<D:where>
<D:like>
<D:prop>
<D:displayname />
</D:prop>
<D:literal>test%</D:literal>
</D:like>
</D:where>
</D:basicsearch>
</D:searchrequest>";
await dispatcher.Received(1).Send(
Arg.Any<Uri>(),
WebDavMethod.Search,
Arg.Is(Predicates.CompareRequestContent(expectedContent)),
CancellationToken.None
);
}

[Fact]
public async void When_RequestIsBadRequest_Should_ReturnStatusCode400()
public async void When_PassingSelectProperties_Should_IncludeThem()
{
var client = new WebDavClient().SetWebDavDispatcher(Dispatcher.Mock());
var response1 = await client.Search(new Uri("http://example.com/"), new Request.SearchParameters());
Assert.Equal(400, response1.StatusCode);
var dispatcher = Dispatcher.Mock();
var client = new WebDavClient().SetWebDavDispatcher(dispatcher);
await client.Search("http://example.com/", new SearchParameters()
{
Scope = "/root/",
SearchProperty = "{DAV:}displayname",
SearchKeyword = "test%",
SelectProperties = new XName[]
{
"{DAV:}displayname",
"{DAV:}getcontenttype"
}
});

const string expectedContent =
@"<?xml version=""1.0"" encoding=""utf-8""?>
<D:searchrequest xmlns:D=""DAV:"">
<D:basicsearch>
<D:select>
<D:prop>
<D:displayname />
<D:getcontenttype />
</D:prop>
</D:select>
<D:from>
<D:scope>
<D:href>/root/</D:href>
<D:depth>infinity</D:depth>
</D:scope>
</D:from>
<D:where>
<D:like>
<D:prop>
<D:displayname />
</D:prop>
<D:literal>test%</D:literal>
</D:like>
</D:where>
</D:basicsearch>
</D:searchrequest>";
await dispatcher.Received(1).Send(
Arg.Any<Uri>(),
WebDavMethod.Search,
Arg.Is(Predicates.CompareRequestContent(expectedContent)),
CancellationToken.None
);
}

[Fact]
public async void Should_SupportPropertiesWithDifferentNamespaces()
{
var dispatcher = Dispatcher.Mock();
var client = new WebDavClient().SetWebDavDispatcher(dispatcher);
var response = await client.Search("http://example.com/", new SearchParameters()
{
Scope = "/root/",
SearchProperty = "{NS1}prop1",
SearchKeyword = "test%",
SelectProperties = new XName[]
{
"{NS1}prop1",
"{http://ns2.example.com}prop2",
"{DEFAULT}prop3",
"{DAV:}prop4"
},
Namespaces = new []
{
new NamespaceAttr("NS1", "NS1"),
new NamespaceAttr("NS2", "http://ns2.example.com"),
new NamespaceAttr("DEFAULT")
}
});

Assert.Equal(200, response.StatusCode);

const string expectedContent =
@"<?xml version=""1.0"" encoding=""utf-8""?>
<D:searchrequest xmlns:D=""DAV:"" xmlns:NS1=""NS1"" xmlns:NS2=""http://ns2.example.com"" xmlns=""DEFAULT"">
<D:basicsearch>
<D:select>
<D:prop>
<NS1:prop1 />
<NS2:prop2 />
<prop3 />
<D:prop4 />
</D:prop>
</D:select>
<D:from>
<D:scope>
<D:href>/root/</D:href>
<D:depth>infinity</D:depth>
</D:scope>
</D:from>
<D:where>
<D:like>
<D:prop>
<NS1:prop1 />
</D:prop>
<D:literal>test%</D:literal>
</D:like>
</D:where>
</D:basicsearch>
</D:searchrequest>";
await dispatcher.Received(1).Send(
Arg.Any<Uri>(),
WebDavMethod.Search,
Arg.Is(Predicates.CompareRequestContent(expectedContent)),
CancellationToken.None
);
}
}
}
5 changes: 1 addition & 4 deletions src/WebDav.Client/Core/PropfindRequestType.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,12 @@
/// Specifies a type of PROPFIND request.
/// AllProperties: 'allprop' + 'include'.
/// NamedProperties: 'prop'.
/// AllPropertiesImplied: no body.
/// </summary>
public enum PropfindRequestType
{
AllProperties,
NamedProperties,

/// <summary>
/// Don't pass a body
/// </summary>
AllPropertiesImplied,
}
}
17 changes: 12 additions & 5 deletions src/WebDav.Client/IWebDavClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
using System.IO;
using System.Net.Http;
using System.Threading.Tasks;
using WebDav.Client.Request;

namespace WebDav
{
Expand Down Expand Up @@ -403,11 +402,19 @@ public interface IWebDavClient : IDisposable
Task<WebDavResponse> Unlock(Uri requestUri, UnlockParameters parameters);

/// <summary>
/// Use WebDav SEARCH Method.
/// Executes a SEARCH operation.
/// </summary>
/// <param name="requestUri">Request uri.</param>
/// <param name="parameters">SEARCH parameters.</param>
/// <returns></returns>
/// <param name="requestUri">A string that represents the request URI.</param>
/// <param name="parameters">Parameters of the SEARCH operation.</param>
/// <returns>An instance of <see cref="PropfindResponse" />.</returns>
Task<PropfindResponse> Search(string requestUri, SearchParameters parameters);

/// <summary>
/// Executes a SEARCH operation.
/// </summary>
/// <param name="requestUri">The <see cref="Uri"/> to request.</param>
/// <param name="parameters">Parameters of the SEARCH operation.</param>
/// <returns>An instance of <see cref="PropfindResponse" />.</returns>
Task<PropfindResponse> Search(Uri requestUri, SearchParameters parameters);
}
}
19 changes: 9 additions & 10 deletions src/WebDav.Client/Request/LockRequestBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,15 @@ internal static class LockRequestBuilder
public static string BuildRequestBody(LockParameters lockParams)
{
var doc = new XDocument(new XDeclaration("1.0", "utf-8", null));
var lockinfo = new XElement("{DAV:}lockinfo", new XAttribute(XNamespace.Xmlns + "D", "DAV:"));
lockinfo.Add(GetLockScope(lockParams.LockScope));
lockinfo.Add(GetLockType());
var lockinfo = new XElement(
"{DAV:}lockinfo",
new XAttribute(XNamespace.Xmlns + "D", "DAV:"),
GetLockScope(lockParams.LockScope),
new XElement(
"{DAV:}locktype",
new XElement("{DAV:}write")
)
);
if (lockParams.Owner != null)
lockinfo.Add(GetLockOwner(lockParams.Owner));

Expand All @@ -36,13 +42,6 @@ private static XElement GetLockScope(LockScope lockScope)
return lockscope;
}

private static XElement GetLockType()
{
var locktype = new XElement("{DAV:}locktype");
locktype.Add(new XElement("{DAV:}write"));
return locktype;
}

private static XElement GetLockOwner(LockOwner lockOwner)
{
var owner = new XElement("{DAV:}owner");
Expand Down
7 changes: 5 additions & 2 deletions src/WebDav.Client/Request/PropfindRequestBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,11 @@ public static string BuildRequest(
private static string BuildAllPropRequest(IReadOnlyCollection<XName> customProperties, IReadOnlyCollection<NamespaceAttr> namespaces)
{
var doc = new XDocument(new XDeclaration("1.0", "utf-8", null));
var propfind = new XElement("{DAV:}propfind", new XAttribute(XNamespace.Xmlns + "D", "DAV:"));
propfind.Add(new XElement("{DAV:}allprop"));
var propfind = new XElement(
"{DAV:}propfind",
new XAttribute(XNamespace.Xmlns + "D", "DAV:"),
new XElement("{DAV:}allprop")
);
if (customProperties.Any())
{
var include = new XElement("{DAV:}include");
Expand Down
39 changes: 21 additions & 18 deletions src/WebDav.Client/Request/ProppatchRequestBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,28 +19,31 @@ public static string BuildRequestBody(IDictionary<XName, string> propertiesToSet
}
if (propertiesToSet.Any())
{
var setEl = new XElement("{DAV:}set");
var propEl = new XElement("{DAV:}prop");
foreach (var prop in propertiesToSet)
{
var el = new XElement(prop.Key);
el.SetInnerXml(prop.Value);
propEl.Add(el);
}
setEl.Add(propEl);
propertyupdate.Add(setEl);
var set = new XElement(
"{DAV:}set",
new XElement(
"{DAV:}prop",
propertiesToSet.Select(prop =>
{
var el = new XElement(prop.Key);
el.SetInnerXml(prop.Value);
return el;
}).ToArray()
)
);
propertyupdate.Add(set);
}

if (propertiesToRemove.Any())
{
var removeEl = new XElement("{DAV:}remove");
var propEl = new XElement("{DAV:}prop");
foreach (var prop in propertiesToRemove)
{
propEl.Add(new XElement(prop));
}
removeEl.Add(propEl);
propertyupdate.Add(removeEl);
var remove = new XElement(
"{DAV:}remove",
new XElement(
"{DAV:}prop",
propertiesToRemove.Select(prop => new XElement(prop)).ToArray()
)
);
propertyupdate.Add(remove);
}

doc.Add(propertyupdate);
Expand Down
Loading

0 comments on commit dd6539d

Please sign in to comment.