Skip to content

Commit 1d91620

Browse files
authored
SF-3639 Show Paratext users not in SF for Back Translation projects (#3578)
1 parent d709b0f commit 1d91620

File tree

4 files changed

+92
-45
lines changed

4 files changed

+92
-45
lines changed

src/SIL.XForge.Scripture/Services/ParatextService.cs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -818,6 +818,22 @@ you will end up here */
818818
}
819819
}
820820

821+
// Add all other users that are in the project that are not registered users in SF
822+
foreach (
823+
ProjectUser projectUser in remotePtProject.SourceUsers?.Users.Where(pu =>
824+
users.All(u => u.Username != pu.UserName)
825+
) ?? []
826+
)
827+
{
828+
users.Add(
829+
new ParatextProjectUser
830+
{
831+
Role = ConvertFromUserRole(projectUser.Role),
832+
Username = projectUser.UserName,
833+
}
834+
);
835+
}
836+
821837
// if the project is a back translation, we want to check if user is still
822838
// on project with permission to sync or return an error to UI
823839
if (!users.Select(u => u.Id).ToList().Contains(userSecret.Id))

src/SIL.XForge.Scripture/Services/ParatextSyncRunner.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1723,7 +1723,9 @@ private async Task CompleteSync(bool successful, bool canRollbackParatext, Cance
17231723
}
17241724
else
17251725
{
1726-
ptUserRoles = _paratextUsers.ToDictionary(u => u.ParatextId, u => u.Role);
1726+
ptUserRoles = _paratextUsers
1727+
.Where(u => !string.IsNullOrWhiteSpace(u.ParatextId))
1728+
.ToDictionary(u => u.ParatextId, u => u.Role);
17271729
}
17281730

17291731
var userIdsToRemove = new List<string>();

test/SIL.XForge.Scripture.Tests/Services/ParatextServiceTests.cs

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4688,9 +4688,11 @@ public async Task GetParatextUsersAsync_UsesTheRepositoryForUnregisteredProjects
46884688
project,
46894689
CancellationToken.None
46904690
);
4691-
Assert.That(users.Count, Is.EqualTo(2));
4692-
Assert.That(users.First(), Is.EqualTo(env.ParatextProjectUser01));
4693-
Assert.That(users.Last(), Is.EqualTo(env.ParatextProjectUser02));
4691+
Assert.That(
4692+
users,
4693+
Is.EqualTo([env.ParatextProjectUser01, env.ParatextProjectUser02, env.ParatextProjectUser03]),
4694+
"map of PT roles should only include PT users"
4695+
);
46944696
}
46954697

46964698
[Test]
@@ -4752,7 +4754,7 @@ public async Task GetParatextUsersAsync_UnregisteredProject_SkipsNonPTUsers()
47524754
proj.UserRoles.Add(env.User04, SFProjectRole.CommunityChecker);
47534755
env.AddProjectRepository(proj);
47544756
env.SetSharedRepositorySource(userSecret, UserRoles.Administrator);
4755-
var projects = await env.RealtimeService.GetRepository<SFProject>().GetAllAsync();
4757+
IReadOnlyList<SFProject> projects = await env.RealtimeService.GetRepository<SFProject>().GetAllAsync();
47564758
SFProject project = projects.First();
47574759
Assert.That(project.UserRoles.Count, Is.EqualTo(4), "setup");
47584760
env.MakeRegistryClientReturn(env.NotFoundHttpResponseMessage);
@@ -4762,9 +4764,11 @@ public async Task GetParatextUsersAsync_UnregisteredProject_SkipsNonPTUsers()
47624764
project,
47634765
CancellationToken.None
47644766
);
4765-
Assert.That(users.Count, Is.EqualTo(2), "map of PT roles should only include PT users");
4766-
Assert.That(users.First(), Is.EqualTo(env.ParatextProjectUser01));
4767-
Assert.That(users.Last(), Is.EqualTo(env.ParatextProjectUser02));
4767+
Assert.That(
4768+
users,
4769+
Is.EqualTo([env.ParatextProjectUser01, env.ParatextProjectUser02, env.ParatextProjectUser03]),
4770+
"map of PT roles should only include PT users"
4771+
);
47684772
}
47694773

