Skip to content

Commit b611bc9

Browse files
authored
SF-3333 Reduce calls to the DBL when configuring sources (#3175)
1 parent 2fd9717 commit b611bc9

File tree

4 files changed

+105
-10
lines changed

4 files changed

+105
-10
lines changed

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ public interface IParatextService
2525
ParatextSettings? GetParatextSettings(UserSecret userSecret, string paratextId);
2626

2727
Task<IReadOnlyList<ParatextResource>> GetResourcesAsync(string userId);
28-
bool IsResource(string paratextId);
28+
bool IsResource(string? paratextId);
2929
Task<string> GetResourcePermissionAsync(string paratextId, string userId, CancellationToken token);
3030
Task<IReadOnlyList<ParatextProjectUser>> GetParatextUsersAsync(
3131
UserSecret userSecret,

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

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -445,7 +445,9 @@ public async Task<IReadOnlyList<ParatextResource>> GetResourcesAsync(string user
445445
/// <summary>
446446
/// Is the PT project referred to by `paratextId` a DBL resource?
447447
/// </summary>
448-
public bool IsResource(string paratextId) =>
448+
/// <param name="paratextId">The Paratext identifier</param>
449+
/// <returns><c>true</c> if the paratext identifier is a resource; otherwise, <c>false</c>.</returns>
450+
public bool IsResource(string? paratextId) =>
449451
paratextId?.Length == SFInstallableDblResource.ResourceIdentifierLength;
450452

451453
/// <summary>

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

Lines changed: 31 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -150,12 +150,20 @@ public async Task<string> CreateProjectAsync(string curUserId, SFProjectCreateSe
150150
// This will make the source project appear after the target, if it needs to be created
151151
if (settings.SourceParatextId != null && settings.SourceParatextId != settings.ParatextId)
152152
{
153+
// Get the resources if the source is a resource
154+
IReadOnlyList<ParatextResource> resources = [];
155+
if (_paratextService.IsResource(settings.SourceParatextId))
156+
{
157+
resources = await _paratextService.GetResourcesAsync(curUserId);
158+
}
159+
153160
TranslateSource source = await GetTranslateSourceAsync(
154161
curUserId,
155162
projectDoc.Id,
156163
settings.SourceParatextId,
157164
syncIfCreated: false,
158-
ptProjects
165+
ptProjects,
166+
resources
159167
);
160168

161169
await projectDoc.SubmitJson0OpAsync(op => UpdateSetting(op, p => p.TranslateConfig.Source, source));
@@ -361,7 +369,8 @@ public async Task UpdateSettingsAsync(string curUserId, string projectId, SFProj
361369
settings.AdditionalTrainingSourceParatextId == ProjectSettingValueUnset;
362370

363371
// Get the list of projects for setting the source or alternate source
364-
IReadOnlyList<ParatextProject> ptProjects = new List<ParatextProject>();
372+
IReadOnlyList<ParatextProject> ptProjects = [];
373+
IReadOnlyList<ParatextResource> resources = [];
365374
if (
366375
(settings.SourceParatextId != null && !unsetSourceProject)
367376
|| (settings.AlternateSourceParatextId != null && !unsetAlternateSourceProject)
@@ -374,6 +383,17 @@ public async Task UpdateSettingsAsync(string curUserId, string projectId, SFProj
374383
throw new DataNotFoundException("The user does not exist.");
375384

376385
ptProjects = await _paratextService.GetProjectsAsync(userSecret);
386+
387+
// Only get the resources if at least one of the paratext ids is a resource id
388+
if (
389+
_paratextService.IsResource(settings.SourceParatextId)
390+
|| _paratextService.IsResource(settings.AlternateSourceParatextId)
391+
|| _paratextService.IsResource(settings.AlternateTrainingSourceParatextId)
392+
|| _paratextService.IsResource(settings.AdditionalTrainingSourceParatextId)
393+
)
394+
{
395+
resources = await _paratextService.GetResourcesAsync(curUserId);
396+
}
377397
}
378398

379399
// Get the source - any creation or permission updates are handled in GetTranslateSourceAsync
@@ -386,6 +406,7 @@ public async Task UpdateSettingsAsync(string curUserId, string projectId, SFProj
386406
settings.SourceParatextId,
387407
syncIfCreated: false,
388408
ptProjects,
409+
resources,
389410
projectDoc.Data.UserRoles
390411
);
391412
if (source.ProjectRef == projectId)
@@ -405,6 +426,7 @@ public async Task UpdateSettingsAsync(string curUserId, string projectId, SFProj
405426
settings.AlternateSourceParatextId,
406427
syncIfCreated: true,
407428
ptProjects,
429+
resources,
408430
projectDoc.Data.UserRoles
409431
);
410432
if (alternateSource.ProjectRef == projectId)
@@ -424,6 +446,7 @@ public async Task UpdateSettingsAsync(string curUserId, string projectId, SFProj
424446
settings.AlternateTrainingSourceParatextId,
425447
syncIfCreated: true,
426448
ptProjects,
449+
resources,
427450
projectDoc.Data.UserRoles
428451
);
429452
if (alternateTrainingSource.ProjectRef == projectId)
@@ -443,6 +466,7 @@ public async Task UpdateSettingsAsync(string curUserId, string projectId, SFProj
443466
settings.AdditionalTrainingSourceParatextId,
444467
syncIfCreated: true,
445468
ptProjects,
469+
resources,
446470
projectDoc.Data.UserRoles
447471
);
448472
if (additionalTrainingSource.ProjectRef == projectId)
@@ -1856,13 +1880,14 @@ private async Task<string> CreateResourceProjectInternalAsync(string curUserId,
18561880
}
18571881

18581882
/// <summary>
1859-
/// Gets the translate source asynchronously.
1883+
/// Gets the translation source asynchronously.
18601884
/// </summary>
18611885
/// <param name="curUserId">The current user identifier.</param>
18621886
/// <param name="sfProjectId">The Scripture Forge project identifier.</param>
18631887
/// <param name="paratextId">The paratext identifier.</param>
18641888
/// <param name="syncIfCreated">If <c>true</c> sync the project if it is created.</param>
18651889
/// <param name="ptProjects">The paratext projects.</param>
1890+
/// <param name="resources">The paratext resources.</param>
18661891
/// <param name="userRoles">The ids and roles of the users who will need to access the source.</param>
18671892
/// <returns>The <see cref="TranslateSource"/> object for the specified resource.</returns>
18681893
/// <exception cref="DataNotFoundException">The source paratext project does not exist.</exception>
@@ -1872,6 +1897,7 @@ private async Task<TranslateSource> GetTranslateSourceAsync(
18721897
string paratextId,
18731898
bool syncIfCreated,
18741899
IEnumerable<ParatextProject> ptProjects,
1900+
IEnumerable<ParatextResource> resources,
18751901
IReadOnlyDictionary<string, string>? userRoles = null
18761902
)
18771903
{
@@ -1880,7 +1906,6 @@ private async Task<TranslateSource> GetTranslateSourceAsync(
18801906
if (sourcePTProject == null)
18811907
{
18821908
// If it is not a project, see if there is a matching resource
1883-
IReadOnlyList<ParatextResource> resources = await _paratextService.GetResourcesAsync(curUserId);
18841909
sourcePTProject = resources.SingleOrDefault(r => r.ParatextId == paratextId);
18851910
if (sourcePTProject == null)
18861911
{
@@ -1889,7 +1914,7 @@ private async Task<TranslateSource> GetTranslateSourceAsync(
18891914
}
18901915

18911916
// Get the users who will access this source resource or project
1892-
IEnumerable<string> userIds = userRoles != null ? userRoles.Keys : new string[] { curUserId };
1917+
IEnumerable<string> userIds = userRoles != null ? userRoles.Keys : [curUserId];
18931918

18941919
// Get the project reference
18951920
SFProject sourceProject = RealtimeService
@@ -1903,7 +1928,7 @@ private async Task<TranslateSource> GetTranslateSourceAsync(
19031928
}
19041929
else
19051930
{
1906-
sourceProjectRef = await CreateResourceProjectAsync(curUserId, paratextId, addUser: false);
1931+
sourceProjectRef = await CreateResourceProjectInternalAsync(curUserId, sourcePTProject);
19071932
projectCreated = true;
19081933
}
19091934

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

Lines changed: 70 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2276,6 +2276,74 @@ public void IsSourceProject_TrueWhenProjectIsATranslationSource()
22762276
Assert.That(env.Service.IsSourceProject("Bad project"), Is.False);
22772277
}
22782278

2279+
[Test]
2280+
public async Task UpdateSettingsAsync_ChangeAllDraftSources_CreatesResourceProject()
2281+
{
2282+
var env = new TestEnvironment();
2283+
const string newResourceParatextId = "resource_project";
2284+
env.ParatextService.GetResourcePermissionAsync(newResourceParatextId, Arg.Any<string>(), CancellationToken.None)
2285+
.Returns(Task.FromResult(TextInfoPermission.Read));
2286+
2287+
// Ensure that the new project does not exist
2288+
Assert.That(
2289+
env.RealtimeService.GetRepository<SFProject>().Query().Any(p => p.ParatextId == newResourceParatextId),
2290+
Is.False
2291+
);
2292+
2293+
// SUT
2294+
await env.Service.UpdateSettingsAsync(
2295+
User01,
2296+
Project01,
2297+
new SFProjectSettings
2298+
{
2299+
AdditionalTrainingSourceParatextId = newResourceParatextId,
2300+
AlternateSourceParatextId = newResourceParatextId,
2301+
AlternateTrainingSourceParatextId = newResourceParatextId,
2302+
}
2303+
);
2304+
2305+
SFProject project = env.GetProject(Project01);
2306+
Assert.That(project.TranslateConfig.DraftConfig.AdditionalTrainingSource?.ProjectRef, Is.Not.Null);
2307+
Assert.That(
2308+
project.TranslateConfig.DraftConfig.AdditionalTrainingSource?.ParatextId,
2309+
Is.EqualTo(newResourceParatextId)
2310+
);
2311+
Assert.That(project.TranslateConfig.DraftConfig.AdditionalTrainingSource?.Name, Is.EqualTo("ResourceProject"));
2312+
Assert.That(project.TranslateConfig.DraftConfig.AlternateSource?.ProjectRef, Is.Not.Null);
2313+
Assert.That(project.TranslateConfig.DraftConfig.AlternateSource?.ParatextId, Is.EqualTo(newResourceParatextId));
2314+
Assert.That(project.TranslateConfig.DraftConfig.AlternateSource?.Name, Is.EqualTo("ResourceProject"));
2315+
Assert.That(project.TranslateConfig.DraftConfig.AlternateTrainingSource?.ProjectRef, Is.Not.Null);
2316+
Assert.That(
2317+
project.TranslateConfig.DraftConfig.AlternateTrainingSource?.ParatextId,
2318+
Is.EqualTo(newResourceParatextId)
2319+
);
2320+
Assert.That(project.TranslateConfig.DraftConfig.AlternateTrainingSource?.Name, Is.EqualTo("ResourceProject"));
2321+
2322+
SFProject alternateSourceProject = env.GetProject(
2323+
project.TranslateConfig.DraftConfig.AlternateSource!.ProjectRef
2324+
);
2325+
Assert.That(alternateSourceProject.ParatextId, Is.EqualTo(newResourceParatextId));
2326+
Assert.That(alternateSourceProject.Name, Is.EqualTo("ResourceProject"));
2327+
2328+
await env
2329+
.MachineProjectService.DidNotReceive()
2330+
.RemoveProjectAsync(Arg.Any<string>(), Arg.Any<bool>(), Arg.Any<CancellationToken>());
2331+
await env
2332+
.MachineProjectService.DidNotReceive()
2333+
.AddSmtProjectAsync(Arg.Any<string>(), Arg.Any<CancellationToken>());
2334+
await env.SyncService.Received().SyncAsync(Arg.Any<SyncConfig>());
2335+
env.BackgroundJobClient.Received(1).Create(Arg.Any<Job>(), Arg.Any<IState>());
2336+
2337+
// Check that the project was created
2338+
Assert.That(
2339+
env.RealtimeService.GetRepository<SFProject>().Query().Any(p => p.ParatextId == newResourceParatextId),
2340+
Is.True
2341+
);
2342+
2343+
// Ensure we only queried the list of resources once
2344+
await env.ParatextService.Received(1).GetResourcesAsync(User01);
2345+
}
2346+
22792347
[Test]
22802348
public async Task UpdateSettingsAsync_ChangeAlternateSource_CannotUseTargetProject()
22812349
{
@@ -5313,9 +5381,9 @@ public TestEnvironment()
53135381
.Returns(true);
53145382

53155383
ParatextService
5316-
.IsResource(Arg.Any<string>())
5384+
.IsResource(Arg.Any<string?>())
53175385
.Returns(callInfo =>
5318-
callInfo.ArgAt<string>(0).Length == SFInstallableDblResource.ResourceIdentifierLength
5386+
callInfo.ArgAt<string?>(0)?.Length == SFInstallableDblResource.ResourceIdentifierLength
53195387
);
53205388

53215389
Service = new SFProjectService(

0 commit comments

Comments
 (0)