Skip to content

Commit b2ef296

Browse files
committed
Remove IAsyncEnumerable to allow correct HTTP status codes
1 parent f24422f commit b2ef296

File tree

5 files changed

+152
-256
lines changed

5 files changed

+152
-256
lines changed

src/SIL.XForge.Scripture/Controllers/MachineApiController.cs

+4-4
Original file line numberDiff line numberDiff line change
@@ -185,7 +185,7 @@ CancellationToken cancellationToken
185185
/// <response code="404">The project does not exist or is not configured on the ML server.</response>
186186
/// <response code="503">The ML server is temporarily unavailable or unresponsive.</response>
187187
[HttpGet(MachineApi.GetBuilds)]
188-
public ActionResult<IAsyncEnumerable<ServalBuildDto>> GetBuildsAsync(
188+
public async Task<ActionResult<IReadOnlyList<ServalBuildDto>>> GetBuildsAsync(
189189
string sfProjectId,
190190
[FromQuery] bool preTranslate,
191191
CancellationToken cancellationToken
@@ -195,7 +195,7 @@ CancellationToken cancellationToken
195195
{
196196
bool isServalAdmin = _userAccessor.SystemRoles.Contains(SystemRole.ServalAdmin);
197197
return Ok(
198-
_machineApiService.GetBuildsAsync(
198+
await _machineApiService.GetBuildsAsync(
199199
_userAccessor.UserId,
200200
sfProjectId,
201201
preTranslate,
@@ -438,7 +438,7 @@ CancellationToken cancellationToken
438438
/// <response code="403">The user does not have permission to access the draft.</response>
439439
/// <response code="404">The draft does not exist.</response>
440440
[HttpGet(MachineApi.GetPreTranslationHistory)]
441-
public ActionResult<IAsyncEnumerable<DocumentRevision>> GetPreTranslationRevisionsAsync(
441+
public async Task<ActionResult<IReadOnlyList<DocumentRevision>>> GetPreTranslationRevisionsAsync(
442442
string sfProjectId,
443443
int bookNum,
444444
int chapterNum,
@@ -449,7 +449,7 @@ CancellationToken cancellationToken
449449
{
450450
bool isServalAdmin = _userAccessor.SystemRoles.Contains(SystemRole.ServalAdmin);
451451
return Ok(
452-
_machineApiService.GetPreTranslationRevisionsAsync(
452+
await _machineApiService.GetPreTranslationRevisionsAsync(
453453
_userAccessor.UserId,
454454
sfProjectId,
455455
bookNum,

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

+2-2
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ CancellationToken cancellationToken
3030
bool isServalAdmin,
3131
CancellationToken cancellationToken
3232
);
33-
public IAsyncEnumerable<ServalBuildDto> GetBuildsAsync(
33+
Task<IReadOnlyList<ServalBuildDto>> GetBuildsAsync(
3434
string curUserId,
3535
string sfProjectId,
3636
bool preTranslate,
@@ -75,7 +75,7 @@ Task<Snapshot<TextData>> GetPreTranslationDeltaAsync(
7575
DateTime timestamp,
7676
CancellationToken cancellationToken
7777
);
78-
IAsyncEnumerable<DocumentRevision> GetPreTranslationRevisionsAsync(
78+
Task<IReadOnlyList<DocumentRevision>> GetPreTranslationRevisionsAsync(
7979
string curUserId,
8080
string sfProjectId,
8181
int bookNum,

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

+31-18
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
using System;
22
using System.Collections.Generic;
33
using System.Linq;
4-
using System.Runtime.CompilerServices;
54
using System.Security.Cryptography;
65
using System.Text;
76
using System.Threading;
@@ -270,14 +269,17 @@ CancellationToken cancellationToken
270269
return buildDto;
271270
}
272271

273-
public async IAsyncEnumerable<ServalBuildDto> GetBuildsAsync(
272+
public async Task<IReadOnlyList<ServalBuildDto>> GetBuildsAsync(
274273
string curUserId,
275274
string sfProjectId,
276275
bool preTranslate,
277276
bool isServalAdmin,
278-
[EnumeratorCancellation] CancellationToken cancellationToken
277+
CancellationToken cancellationToken
279278
)
280279
{
280+
// Set up the list of builds to be returned
281+
List<ServalBuildDto> builds = [];
282+
281283
// Ensure that the user has permission
282284
await EnsureProjectPermissionAsync(curUserId, sfProjectId, isServalAdmin);
283285

@@ -358,7 +360,7 @@ [EnumeratorCancellation] CancellationToken cancellationToken
358360
}
359361

360362
// Make sure the DTO conforms to the machine-api URLs
361-
yield return UpdateDto(buildDto, sfProjectId);
363+
builds.Add(UpdateDto(buildDto, sfProjectId));
362364
}
363365

364366
// See if any builds are queued at our end
@@ -380,8 +382,10 @@ [EnumeratorCancellation] CancellationToken cancellationToken
380382
queuedState = UpdateDto(queuedState, eventMetric);
381383
}
382384

383-
yield return queuedState;
385+
builds.Add(queuedState);
384386
}
387+
388+
return builds;
385389
}
386390

387391
public async Task<ServalBuildDto?> GetLastCompletedPreTranslationBuildAsync(
@@ -697,15 +701,18 @@ CancellationToken cancellationToken
697701
/// <exception cref="ForbiddenException">
698702
/// The user does not have permission to access the Serval/Machine API.
699703
/// </exception>
700-
public async IAsyncEnumerable<DocumentRevision> GetPreTranslationRevisionsAsync(
704+
public async Task<IReadOnlyList<DocumentRevision>> GetPreTranslationRevisionsAsync(
701705
string curUserId,
702706
string sfProjectId,
703707
int bookNum,
704708
int chapterNum,
705709
bool isServalAdmin,
706-
[EnumeratorCancellation] CancellationToken cancellationToken
710+
CancellationToken cancellationToken
707711
)
708712
{
713+
// Set up the list of revisions to be returned
714+
List<DocumentRevision> revisions = [];
715+
709716
// Ensure that the user has permission
710717
await EnsureProjectPermissionAsync(curUserId, sfProjectId, isServalAdmin);
711718

@@ -724,11 +731,13 @@ [EnumeratorCancellation] CancellationToken cancellationToken
724731
);
725732
if (build is not null)
726733
{
727-
yield return new DocumentRevision
728-
{
729-
Source = OpSource.Draft,
730-
Timestamp = build.AdditionalInfo?.DateFinished?.UtcDateTime ?? DateTime.UtcNow,
731-
};
734+
revisions.Add(
735+
new DocumentRevision
736+
{
737+
Source = OpSource.Draft,
738+
Timestamp = build.AdditionalInfo?.DateFinished?.UtcDateTime ?? DateTime.UtcNow,
739+
}
740+
);
732741
}
733742
}
734743
else
@@ -743,14 +752,18 @@ [EnumeratorCancellation] CancellationToken cancellationToken
743752
break;
744753
}
745754

746-
yield return new DocumentRevision
747-
{
748-
Source = op.Metadata.Source ?? OpSource.Draft,
749-
Timestamp = op.Metadata.Timestamp,
750-
UserId = op.Metadata.UserId,
751-
};
755+
revisions.Add(
756+
new DocumentRevision
757+
{
758+
Source = op.Metadata.Source ?? OpSource.Draft,
759+
Timestamp = op.Metadata.Timestamp,
760+
UserId = op.Metadata.UserId,
761+
}
762+
);
752763
}
753764
}
765+
766+
return revisions;
754767
}
755768

756769
public async Task<string> GetPreTranslationUsfmAsync(

test/SIL.XForge.Scripture.Tests/Controllers/MachineApiControllerTests.cs

+18-32
Original file line numberDiff line numberDiff line change
@@ -518,7 +518,7 @@ await env
518518
}
519519

520520
[Test]
521-
public void GetBuildsAsync_MachineApiDown()
521+
public async Task GetBuildsAsync_MachineApiDown()
522522
{
523523
// Set up test environment
524524
var env = new TestEnvironment();
@@ -532,7 +532,7 @@ public void GetBuildsAsync_MachineApiDown()
532532
.Throws(new BrokenCircuitException());
533533

534534
// SUT
535-
ActionResult<IAsyncEnumerable<ServalBuildDto>> actual = env.Controller.GetBuildsAsync(
535+
ActionResult<IReadOnlyList<ServalBuildDto>> actual = await env.Controller.GetBuildsAsync(
536536
Project01,
537537
preTranslate: true,
538538
CancellationToken.None
@@ -544,7 +544,7 @@ public void GetBuildsAsync_MachineApiDown()
544544
}
545545

546546
[Test]
547-
public void GetBuildsAsync_NoPermission()
547+
public async Task GetBuildsAsync_NoPermission()
548548
{
549549
// Set up test environment
550550
var env = new TestEnvironment();
@@ -558,7 +558,7 @@ public void GetBuildsAsync_NoPermission()
558558
.Throws(new ForbiddenException());
559559

560560
// SUT
561-
ActionResult<IAsyncEnumerable<ServalBuildDto>> actual = env.Controller.GetBuildsAsync(
561+
ActionResult<IReadOnlyList<ServalBuildDto>> actual = await env.Controller.GetBuildsAsync(
562562
Project01,
563563
preTranslate: true,
564564
CancellationToken.None
@@ -568,7 +568,7 @@ public void GetBuildsAsync_NoPermission()
568568
}
569569

570570
[Test]
571-
public void GetBuildsAsync_NoProject()
571+
public async Task GetBuildsAsync_NoProject()
572572
{
573573
// Set up test environment
574574
var env = new TestEnvironment();
@@ -582,7 +582,7 @@ public void GetBuildsAsync_NoProject()
582582
.Throws(new DataNotFoundException(string.Empty));
583583

584584
// SUT
585-
ActionResult<IAsyncEnumerable<ServalBuildDto>> actual = env.Controller.GetBuildsAsync(
585+
ActionResult<IReadOnlyList<ServalBuildDto>> actual = await env.Controller.GetBuildsAsync(
586586
Project01,
587587
preTranslate: true,
588588
CancellationToken.None
@@ -603,19 +603,18 @@ public async Task GetBuildsAsync_Success()
603603
isServalAdmin: false,
604604
CancellationToken.None
605605
)
606-
.Returns(env.ServalBuilds());
606+
.Returns(Task.FromResult<IReadOnlyList<ServalBuildDto>>([env.TestBuild]));
607607

608608
// SUT
609-
ActionResult<IAsyncEnumerable<ServalBuildDto>> actual = env.Controller.GetBuildsAsync(
609+
ActionResult<IReadOnlyList<ServalBuildDto>> actual = await env.Controller.GetBuildsAsync(
610610
Project01,
611611
preTranslate: true,
612612
CancellationToken.None
613613
);
614614

615615
Assert.IsInstanceOf<OkObjectResult>(actual.Result);
616616
bool buildsExist = false;
617-
var builds = (IAsyncEnumerable<ServalBuildDto>)((OkObjectResult)actual.Result!).Value!;
618-
await foreach (ServalBuildDto build in builds)
617+
foreach (ServalBuildDto build in (IReadOnlyList<ServalBuildDto>)((OkObjectResult)actual.Result!).Value!)
619618
{
620619
buildsExist = true;
621620
Assert.AreEqual(env.TestBuild, build);
@@ -1064,15 +1063,15 @@ public async Task GetPreTranslationDeltaAsync_Success()
10641063
}
10651064

10661065
[Test]
1067-
public void GetPreTranslationRevisionsAsync_MachineApiDown()
1066+
public async Task GetPreTranslationRevisionsAsync_MachineApiDown()
10681067
{
10691068
// Set up test environment
10701069
var env = new TestEnvironment();
10711070
env.MachineApiService.GetPreTranslationRevisionsAsync(User01, Project01, 40, 1, false, CancellationToken.None)
10721071
.Throws(new BrokenCircuitException());
10731072

10741073
// SUT
1075-
ActionResult<IAsyncEnumerable<DocumentRevision>> actual = env.Controller.GetPreTranslationRevisionsAsync(
1074+
ActionResult<IReadOnlyList<DocumentRevision>> actual = await env.Controller.GetPreTranslationRevisionsAsync(
10761075
Project01,
10771076
40,
10781077
1,
@@ -1085,15 +1084,15 @@ public void GetPreTranslationRevisionsAsync_MachineApiDown()
10851084
}
10861085

10871086
[Test]
1088-
public void GetPreTranslationRevisionsAsync_NoPermission()
1087+
public async Task GetPreTranslationRevisionsAsync_NoPermission()
10891088
{
10901089
// Set up test environment
10911090
var env = new TestEnvironment();
10921091
env.MachineApiService.GetPreTranslationRevisionsAsync(User01, Project01, 40, 1, false, CancellationToken.None)
10931092
.Throws(new ForbiddenException());
10941093

10951094
// SUT
1096-
ActionResult<IAsyncEnumerable<DocumentRevision>> actual = env.Controller.GetPreTranslationRevisionsAsync(
1095+
ActionResult<IReadOnlyList<DocumentRevision>> actual = await env.Controller.GetPreTranslationRevisionsAsync(
10971096
Project01,
10981097
40,
10991098
1,
@@ -1104,15 +1103,15 @@ public void GetPreTranslationRevisionsAsync_NoPermission()
11041103
}
11051104

11061105
[Test]
1107-
public void GetPreTranslationRevisionsAsync_NoProject()
1106+
public async Task GetPreTranslationRevisionsAsync_NoProject()
11081107
{
11091108
// Set up test environment
11101109
var env = new TestEnvironment();
11111110
env.MachineApiService.GetPreTranslationRevisionsAsync(User01, Project01, 40, 1, false, CancellationToken.None)
11121111
.Throws(new DataNotFoundException(string.Empty));
11131112

11141113
// SUT
1115-
ActionResult<IAsyncEnumerable<DocumentRevision>> actual = env.Controller.GetPreTranslationRevisionsAsync(
1114+
ActionResult<IReadOnlyList<DocumentRevision>> actual = await env.Controller.GetPreTranslationRevisionsAsync(
11161115
Project01,
11171116
40,
11181117
1,
@@ -1128,10 +1127,10 @@ public async Task GetPreTranslationRevisionsAsync_Success()
11281127
// Set up test environment
11291128
var env = new TestEnvironment();
11301129
env.MachineApiService.GetPreTranslationRevisionsAsync(User01, Project01, 40, 1, false, CancellationToken.None)
1131-
.Returns(env.RevisionHistory());
1130+
.Returns(Task.FromResult<IReadOnlyList<DocumentRevision>>([env.TestRevision]));
11321131

11331132
// SUT
1134-
ActionResult<IAsyncEnumerable<DocumentRevision>> actual = env.Controller.GetPreTranslationRevisionsAsync(
1133+
ActionResult<IReadOnlyList<DocumentRevision>> actual = await env.Controller.GetPreTranslationRevisionsAsync(
11351134
Project01,
11361135
40,
11371136
1,
@@ -1140,8 +1139,7 @@ public async Task GetPreTranslationRevisionsAsync_Success()
11401139

11411140
Assert.IsInstanceOf<OkObjectResult>(actual.Result);
11421141
bool revisionsExist = false;
1143-
var revisions = (IAsyncEnumerable<DocumentRevision>)((OkObjectResult)actual.Result!).Value!;
1144-
await foreach (DocumentRevision revision in revisions)
1142+
foreach (DocumentRevision revision in (IReadOnlyList<DocumentRevision>)((OkObjectResult)actual.Result!).Value!)
11451143
{
11461144
revisionsExist = true;
11471145
Assert.AreEqual(env.TestRevision, revision);
@@ -2252,17 +2250,5 @@ public TestEnvironment()
22522250
public IExceptionHandler ExceptionHandler { get; }
22532251
public IMachineApiService MachineApiService { get; }
22542252
public IUserAccessor UserAccessor { get; }
2255-
2256-
public async IAsyncEnumerable<DocumentRevision> RevisionHistory()
2257-
{
2258-
yield return TestRevision;
2259-
await Task.CompletedTask;
2260-
}
2261-
2262-
public async IAsyncEnumerable<ServalBuildDto> ServalBuilds()
2263-
{
2264-
yield return TestBuild;
2265-
await Task.CompletedTask;
2266-
}
22672253
}
22682254
}

0 commit comments

Comments
 (0)