47704774
[Test]
@@ -7015,6 +7019,9 @@ public TestEnvironment()
70157019
Username = Username02,
70167020
};
70177021

7022+
public ParatextProjectUser ParatextProjectUser03 =>
7023+
new ParatextProjectUser { Role = SFProjectRole.Administrator, Username = Username03 };
7024+
70187025
public static HttpResponseMessage MakeOkHttpResponseMessage(string content) =>
70197026
new HttpResponseMessage(HttpStatusCode.OK)
70207027
{

test/SIL.XForge.Scripture.Tests/Services/ParatextSyncRunnerTests.cs

Lines changed: 59 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -381,7 +381,7 @@ await env
381381
env.ParatextService.DidNotReceive().PutNotes(Arg.Any<UserSecret>(), "target", Arg.Any<XElement>());
382382

383383
SFProject project = env.VerifyProjectSync(true);
384-
Assert.That(project.ParatextUsers.Count, Is.EqualTo(2));
384+
Assert.That(project.ParatextUsers.Count, Is.EqualTo(4));
385385
Assert.That(project.UserRoles["user01"], Is.EqualTo(SFProjectRole.Administrator));
386386
Assert.That(project.UserRoles["user02"], Is.EqualTo(SFProjectRole.Translator));
387387
}
@@ -425,7 +425,7 @@ await env
425425
env.ParatextService.Received(2).PutNotes(Arg.Any<UserSecret>(), "target", Arg.Any<XElement>());
426426

427427
SFProject project = env.GetProject();
428-
Assert.That(project.ParatextUsers.Count, Is.EqualTo(2));
428+
Assert.That(project.ParatextUsers.Count, Is.EqualTo(4));
429429
env.VerifyProjectSync(true);
430430
}
431431

@@ -468,7 +468,7 @@ await env
468468
Assert.That(env.GetText("project02", "MRK", 2).DeepEquals(delta), Is.True);
469469

470470
SFProject project = env.GetProject();
471-
Assert.That(project.ParatextUsers.Count, Is.EqualTo(2));
471+
Assert.That(project.ParatextUsers.Count, Is.EqualTo(4));
472472
env.VerifyProjectSync(true);
473473

474474
// Verify the sync metrics
@@ -1822,7 +1822,7 @@ public async Task SyncAsync_UpdatesParatextComments()
18221822
var book = new Book("MAT", 1, true);
18231823
env.SetupSFData(true, false, false, true, book);
18241824
env.SetupPTData(book);
1825-
string dataId = "dataId01";
1825+
const string dataId = "dataId01";
18261826
env.SetupNoteChanges(dataId, "thread01", "MAT 1:1", false);
18271827
SyncMetricInfo info = new SyncMetricInfo(0, 0, 1);
18281828
env.ParatextService.UpdateParatextCommentsAsync(
@@ -1848,7 +1848,10 @@ await env
18481848
);
18491849

