Skip to content

Commit

Permalink
Add assessment API
Browse files Browse the repository at this point in the history
  • Loading branch information
ddaspit committed Aug 19, 2024
1 parent d9e928d commit 0a9a67b
Show file tree
Hide file tree
Showing 85 changed files with 4,546 additions and 214 deletions.
9 changes: 8 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,14 @@ jobs:
- name: Pre-Test
run: sudo mkdir -p /var/lib/serval && sudo chmod 777 /var/lib/serval
- name: Test
run: dotnet test --verbosity normal --filter "TestCategory!=E2E&TestCategory!=E2EMissingServices" --collect:"Xplat Code Coverage"
run: dotnet test --verbosity normal --filter "TestCategory!=E2E&TestCategory!=E2EMissingServices" --collect:"Xplat Code Coverage" --logger "trx;LogFileName=test-results.trx"
- name: Test report
uses: dorny/test-reporter@v1
if: success() || failure()
with:
name: NUnit Tests
path: src/**/TestResults/test-results.trx
reporter: dotnet-trx
- name: Upload coverage reports to Codecov
uses: codecov/codecov-action@v3
env:
Expand Down
7 changes: 7 additions & 0 deletions Serval.sln
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Serval.Machine.JobServer",
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Serval.Machine.Shared.Tests", "src\Machine\test\Serval.Machine.Shared.Tests\Serval.Machine.Shared.Tests.csproj", "{B0D23A55-AB09-4C2C-B309-F4BEB3BC968D}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Serval.Assessment", "src\Serval\src\Serval.Assessment\Serval.Assessment.csproj", "{10657805-48F1-4205-B8F5-79447F6EF620}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "ServiceToolkit", "ServiceToolkit", "{EA69B41C-49EF-4017-A687-44B9DF37FF98}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{C3A14577-A654-4604-818C-4E683DD45A51}"
Expand Down Expand Up @@ -170,6 +172,10 @@ Global
{B0D23A55-AB09-4C2C-B309-F4BEB3BC968D}.Debug|Any CPU.Build.0 = Debug|Any CPU
{B0D23A55-AB09-4C2C-B309-F4BEB3BC968D}.Release|Any CPU.ActiveCfg = Release|Any CPU
{B0D23A55-AB09-4C2C-B309-F4BEB3BC968D}.Release|Any CPU.Build.0 = Release|Any CPU
{10657805-48F1-4205-B8F5-79447F6EF620}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{10657805-48F1-4205-B8F5-79447F6EF620}.Debug|Any CPU.Build.0 = Debug|Any CPU
{10657805-48F1-4205-B8F5-79447F6EF620}.Release|Any CPU.ActiveCfg = Release|Any CPU
{10657805-48F1-4205-B8F5-79447F6EF620}.Release|Any CPU.Build.0 = Release|Any CPU
{0E40F959-C641-40A2-9750-B17A4F9F9E55}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{0E40F959-C641-40A2-9750-B17A4F9F9E55}.Debug|Any CPU.Build.0 = Debug|Any CPU
{0E40F959-C641-40A2-9750-B17A4F9F9E55}.Release|Any CPU.ActiveCfg = Release|Any CPU
Expand Down Expand Up @@ -206,6 +212,7 @@ Global
{C02494FB-663E-4430-9F2D-41F1A740B271} = {D808D2BE-ED26-4E60-A409-AE58F7C1CB8F}
{BC766753-E560-4ADF-9923-C7A96076EA47} = {D808D2BE-ED26-4E60-A409-AE58F7C1CB8F}
{B0D23A55-AB09-4C2C-B309-F4BEB3BC968D} = {40C225C2-1EEF-4D1D-9D14-1CBB86C8A1CB}
{10657805-48F1-4205-B8F5-79447F6EF620} = {25CDB05B-4E24-4A6E-933E-1E0BEC97D74D}
{C3A14577-A654-4604-818C-4E683DD45A51} = {EA69B41C-49EF-4017-A687-44B9DF37FF98}
{0E40F959-C641-40A2-9750-B17A4F9F9E55} = {C3A14577-A654-4604-818C-4E683DD45A51}
EndGlobalSection
Expand Down
2 changes: 1 addition & 1 deletion src/DataAccess/src/SIL.DataAccess/ObjectRefConvention.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ public class ObjectRefConvention : ConventionBase, IMemberMapConvention
{
public void Apply(BsonMemberMap memberMap)
{
if (memberMap.MemberName.EndsWith("Ref"))
if (memberMap.MemberName.EndsWith("Ref") && memberMap.MemberName.Length > 3)
memberMap.SetSerializer(new StringSerializer(BsonType.ObjectId));
}
}
15 changes: 15 additions & 0 deletions src/Echo/src/EchoTranslationEngine/HealthServiceV1.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
using Serval.Health.V1;

