Skip to content

Commit 4996f38

Browse files
authored
Develop builtin content classes season 1 episode 2. (#132)
* Modify Content, File, Image, User. Add Workspace. * Add more classes. * Refactor * Register new types and fix tests. * Group * CalendarEvent Link Memo * refactor * Query, Task, WebHookSubscription * Implement choice fields (partial) * refactor * refactor * Fix tests. * Generic conversion between enum and single choice with item renaming option. * More enums * Remove unnecessary conversion in User class. * Remove unnecesary conversions from: Folder, Task. * Handle enums with FlagsAttribute. * Finalize flags enum handling. * Refactor * Refactor failed tests. * Fix a test + refactor. * Fix strongly typed references. * refactor * refactor. * Fix Repository.GetCurrentUserAsync (missing ".d"). * refactor. * refactor * First get the type from the json. * Additional multi type tests. * One complex integration test. * Implement AllRoles and DirectRoles * Group.RemoveMembersAsync uses new Invoke API. * refactor * ContentType class and ChildType properties. * More allowedChildTypes test. * New reference loader API and implementation (not fully tested). * Unit tests for LoadReferenceRequest. * Develop and test AssertLoadReferenceRequest. * Fix error message. * REFACTOR (nullability). * refactor.
1 parent 7a4104c commit 4996f38

54 files changed

Lines changed: 2431 additions & 504 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
using System.Diagnostics;
2+
3+
namespace SenseNet.Client.IntegrationTests;
4+
5+
[TestClass]
6+
public class ComplexTests : IntegrationTestBase
7+
{
8+
private readonly CancellationToken _cancel = new CancellationTokenSource().Token;
9+
10+
[TestMethod]
11+
public async Task IT_Complex_HandleUsersGroups_Membership()
12+
{
13+
var repository = await GetRepositoryCollection()
14+
.GetRepositoryAsync("local", CancellationToken.None).ConfigureAwait(false);
15+
16+
await repository.DeleteContentAsync(new[]
17+
{
18+
"/Root/IMS/Public/Group-3",
19+
"/Root/IMS/Public/Group-2",
20+
"/Root/IMS/Public/Group-1",
21+
"/Root/IMS/Public/User-1"
22+
}, true, _cancel).ConfigureAwait(false);
23+
24+
var publicDomainPath = "/Root/IMS/Public";
25+
var expand = new[] {"Members"};
26+
var select = new[] {"*", "Members/Id", "Members/Path", "Members/Name"};
27+
28+
var admin = await repository.LoadContentAsync<User>(1, _cancel).ConfigureAwait(false);
29+
30+
// ACT-1: Creation
31+
var user1 = repository.CreateContent<User>(publicDomainPath, null, "User-1");
32+
user1.FullName = "Rhoda Scott";
33+
user1.LoginName = "rscott";
34+
user1.Email = "rscott@example.com";
35+
user1.BirthDate = new DateTime(1938, 06, 03);
36+
user1.Enabled = true;
37+
user1.Gender = Gender.Female;
38+
user1.MaritalStatus = MaritalStatus.Married;
39+
user1.Manager = admin;
40+
user1.Password = "password1";
41+
await user1.SaveAsync(_cancel);
42+
43+
var group1 = repository.CreateContent<Group>(publicDomainPath, null, "Group-1");
44+
await group1.SaveAsync(_cancel);
45+
var group2 = repository.CreateContent<Group>(publicDomainPath, null, "Group-2");
46+
group2.Members = new Content[] {user1, group1};
47+
await group2.SaveAsync(_cancel);
48+
var group3 = repository.CreateContent<Group>(publicDomainPath, null, "Group-3");
49+
group3.Members = new Content[] { group1 };
50+
await group3.SaveAsync(_cancel);
51+
52+
// ASSERT-1
53+
Assert.IsTrue(user1.Id > 0);
54+
Assert.IsTrue(group1.Id > 0);
55+
Assert.IsTrue(group2.Id > 0);
56+
Assert.IsTrue(group3.Id > 0);
57+
// check membership
58+
var loadedGroup2 = await repository.LoadContentAsync<Group>(
59+
new LoadContentRequest {ContentId = group2.Id, Expand = expand, Select = select}, _cancel).ConfigureAwait(false);
60+
Assert.IsNotNull(loadedGroup2?.Members);
61+
var loadedMemberNames2 = loadedGroup2.Members.Select(c => c.Name).OrderBy(x => x);
62+
Assert.AreEqual("Group-1 User-1", string.Join(" ", loadedMemberNames2));
63+
var loadedGroup3 = await repository.LoadContentAsync<Group>(
64+
new LoadContentRequest {ContentId = group3.Id, Expand = expand, Select = select}, _cancel).ConfigureAwait(false);
65+
Assert.IsNotNull(loadedGroup2?.Members);
66+
var loadedMemberNames3 = loadedGroup3.Members.Select(c => c.Name).OrderBy(x => x);
67+
Assert.AreEqual("Group-1", string.Join(" ", loadedMemberNames3));
68+
69+
// ACT-2: Move user1 from group2 to group3
70+
var sourceMembers = loadedGroup2.Members.ToArray();
71+
var membersToMove = sourceMembers.Where(x => x.Name == "User-1").ToArray();
72+
loadedGroup2.Members = sourceMembers.Except(membersToMove);
73+
await loadedGroup2.SaveAsync(_cancel).ConfigureAwait(false);
74+
loadedGroup3.Members = loadedGroup3.Members.Union(membersToMove);
75+
await loadedGroup3.SaveAsync(_cancel).ConfigureAwait(false);
76+
77+
// ASSERT-2 check membership
78+
var reloadedGroup2 = await repository.LoadContentAsync<Group>(
79+
new LoadContentRequest {ContentId = group2.Id, Expand = expand, Select = select}, _cancel).ConfigureAwait(false);
80+
Assert.IsNotNull(reloadedGroup2?.Members);
81+
var reloadedMemberNames2 = reloadedGroup2.Members.Select(c => c.Name).OrderBy(x => x);
82+
Assert.AreEqual("Group-1", string.Join(" ", reloadedMemberNames2));
83+
var reloadedGroup3 = await repository.LoadContentAsync<Group>(
84+
new LoadContentRequest {ContentId = group3.Id, Expand = expand, Select = select}, _cancel).ConfigureAwait(false);
85+
Assert.IsNotNull(reloadedGroup3?.Members);
86+
var reloadedMemberNames3 = reloadedGroup3.Members.Select(c => c.Name).OrderBy(x => x);
87+
Assert.AreEqual("Group-1 User-1", string.Join(" ", reloadedMemberNames3));
88+
89+
// ACT-3: Make circle: Add group2 to group3, group3 to group1
90+
await group3.AddMembersAsync(new[] { group2.Id }, _cancel).ConfigureAwait(false);
91+
await group1.AddMembersAsync(new[] { group3.Id }, _cancel).ConfigureAwait(false);
92+
93+
// ASSERT-3: check user roles
94+
var reloadedUser = await repository.LoadContentAsync<User>(new LoadContentRequest
95+
{
96+
ContentId = user1.Id,
97+
Expand = new []{"AllRoles", "DirectRoles"},
98+
Select = new []{"*", "AllRoles/Name", "DirectRoles/Name" }
99+
}, _cancel).ConfigureAwait(false);
100+
var allRoles = reloadedUser.AllRoles.Select(x => x.Name).OrderBy(x => x);
101+
var directRoles = reloadedUser.DirectRoles.Select(x => x.Name).OrderBy(x => x);
102+
Assert.AreEqual("Group-1 Group-2 Group-3", string.Join(" ", allRoles));
103+
Assert.AreEqual("Group-3", string.Join(" ", directRoles));
104+
105+
// ACT-3: Break the circle: remove group3 to group1
106+
await group1.RemoveMembersAsync(new[] { group3.Id }, _cancel).ConfigureAwait(false);
107+
108+
// ASSERT-3: check user roles
109+
reloadedUser = await repository.LoadContentAsync<User>(new LoadContentRequest
110+
{
111+
ContentId = user1.Id,
112+
Expand = new[] { "AllRoles", "DirectRoles" },
113+
Select = new[] { "*", "AllRoles/Name", "DirectRoles/Name" }
114+
}, _cancel).ConfigureAwait(false);
115+
allRoles = reloadedUser.AllRoles.Select(x => x.Name).OrderBy(x => x);
116+
directRoles = reloadedUser.DirectRoles.Select(x => x.Name).OrderBy(x => x);
117+
Assert.AreEqual("Group-3", string.Join(" ", allRoles));
118+
Assert.AreEqual("Group-3", string.Join(" ", directRoles));
119+
}
120+
}

src/SenseNet.Client.IntegrationTests/ContentTests.cs

Lines changed: 132 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -202,6 +202,61 @@ private async Task CreateStructureForDepthTests(IRepository repository, string r
202202
}
203203
}
204204