18501850
SFProject project = env.GetProject();
1851-
Assert.That(project.ParatextUsers.Select(u => u.Username), Is.EquivalentTo(new[] { "User 1", "User 2" }));
1851+
Assert.That(
1852+
project.ParatextUsers.Select(u => u.Username),
1853+
Is.EquivalentTo(["User 1", "User 2", "User 4", "User 5"])
1854+
);
18521855
SyncMetrics syncMetrics = env.GetSyncMetrics("project01");
18531856
Assert.That(syncMetrics.ParatextNotes, Is.EqualTo(info));
18541857
}
@@ -1897,7 +1900,10 @@ await env
18971900
SFProject project = env.GetProject();
18981901
NoteThread noteThread = env.GetNoteThread("project01", "dataId01");
18991902
Assert.That(noteThread.Notes[0].OwnerRef, Is.EqualTo("user03"));
1900-
Assert.That(project.ParatextUsers.Select(u => u.Username), Is.EquivalentTo(new[] { "User 1", "User 2" }));
1903+
Assert.That(
1904+
project.ParatextUsers.Select(u => u.Username),
1905+
Is.EquivalentTo(["User 1", "User 2", "User 4", "User 5"])
1906+
);
19011907
SyncMetrics syncMetrics = env.GetSyncMetrics("project01");
19021908
Assert.That(syncMetrics.ParatextNotes, Is.EqualTo(info));
19031909
}
@@ -1909,7 +1915,7 @@ public async Task SyncAsync_SavesNewParatextUser()
19091915
env.SetupSFData(false, false, false, true);
19101916
env.SetupPTData(new Book("MAT", 1, true));
19111917
SFProject project = env.GetProject();
1912-
Assert.That(project.ParatextUsers.Select(u => u.Username), Is.EquivalentTo(new[] { "User 1", "User 2" }));
1918+
Assert.That(project.ParatextUsers.Select(u => u.Username), Is.EquivalentTo(["User 1", "User 2"]));
19131919