namespace EchoTranslationEngine;

public class HealthServiceV1(HealthCheckService healthCheckService) : HealthApi.HealthApiBase
{
private readonly HealthCheckService _healthCheckService = healthCheckService;

public override async Task<HealthCheckResponse> HealthCheck(Empty request, ServerCallContext context)
{
HealthReport healthReport = await _healthCheckService.CheckHealthAsync();
HealthCheckResponse healthCheckResponse = WriteGrpcHealthCheckResponse.Generate(healthReport);
return healthCheckResponse;
}
}
1 change: 1 addition & 0 deletions src/Echo/src/EchoTranslationEngine/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,5 +18,6 @@
app.UseHttpsRedirection();

app.MapGrpcService<TranslationEngineServiceV1>();
app.MapGrpcService<HealthServiceV1>();

app.Run();
22 changes: 6 additions & 16 deletions src/Echo/src/EchoTranslationEngine/TranslationEngineServiceV1.cs
Original file line number Diff line number Diff line change
@@ -1,13 +1,10 @@
namespace EchoTranslationEngine;

public class TranslationEngineServiceV1(BackgroundTaskQueue taskQueue, HealthCheckService healthCheckService)
: TranslationEngineApi.TranslationEngineApiBase
public class TranslationEngineServiceV1(BackgroundTaskQueue taskQueue) : TranslationEngineApi.TranslationEngineApiBase
{
private static readonly Empty Empty = new();
private readonly BackgroundTaskQueue _taskQueue = taskQueue;

private readonly HealthCheckService _healthCheckService = healthCheckService;

public override Task<CreateResponse> Create(CreateRequest request, ServerCallContext context)
{
if (request.SourceLanguage != request.TargetLanguage)
Expand Down Expand Up @@ -79,7 +76,7 @@ await client.BuildStartedAsync(
try
{
using (
AsyncClientStreamingCall<InsertPretranslationRequest, Empty> call =
AsyncClientStreamingCall<InsertPretranslationsRequest, Empty> call =
client.InsertPretranslations(cancellationToken: cancellationToken)
)
{
Expand Down Expand Up @@ -124,7 +121,7 @@ await client.BuildStartedAsync(
if (sourceLine.Length > 0 && targetLine.Length == 0)
{
await call.RequestStream.WriteAsync(
new InsertPretranslationRequest
new InsertPretranslationsRequest
{
EngineId = request.EngineId,
CorpusId = corpus.Id,
Expand Down Expand Up @@ -157,7 +154,7 @@ await call.RequestStream.WriteAsync(
if (sourceLine.Length > 0 && targetLine.Length == 0)
{
await call.RequestStream.WriteAsync(
new InsertPretranslationRequest
new InsertPretranslationsRequest
{
EngineId = request.EngineId,
CorpusId = corpus.Id,
Expand All @@ -182,7 +179,7 @@ await call.RequestStream.WriteAsync(
if (sourceLine.Length > 0)
{
await call.RequestStream.WriteAsync(
new InsertPretranslationRequest
new InsertPretranslationsRequest
{
EngineId = request.EngineId,
CorpusId = corpus.Id,
Expand All @@ -203,7 +200,7 @@ await call.RequestStream.WriteAsync(
if (sourceLine.Length > 0)
{
await call.RequestStream.WriteAsync(
new InsertPretranslationRequest
new InsertPretranslationsRequest
{
EngineId = request.EngineId,
CorpusId = corpus.Id,
Expand Down Expand Up @@ -319,11 +316,4 @@ ServerCallContext context
new GetLanguageInfoResponse { InternalCode = request.Language + "_echo", IsNative = true, }
);
}

public override async Task<HealthCheckResponse> HealthCheck(Empty request, ServerCallContext context)
{
HealthReport healthReport = await _healthCheckService.CheckHealthAsync();
HealthCheckResponse healthCheckResponse = WriteGrpcHealthCheckResponse.Generate(healthReport);
return healthCheckResponse;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ public static class IEndpointRouteBuilderExtensions
public static IEndpointRouteBuilder MapServalTranslationEngineService(this IEndpointRouteBuilder builder)
{
builder.MapGrpcService<ServalTranslationEngineServiceV1>();
builder.MapGrpcService<ServalHealthServiceV1>();

return builder;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
using Google.Protobuf.WellKnownTypes;
using Serval.Health.V1;

namespace Serval.Machine.Shared.Services;

public class ServalHealthServiceV1(HealthCheckService healthCheckService) : HealthApi.HealthApiBase
{
private readonly HealthCheckService _healthCheckService = healthCheckService;

public override async Task<HealthCheckResponse> HealthCheck(Empty request, ServerCallContext context)
{
HealthReport healthReport = await _healthCheckService.CheckHealthAsync();
HealthCheckResponse healthCheckResponse = WriteGrpcHealthCheckResponse.Generate(healthReport);
return healthCheckResponse;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ await _client.BuildRestartingAsync(
await foreach (Pretranslation pretranslation in pretranslations)
{
await call.RequestStream.WriteAsync(
new InsertPretranslationRequest
new InsertPretranslationsRequest
{
EngineId = content!,
CorpusId = pretranslation.CorpusId,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,14 @@

namespace Serval.Machine.Shared.Services;

public class ServalTranslationEngineServiceV1(
IEnumerable<ITranslationEngineService> engineServices,
HealthCheckService healthCheckService
) : TranslationEngineApi.TranslationEngineApiBase
public class ServalTranslationEngineServiceV1(IEnumerable<ITranslationEngineService> engineServices)
: TranslationEngineApi.TranslationEngineApiBase
{
private static readonly Empty Empty = new();

private readonly Dictionary<TranslationEngineType, ITranslationEngineService> _engineServices =
engineServices.ToDictionary(es => es.Type);

private readonly HealthCheckService _healthCheckService = healthCheckService;

public override async Task<CreateResponse> Create(CreateRequest request, ServerCallContext context)
{
ITranslationEngineService engineService = GetEngineService(request.EngineType);
Expand Down Expand Up @@ -172,13 +168,6 @@ ServerCallContext context
return Task.FromResult(new GetLanguageInfoResponse { InternalCode = internalCode, IsNative = isNative, });
}

public override async Task<HealthCheckResponse> HealthCheck(Empty request, ServerCallContext context)
{
HealthReport healthReport = await _healthCheckService.CheckHealthAsync();
HealthCheckResponse healthCheckResponse = WriteGrpcHealthCheckResponse.Generate(healthReport);
return healthCheckResponse;
}

private ITranslationEngineService GetEngineService(string engineTypeStr)
{
if (_engineServices.TryGetValue(GetEngineType(engineTypeStr), out ITranslationEngineService? service))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ await env.Handler.HandleMessageAsync(
_ = env.Client.Received(1).InsertPretranslations();
_ = env.PretranslationWriter.Received(1)
.WriteAsync(
new InsertPretranslationRequest
new InsertPretranslationsRequest
{
EngineId = "engine1",
CorpusId = "corpus1",
Expand All @@ -78,7 +78,7 @@ public TestEnvironment()
Client
.IncrementTranslationEngineCorpusSizeAsync(Arg.Any<IncrementTranslationEngineCorpusSizeRequest>())
.Returns(CreateEmptyUnaryCall());
PretranslationWriter = Substitute.For<IClientStreamWriter<InsertPretranslationRequest>>();
PretranslationWriter = Substitute.For<IClientStreamWriter<InsertPretranslationsRequest>>();
Client
.InsertPretranslations(cancellationToken: Arg.Any<CancellationToken>())
.Returns(
Expand All @@ -97,7 +97,7 @@ public TestEnvironment()

public TranslationPlatformApi.TranslationPlatformApiClient Client { get; }
public ServalPlatformOutboxMessageHandler Handler { get; }
public IClientStreamWriter<InsertPretranslationRequest> PretranslationWriter { get; }
public IClientStreamWriter<InsertPretranslationsRequest> PretranslationWriter { get; }

private static AsyncUnaryCall<Empty> CreateEmptyUnaryCall()
{
Expand Down
1 change: 1 addition & 0 deletions src/Serval/src/Serval.ApiServer/Serval.ApiServer.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\Serval.Assessment\Serval.Assessment.csproj" />
<ProjectReference Include="..\Serval.DataFiles\Serval.DataFiles.csproj" />
<ProjectReference Include="..\Serval.Translation\Serval.Translation.csproj" />
<ProjectReference Include="..\Serval.Webhooks\Serval.Webhooks.csproj" />
Expand Down
91 changes: 53 additions & 38 deletions src/Serval/src/Serval.ApiServer/Startup.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ public class Startup(IConfiguration configuration, IWebHostEnvironment environme

public void ConfigureServices(IServiceCollection services)
{
services.AddFeatureManagement();
services.AddRouting(o => o.LowercaseUrls = true);
services.AddOutputCache(options =>
{
Expand Down Expand Up @@ -71,10 +72,12 @@ public void ConfigureServices(IServiceCollection services)
.AddMongoDataAccess(cfg =>
{
cfg.AddTranslationRepositories();
cfg.AddAssessmentRepositories();
cfg.AddDataFilesRepositories();
cfg.AddWebhooksRepositories();
})
.AddTranslation()
.AddAssessment()
.AddDataFiles()
.AddWebhooks();
services.AddTransient<IUrlService, UrlService>();
Expand Down Expand Up @@ -102,6 +105,7 @@ public void ConfigureServices(IServiceCollection services)
services.AddMediator(cfg =>
{
cfg.AddTranslationConsumers();
cfg.AddAssessmentConsumers();
cfg.AddDataFilesConsumers();
cfg.AddWebhooksConsumers();
});
Expand All @@ -128,49 +132,59 @@ public void ConfigureServices(IServiceCollection services)
Version[] versions = [new Version(1, 0)];
foreach (Version version in versions)
{
services.AddSwaggerDocument(o =>
{
o.SchemaSettings.SchemaType = SchemaType.Swagger2;
o.Title = "Serval API";
o.Description = "Natural language processing services for minority language Bible translation.";
o.DocumentName = "v" + version.Major;
o.ApiGroupNames = new[] { "v" + version.Major };
o.Version = version.Major + "." + version.Minor;
o.SchemaSettings.SchemaNameGenerator = new ServalSchemaNameGenerator();
o.UseControllerSummaryAsTagDescription = true;
o.AddSecurity(
"bearer",
Enumerable.Empty<string>(),
new OpenApiSecurityScheme
services.AddSwaggerDocument(
(o, sp) =>
{
o.SchemaSettings.SchemaType = SchemaType.Swagger2;
o.Title = "Serval API";
o.Description = "Natural language processing services for minority language Bible translation.";
o.DocumentName = "v" + version.Major;
o.ApiGroupNames = ["v" + version.Major];
o.Version = version.Major + "." + version.Minor;
var featureManager = sp.GetRequiredService<IFeatureManager>();
if (!featureManager.IsEnabledAsync("Assessment").WaitAndUnwrapException())
{
Type = OpenApiSecuritySchemeType.OAuth2,
Description = "Auth0 Client Credentials Flow",
Flow = OpenApiOAuth2Flow.Application,
Flows = new OpenApiOAuthFlows
o.AddOperationFilter(ctxt =>
!(ctxt.ControllerType.Namespace?.StartsWith("Serval.Assessment") ?? true)
);
}
o.SchemaSettings.SchemaNameGenerator = new ServalSchemaNameGenerator();
o.UseControllerSummaryAsTagDescription = true;
o.AddSecurity(
"bearer",
Enumerable.Empty<string>(),
new OpenApiSecurityScheme
{
ClientCredentials = new OpenApiOAuthFlow
Type = OpenApiSecuritySchemeType.OAuth2,
Description = "Auth0 Client Credentials Flow",
Flow = OpenApiOAuth2Flow.Application,
Flows = new OpenApiOAuthFlows
{
AuthorizationUrl = $"{authority}authorize",
TokenUrl = $"{authority}oauth/token"
}
},
}
);
o.OperationProcessors.Add(new AspNetCoreOperationSecurityScopeProcessor("bearer"));
ClientCredentials = new OpenApiOAuthFlow
{
AuthorizationUrl = $"{authority}authorize",
TokenUrl = $"{authority}oauth/token"
}
},
}
);
o.OperationProcessors.Add(new AspNetCoreOperationSecurityScopeProcessor("bearer"));
o.SchemaSettings.AllowReferencesWithProperties = true;
o.PostProcess = document =>
{
string prefix = "/api/v" + version.Major;
document.Servers.Add(new OpenApiServer { Url = prefix });
foreach (KeyValuePair<string, OpenApiPathItem> pair in document.Paths.ToArray())
o.SchemaSettings.AllowReferencesWithProperties = true;
o.PostProcess = document =>
{
document.Paths.Remove(pair.Key);
document.Paths[pair.Key[prefix.Length..]] = pair.Value;
}
};
});
string prefix = "/api/v" + version.Major;
document.Servers.Add(new OpenApiServer { Url = prefix });
foreach (KeyValuePair<string, OpenApiPathItem> pair in document.Paths.ToArray())
{
document.Paths.Remove(pair.Key);
document.Paths[pair.Key[prefix.Length..]] = pair.Value;
}
};
}
);
}
if (Environment.IsDevelopment())
{
Expand Down Expand Up @@ -207,6 +221,7 @@ public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
x.MapControllers();
x.MapServalTranslationServices();
x.MapServalAssessmentServices();
x.MapHangfireDashboard();
});

Expand Down
2 changes: 2 additions & 0 deletions src/Serval/src/Serval.ApiServer/Usings.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,9 @@
global using Microsoft.AspNetCore.Mvc;
global using Microsoft.AspNetCore.OutputCaching;
global using Microsoft.Extensions.Diagnostics.HealthChecks;
global using Microsoft.FeatureManagement;
global using Microsoft.IdentityModel.Tokens;
global using Nito.AsyncEx.Synchronous;
global using NJsonSchema;
global using NJsonSchema.Generation;
global using NSwag;
Expand Down
Loading

0 comments on commit 0a9a67b

Please sign in to comment.