205+
[TestMethod]
206+
public async Task IT_Content_Collection_MultiType()
207+
{
208+
var cancel = new CancellationTokenSource(TimeSpan.FromSeconds(10)).Token;
209+
var repository = await GetRepositoryCollection()
210+
.GetRepositoryAsync("local", cancel).ConfigureAwait(false);
211+
212+
// ACT
213+
var request = new LoadCollectionRequest()
214+
{
215+
Path = "/Root/IMS/BuiltIn/Portal",
216+
Select = new[] { "Name", "Path", "Type" },
217+
OrderBy = new[] { "Path" }
218+
};
219+
var result = await repository.LoadCollectionAsync(request, cancel).ConfigureAwait(false);
220+
221+
// ASSERT
222+
var contents = result.ToArray();
223+
var types = contents
224+
.Select(x => x.GetType())
225+
.Distinct()
226+
.Select(t => t.Name)
227+
.OrderBy(n => n)
228+
.ToArray();
229+
Assert.AreEqual("Group User", string.Join(" ", types));
230+
}
231+
232+
[TestMethod]
233+
public async Task IT_Content_Query_MultiType()
234+
{
235+
var cancel = new CancellationTokenSource(TimeSpan.FromSeconds(10)).Token;
236+
var repository = await GetRepositoryCollection()
237+
.GetRepositoryAsync("local", cancel).ConfigureAwait(false);
238+
239+
// ACT
240+
var request = new QueryContentRequest
241+
{
242+
ContentQuery = "InTree:'/Root/IMS'",
243+
Select = new[] { "Name", "Path", "Type" },
244+
OrderBy = new[] { "Path" }
245+
};
246+
var result = await repository.QueryAsync(request, cancel).ConfigureAwait(false);
247+
248+
// ASSERT
249+
var contents = result.ToArray();
250+
var types = contents
251+
.Select(x => x.GetType())
252+
.Distinct()
253+
.Select(t => t.Name)
254+
.OrderBy(n => n)
255+
.ToArray();
256+
//Assert.AreEqual("Domains Domain Group Image OrganizationalUnit User", string.Join(" ", types));
257+
Assert.AreEqual("Content Domain Group Image OrganizationalUnit User", string.Join(" ", types));
258+
}
259+
205260
/* ================================================================================================== ACTIONS */
206261