19141920
ParatextProjectUser newUser = new ParatextProjectUser
19151921
{
@@ -1923,10 +1929,7 @@ public async Task SyncAsync_SavesNewParatextUser()
19231929

19241930
await env.Runner.RunAsync("project01", "user01", "project01", false, CancellationToken.None);
19251931
project = env.GetProject();
1926-
Assert.That(
1927-
project.ParatextUsers.Select(u => u.Username),
1928-
Is.EquivalentTo(new[] { "User 1", "User 2", "New User 1" })
1929-
);
1932+
Assert.That(project.ParatextUsers.Select(u => u.Username), Is.EquivalentTo(["User 1", "User 2", "New User 1"]));
19301933

19311934
ParatextUserProfile newPtUser = project.ParatextUsers.Single(u => u.Username == "New User 1");
19321935
Assert.That(newPtUser.SFUserId, Is.EqualTo("user01"));
@@ -2019,9 +2022,9 @@ public async Task SyncAsync_UpdatesParatextNoteThreadDoc()
20192022
var book = new Book("MAT", 1, true);
20202023
env.SetupSFData(true, false, false, true, book);
20212024
env.SetupPTData(book);
2022-
string dataId = "dataId01";
2025+
const string dataId = "dataId01";
20232026
NoteThread thread01Before = env.GetNoteThread("project01", "dataId01");
2024-
int startingNoteCount = 2;
2027+
const int startingNoteCount = 2;
20252028
Assert.That(thread01Before.Notes.Count, Is.EqualTo(startingNoteCount), "setup");
20262029
env.SetupNoteChanges(dataId, "thread01");
20272030
// One note is added. One note is marked as Deleted but not actually removed.
@@ -2031,8 +2034,8 @@ public async Task SyncAsync_UpdatesParatextNoteThreadDoc()
20312034
await env.Runner.RunAsync("project01", "user01", "project01", false, CancellationToken.None);
20322035

20332036
NoteThread thread01 = env.GetNoteThread("project01", dataId);
2034-
int expectedNoteTagId = 3;
2035-
string threadExpected = "Context before Scripture text in project context after-Start:0-Length:0-MAT 1:1";
2037+
const int expectedNoteTagId = 3;
2038+
const string threadExpected = "Context before Scripture text in project context after-Start:0-Length:0-MAT 1:1";
20362039
Assert.That(thread01.NoteThreadToString(), Is.EqualTo(threadExpected));
20372040
Assert.That(thread01.Assignment, Is.EqualTo(CommentThread.teamUser));
20382041
env.DeltaUsxMapper.ReceivedWithAnyArgs(1).ToChapterDeltas(default);
@@ -2050,10 +2053,7 @@ public async Task SyncAsync_UpdatesParatextNoteThreadDoc()
20502053

20512054
SFProject project = env.GetProject();
20522055
// User 3 was added as a sync user
2053-
Assert.That(
2054-
project.ParatextUsers.Select(u => u.Username),
2055-
Is.EquivalentTo(new[] { "User 1", "User 2", "User 3" })
2056-
);
2056+
Assert.That(project.ParatextUsers.Select(u => u.Username), Is.EquivalentTo(["User 1", "User 2", "User 3"]));
20572057
Assert.That(project.ParatextUsers.Single(u => u.Username == "User 1").SFUserId, Is.EqualTo("user01"));
20582058
Assert.That(project.ParatextUsers.Single(u => u.Username == "User 2").SFUserId, Is.EqualTo("user02"));
20592059
Assert.That(project.ParatextUsers.Single(u => u.Username == "User 3").SFUserId, Is.EqualTo("user03"));
@@ -2662,7 +2662,7 @@ await env
26622662
;
26632663

26642664
SFProject project = env.GetProject();
2665-
Assert.That(project.ParatextUsers.Count, Is.EqualTo(2));
2665+
Assert.That(project.ParatextUsers.Count, Is.EqualTo(4));
26662666
env.VerifyProjectSync(true);
26672667

26682668
// Check that as PutBookText was run twice, the metrics will be that two books are added
@@ -2740,24 +2740,24 @@ public async Task SyncAsync_BiblicalTermsAreUpdated()
27402740
DataId = "dataId01",
27412741
TermId = "termId01",
27422742
Transliteration = "transliteration01",
2743-
Renderings = new[] { "rendering01", "rendering02" },
2743+
Renderings = ["rendering01", "rendering02"],
27442744
Description = "description01",
27452745
Language = "language01",
2746-
Links = new[] { "link01", "link02" },
2747-
References = new[] { VerseRef.GetBBBCCCVVV(1, 1, 1), VerseRef.GetBBBCCCVVV(2, 2, 2) },
2746+
Links = ["link01", "link02"],
2747+
References = [VerseRef.GetBBBCCCVVV(1, 1, 1), VerseRef.GetBBBCCCVVV(2, 2, 2)],
27482748
Definitions = new Dictionary<string, BiblicalTermDefinition>
27492749
{
27502750
["en"] = new BiblicalTermDefinition
27512751
{
2752-
Categories = new[] { "category01_en", "category02_en" },
2753-
Domains = new[] { "domain01_en", "domain02_en" },
2752+
Categories = ["category01_en", "category02_en"],
2753+
Domains = ["domain01_en", "domain02_en"],
27542754
Gloss = "gloss01_en",
27552755
Notes = "notes01_en",
27562756
},
27572757
["fr"] = new BiblicalTermDefinition
27582758
{
2759-
Categories = new[] { "category01_fr", "category02_fr" },
2760-
Domains = new[] { "domain01_fr", "domain02_fr" },
2759+
Categories = ["category01_fr", "category02_fr"],
2760+
Domains = ["domain01_fr", "domain02_fr"],
27612761
Gloss = "gloss01_fr",
27622762
Notes = "notes01_fr",
27632763
},
@@ -2789,24 +2789,24 @@ public async Task SyncAsync_BiblicalTermsAreUpdated()
27892789
{
27902790
TermId = "termId01",
27912791
Transliteration = "transliteration02",
2792-
Renderings = new[] { "rendering02", "rendering03" },
2792+
Renderings = ["rendering02", "rendering03"],
27932793
Description = "description02",
27942794
Language = "language02",
2795-
Links = new[] { "link02", "link03" },
2796-
References = new[] { VerseRef.GetBBBCCCVVV(2, 2, 2), VerseRef.GetBBBCCCVVV(3, 3, 3) },
2795+
Links = ["link02", "link03"],
2796+
References = [VerseRef.GetBBBCCCVVV(2, 2, 2), VerseRef.GetBBBCCCVVV(3, 3, 3)],
27972797
Definitions = new Dictionary<string, BiblicalTermDefinition>
27982798
{
27992799
["en"] = new BiblicalTermDefinition
28002800
{
2801-
Categories = new[] { "category02_en", "category03_en" },
2802-
Domains = new[] { "domain02_en", "domain03_en" },
2801+
Categories = ["category02_en", "category03_en"],
2802+
Domains = ["domain02_en", "domain03_en"],
28032803
Gloss = "gloss02_en",
28042804
Notes = "notes02_en",
28052805
},
28062806
["de"] = new BiblicalTermDefinition
28072807
{
2808-
Categories = new[] { "category01_de", "category02_de" },
2809-
Domains = new[] { "domain01_de", "domain02_de" },
2808+
Categories = ["category01_de", "category02_de"],
2809+
Domains = ["domain01_de", "domain02_de"],
28102810
Gloss = "gloss01_de",
28112811
Notes = "notes01_de",
28122812
},
@@ -2828,7 +2828,7 @@ public async Task SyncAsync_BiblicalTermsAreUpdated()
28282828
.UpdateBiblicalTerms(Arg.Any<UserSecret>(), Arg.Any<string>(), Arg.Any<IReadOnlyList<BiblicalTerm>>());
28292829

28302830
SFProject project = env.GetProject();
2831-
Assert.That(project.ParatextUsers.Count, Is.EqualTo(2));
2831+
Assert.That(project.ParatextUsers.Count, Is.EqualTo(4));
28322832
env.VerifyProjectSync(true);
28332833

28342834
// dataId02 should be deleted
@@ -2934,7 +2934,10 @@ await env
29342934
Assert.That(noteThread.ExtraHeadingInfo?.Language, Is.EqualTo("language01"));
29352935
Assert.That(noteThread.ExtraHeadingInfo?.Lemma, Is.EqualTo("lemma01"));
29362936
Assert.That(noteThread.ExtraHeadingInfo?.Transliteration, Is.EqualTo("transliteration01"));
2937-
Assert.That(project.ParatextUsers.Select(u => u.Username), Is.EquivalentTo(new[] { "User 1", "User 2" }));
2937+
Assert.That(
2938+
project.ParatextUsers.Select(u => u.Username),
2939+
Is.EquivalentTo(["User 1", "User 2", "User 4", "User 5"])
2940+
);
29382941
SyncMetrics syncMetrics = env.GetSyncMetrics("project01");
29392942
Assert.That(syncMetrics.ParatextNotes, Is.EqualTo(info));
29402943
}
@@ -3525,6 +3528,18 @@ private class TestEnvironment
35253528
Username = "User 3",
35263529
};
35273530

3531+
public static readonly ParatextProjectUser ParatextProjectUnknownUser01 = new ParatextProjectUser
3532+
{
3533+
Role = SFProjectRole.PTObserver,
3534+
Username = "User 4",
3535+
};
3536+
3537+
public static readonly ParatextProjectUser ParatextProjectUnknownUser02 = new ParatextProjectUser
3538+
{
3539+
Role = SFProjectRole.PTObserver,
3540+
Username = "User 5",
3541+
};
3542+
35283543
/// <summary>
35293544
/// Initializes a new instance of the <see cref="TestEnvironment" /> class.
35303545
/// </summary>
@@ -3574,7 +3589,14 @@ public TestEnvironment(bool substituteRealtimeService = false, IDeltaUsxMapper d
35743589
Arg.Is((SFProject project) => project.ParatextId == "target"),
35753590
Arg.Any<CancellationToken>()
35763591
)
3577-
.Returns([ParatextProjectUser01, ParatextProjectUser02]);
3592+
.Returns(
3593+
[
3594+
ParatextProjectUser01,
3595+
ParatextProjectUser02,
3596+
ParatextProjectUnknownUser01,
3597+
ParatextProjectUnknownUser02,
3598+
]
3599+
);
35783600
ParatextService
35793601
.When(x =>
35803602
x.SendReceiveAsync(

0 commit comments

Comments
 (0)