207262
public class File : Content
@@ -421,7 +476,7 @@ public TestMemo(IRestCaller restCaller, ILogger<TestMemo> logger) : base(restCal
421476
[TestMethod]
422477
public async Task IT_Content_T_Update()
423478
{
424-
var cancel = new CancellationTokenSource(TimeSpan.FromSeconds(10)).Token;
479+
var cancel = new CancellationTokenSource().Token;
425480
var repository =
426481
await GetRepositoryCollection(
427482
services => { services.RegisterGlobalContentType<TestMemo>("Memo"); })
@@ -485,7 +540,7 @@ await GetRepositoryCollection(
485540
[TestMethod]
486541
public async Task IT_Content_T_Update_ReferenceList()
487542
{
488-
var cancel = new CancellationTokenSource(TimeSpan.FromSeconds(10)).Token;
543+
var cancel = new CancellationTokenSource(TimeSpan.FromSeconds(120)).Token;
489544
var repository =
490545
await GetRepositoryCollection(
491546
services => { services.RegisterGlobalContentType<TestMemo>("Memo"); })
@@ -508,22 +563,22 @@ await GetRepositoryCollection(
508563

509564
// 2 - Create container and some memos
510565
var container = repository.CreateContent(rootPath, containerType, containerName);
511-
await container.SaveAsync().ConfigureAwait(false);
566+
await container.SaveAsync(cancel).ConfigureAwait(false);
512567
var referredMemos = new Content[3];
513568
for (int i = 0; i < referredMemos.Length; i++)
514569
{
515570
referredMemos[i] = repository.CreateContent(container.Path, contentTypeName, $"ReferredMemo{i + 1}");
516571
//referredMemos[i]["MemoType"] = Array.Empty<string>();
517572
//referredMemos[i]["MemoType"] = new[] { "generic" };
518573
((TestMemo)referredMemos[i]).MemoType = Array.Empty<string>();
519-
await referredMemos[i].SaveAsync().ConfigureAwait(false);
574+
await referredMemos[i].SaveAsync(cancel).ConfigureAwait(false);
520575
}
521576

522577
// 3 - Create brand new content and test its existence
523578
var content = repository.CreateContent<TestMemo>(containerPath, null, contentName);
524579
content.MemoType = Array.Empty<string>();
525580
content.SeeAlso = referredMemos.Take(2).ToList();
526-
await content.SaveAsync().ConfigureAwait(false);
581+
await content.SaveAsync(cancel).ConfigureAwait(false);
527582
var contentId = content.Id;
528583
Assert.IsTrue(contentId > 0);
529584

@@ -546,7 +601,7 @@ await GetRepositoryCollection(
546601

547602
// 6 - Update content
548603
loadedContent.SeeAlso[1] = referredMemos[2];
549-
await loadedContent.SaveAsync().ConfigureAwait(false);
604+
await loadedContent.SaveAsync(cancel).ConfigureAwait(false);
550605

551606
// 7 - Load updated content
552607
var reloadedContent = await repository.LoadContentAsync<TestMemo>(expandedRequest, cancel).ConfigureAwait(false);
@@ -574,7 +629,7 @@ await GetRepositoryCollection(
574629
wrongContent.SeeAlso.Add(referredMemos[2]);
575630
try
576631
{
577-
await wrongContent.SaveAsync().ConfigureAwait(false);
632+
await wrongContent.SaveAsync(cancel).ConfigureAwait(false);
578633
Assert.Fail("The expected ApplicationException was not thrown.");
579634
}
580635
catch (ApplicationException e)
@@ -590,6 +645,76 @@ await GetRepositoryCollection(
590645
Assert.IsFalse(await repository.IsContentExistsAsync(path, cancel).ConfigureAwait(false));
591646
}
592647

648+
/* ============================================================================================= ALLOWED CHILD TYPES */
649+
650+
[TestMethod]
651+
public async Task IT_Content_AllowedChildTypes()
652+
{
653+
var cancel = new CancellationTokenSource().Token;
654+
var repository = await GetRepositoryCollection()
655+
.GetRepositoryAsync("local", cancel).ConfigureAwait(false);
656+
657+
await repository.DeleteContentAsync(new[] {"/Root/Content/DocLib-1"}, true, cancel).ConfigureAwait(false);
658+
659+
// ACT-1: /Root/Content
660+
var request = new LoadContentRequest
661+
{
662+
Path = "/Root/Content",
663+
Expand = new[] {"AllowedChildTypes", "EffectiveAllowedChildTypes"},
664+
Select = new[] {"*", "AllowedChildTypes/Name", "EffectiveAllowedChildTypes/Name" }
665+
};
666+
var content1 = await repository.LoadContentAsync<Workspace>(request, cancel).ConfigureAwait(false);
667+
668+
// ASSERT-1: AllowedChildTypes fields are not null, not empty
669+
Assert.IsNotNull(content1.AllowedChildTypes);
670+
Assert.IsNotNull(content1.EffectiveAllowedChildTypes);
671+
Assert.IsTrue(content1.AllowedChildTypes.Any());
672+
Assert.IsTrue(content1.EffectiveAllowedChildTypes.Any());
673+
674+
// ACT-2: Create a doclib
675+
var docLib1 = repository.CreateContent("/Root/Content", "DocumentLibrary", "DocLib-1");
676+
//docLib1.AllowedChildTypes = new ContentType[] { taskListContentType, memoListContentType};
677+
await docLib1.SaveAsync(cancel).ConfigureAwait(false);
678+
679+
// ASSERT-2
680+
request = new LoadContentRequest
681+
{
682+
Path = "/Root/Content/DocLib-1",
683+
Expand = new[] { "AllowedChildTypes", "EffectiveAllowedChildTypes" },
684+
Select = new[] { "*", "AllowedChildTypes/Name", "EffectiveAllowedChildTypes/Name" }
685+
};
686+
docLib1 = await repository.LoadContentAsync<Content>(request, cancel).ConfigureAwait(false);
687+
var allowedChildTypes = docLib1.AllowedChildTypes.Select(x => x.Name).OrderBy(x => x).ToArray();
688+
var effectiveChildTypes = docLib1.EffectiveAllowedChildTypes.Select(x => x.Name).OrderBy(x => x).ToArray();
689+
Assert.AreEqual("", string.Join(" ", allowedChildTypes));
690+
Assert.AreEqual("File Folder SystemFolder", string.Join(" ", effectiveChildTypes));
691+
692+
// ACT-3: Update a doclib with childType control
693+
var taskListContentType = await repository.LoadContentAsync<ContentType>(
694+
"/Root/System/Schema/ContentTypes/GenericContent/Folder/ContentList/ItemList/TaskList", cancel)
695+
.ConfigureAwait(false);
696+
var memoListContentType = await repository.LoadContentAsync<ContentType>(
697+
"/Root/System/Schema/ContentTypes/GenericContent/Folder/ContentList/ItemList/MemoList", cancel)
698+
.ConfigureAwait(false);
699+
docLib1.AllowedChildTypes = new ContentType[] { taskListContentType, memoListContentType};
700+
await docLib1.SaveAsync(cancel).ConfigureAwait(false);
701+
702+
// ASSERT-3
703+
request = new LoadContentRequest
704+
{
705+
Path = "/Root/Content/DocLib-1",
706+
Expand = new[] { "AllowedChildTypes", "EffectiveAllowedChildTypes" },
707+
Select = new[] { "*", "AllowedChildTypes/Name", "EffectiveAllowedChildTypes/Name" }
708+
};
709+
docLib1 = await repository.LoadContentAsync<Content>(request, cancel).ConfigureAwait(false);
710+
allowedChildTypes = docLib1.AllowedChildTypes?.Select(x => x.Name).OrderBy(x => x)
711+
.ToArray() ?? Array.Empty<string>();
712+
effectiveChildTypes = docLib1.EffectiveAllowedChildTypes?.Select(x => x.Name).OrderBy(x => x)
713+
.ToArray() ?? Array.Empty<string>();
714+
Assert.AreEqual("MemoList TaskList", string.Join(" ", allowedChildTypes));
715+
Assert.AreEqual("MemoList SystemFolder TaskList", string.Join(" ", effectiveChildTypes));
716+
}
717+
593718
/* ================================================================================================== OPERATIONS */
594719

595720
[TestMethod]

src/SenseNet.Client.IntegrationTests/Legacy/ContentTests.cs

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
namespace SenseNet.Client.IntegrationTests.Legacy
77
{
88
[TestClass]
9-
public class ContentTests
9+
public class ContentTests : IntegrationTestBase
1010
{
1111
[ClassInitialize]
1212
public static void ClassInitializer(TestContext context)
@@ -26,19 +26,25 @@ public async Task Content_Create()
2626
[TestMethod]
2727
public async Task Content_Modify()
2828
{
29+
var cancel = new CancellationTokenSource().Token;
30+
var repository = await GetRepositoryCollection()
31+
.GetRepositoryAsync("local", CancellationToken.None).ConfigureAwait(false);
32+
2933
ClientContext.Current.Server.IsTrusted = true;
3034

31-
var content0 = Content.CreateNew("/Root", "Folder", Guid.NewGuid().ToString());
35+
//var content0 = Content.CreateNew("/Root", "Folder", Guid.NewGuid().ToString());
36+
var content0 = repository.CreateContent("/Root", "Folder", Guid.NewGuid().ToString());
3237
content0["Index"] = 42;
33-
await content0.SaveAsync().ConfigureAwait(false);
38+
await content0.SaveAsync(cancel).ConfigureAwait(false);
3439
var contentId = content0.Id;
3540

36-
var content1 = await Content.LoadAsync(contentId).ConfigureAwait(false);
41+
//var content1 = await Content.LoadAsync(contentId).ConfigureAwait(false);
42+
var content1 = await repository.LoadContentAsync(contentId, cancel).ConfigureAwait(false);
3743
//var indexBefore = ((JValue)content1["Index"]).Value<int>();
3844
content1["Index"] = 142;
39-
await content1.SaveAsync().ConfigureAwait(false);
45+
await content1.SaveAsync(cancel).ConfigureAwait(false);
4046

41-
var content2 = await Content.LoadAsync(contentId).ConfigureAwait(false);
47+
var content2 = await repository.LoadContentAsync(contentId, cancel).ConfigureAwait(false);
4248
var indexAfter = ((JValue)content2["Index"]).Value<int>();
4349

4450
Assert.AreEqual(142, indexAfter);

src/SenseNet.Client.IntegrationTests/Legacy/FieldTests.cs

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -11,16 +11,6 @@ public static void ClassInitializer(TestContext context)
1111
Initializer.InitializeServer(context);
1212
}
1313

14-
[TestMethod]
15-
public async Task ReferenceField_LoadReferences()
16-
{
17-
var references = (await Content.LoadReferencesAsync("/Root", "CreatedBy")).ToArray();
18-
Assert.AreEqual(1, references.Single().Id);
19-
20-
var single = await Content.LoadReferenceAsync("/Root", "CreatedBy");
21-
Assert.AreEqual(1, single.Id);
22-
}
23-
2414
[TestMethod]
2515
public async Task LongTextField_Emoji()
2616
{

0 commit comments

Comments
 (0)