Skip to content

Commit

Permalink
Reworked projectType to projectInterfaces, reworked ParatextStandard …
Browse files Browse the repository at this point in the history
…file system format to match Paratext 9 (#918)
  • Loading branch information
tjcouch-sil authored Jun 4, 2024
2 parents 045ec59 + 61ea65a commit 47bb1ee
Show file tree
Hide file tree
Showing 76 changed files with 3,409 additions and 1,566 deletions.
4 changes: 2 additions & 2 deletions c-sharp-tests/DummyLocalParatextProjects.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
using Paranext.DataProvider.Projects;
using System.Diagnostics.CodeAnalysis;
using Paranext.DataProvider.Projects;

namespace TestParanextDataProvider
{
Expand All @@ -11,7 +11,7 @@ public void FakeAddProject(ProjectDetails details)
_projectDetailsMap[details.Metadata.ID] = details;
}

public override void Initialize()
public override void Initialize(bool shouldIncludePT9ProjectsOnWindows)
{
// Nothing to do
}
Expand Down
208 changes: 139 additions & 69 deletions c-sharp-tests/DummyPapiClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,18 @@
using Paranext.DataProvider.MessageHandlers;
using Paranext.DataProvider.Messages;
using Paranext.DataProvider.MessageTransports;
using Paranext.DataProvider.Projects;
using Paranext.DataProvider.Services;

namespace TestParanextDataProvider
{
[ExcludeFromCodeCoverage]
internal class DummyPapiClient : PapiClient
{
private readonly Dictionary<string, Func<MessageEvent, Message?>> _eventHandlers = new();
private readonly Dictionary<string, List<(string newValue, string oldValue)>> _validSettings = new();
private readonly Dictionary<
string,
List<(string newValue, string oldValue)>
> _validSettings = new();

public Stack<Message?> EventMessages { get; } = new();

Expand All @@ -32,7 +35,11 @@ public IEnumerable<Message> FakeMessageFromServer(Message message)
return handler.HandleMessage(message);
}

public void AddSettingValueToTreatAsValid(string pbSettingName, string newValue, string oldValue)
public void AddSettingValueToTreatAsValid(
string pbSettingName,
string newValue,
string oldValue
)
{
if (!_validSettings.TryGetValue(pbSettingName, out var values))
{
Expand Down Expand Up @@ -91,89 +98,152 @@ public override void SendEvent(MessageEvent message)
EventMessages.Push(result);
}

public override void SendRequest(string requestType, object requestContents,
Action<bool, object?> responseCallback)
public override void SendRequest(
string requestType,
object requestContents,
Action<bool, object?> responseCallback
)
{
Task.Delay(1).ContinueWith(async _ =>
{
bool success = false;
object? result = null;

if ((requestType == "object:ProjectSettingsService.function") &&
(requestContents is string[] details) &&
(details.Length > 2))
Task.Delay(1)
.ContinueWith(async _ =>
{
// If this is a setting known to both Platform.Bible and Paratext, do the
// translation from the Paratext settings key to the PB setting id.
// If it's a custom settings key, then perhaps it's one we've registered to
// handle, and no translation is needed.
var ourSettingName = details[1];
var pbSettingName = ProjectSettings
.GetPlatformBibleSettingNameFromParatextSettingName(ourSettingName);

if (ourSettingName != null)
bool success = false;
object? result = null;

// Try to run registered request handlers
var responder = (MessageHandlerRequestByRequestType)
_messageHandlersByMessageType[MessageType.REQUEST];
var requestMessage =
(requestContents is JsonElement jse)
? new MessageRequest(requestType, 0, jse)
: new MessageRequest(requestType, 0, requestContents);
var response = responder.HandleMessage(requestMessage);
// There should be just one response message
if (response.Count() > 1)
throw new Exception(
$"Somehow there were multiple response messages for request {requestType}"
);
// If there were no responses, there isn't a registered request handler
if (response.Count() == 1)
{
switch (details[0])
if (response.First() is MessageResponse messageResponse)
{
case "isValid":
if (details.Length == 6 && details[4] == ProjectType.Paratext)
{
success = true;
// Might be a setting we've registered to handle.
var isValidRequestContents = new []
{details[2], details[3], details[5]};
if (TryValidationUsingRegisteredHandler(isValidRequestContents,
ourSettingName, out var isValid))
{
result = isValid;
}
else if (pbSettingName == null)
success = messageResponse.Success;
result = messageResponse.Contents;
}
else
{
throw new Exception(
$"Somehow the message handler for {requestType} responded with a message that was not a MessageResponse"
);
}
}
// Special hard-coded request handlers. We should probably try to get rid of these over time
else if (
(requestType == "object:ProjectSettingsService.function")
&& (requestContents is object[] details)
&& (details.Length >= 2)
)
{
// If this is a setting known to both Platform.Bible and Paratext, do the
// translation from the Paratext settings key to the PB setting id.
// If it's a custom settings key, then perhaps it's one we've registered to
// handle, and no translation is needed.
var ourSettingName = (string)details[1];
var pbSettingName =
ProjectSettings.GetPlatformBibleSettingNameFromParatextSettingName(
ourSettingName
);

if (ourSettingName != null)
{
switch (details[0])
{
case "isValid":
if (details.Length == 5)
{
// Per comment in isValid (in
// project-settings.service-host.ts), if there is no
// validator just let the change go through
result = true;
success = true;
// Might be a setting we've registered to handle.
var isValidRequestContents = new[]
{
// From ProjectSettingsService.IsValid
// new value
details[2],
// current value
details[3],
// all changes?
details[4]
};
if (
TryValidationUsingRegisteredHandler(
isValidRequestContents,
ourSettingName,
out var isValid
)
)
{
result = isValid;
}
else if (pbSettingName == null)
{
// Per comment in isValid (in
// project-settings.service-host.ts), if there is no
// validator just let the change go through
result = true;
}
else
{
result =
_validSettings.TryGetValue(
pbSettingName,
out var validValues
)
&& validValues.Any(vv =>
vv.newValue == (string)details[2]
&& vv.oldValue == (string)details[3]
);
}
}
else
break;
case "getDefault":
if (details.Length == 2 && pbSettingName != null)
{
result = _validSettings.TryGetValue(pbSettingName,
out var validValues) && validValues.Any(vv =>
vv.newValue == details[2] &&
vv.oldValue == details[3]);
success = true;
result = $"default value for {pbSettingName}";
}
}
break;
case "getDefault":
if (details.Length == 3 &&
details[2] == ProjectType.Paratext &&
pbSettingName != null)
{
success = true;
result = $"default value for {pbSettingName}";
}
break;
default:
break;
break;
default:
break;
}
}
}
}
// Otherwise we didn't find a request handler, so it should just be a request
// failure. Can keep the original values

await Task.Run(() => responseCallback(success,
JsonSerializer.SerializeToElement(result)));
});
await Task.Run(
() => responseCallback(success, JsonSerializer.SerializeToElement(result))
);
});
}

private bool TryValidationUsingRegisteredHandler(string[] requestContents, string settingName,
out bool isValid)
private bool TryValidationUsingRegisteredHandler(
object[] requestContents,
string settingName,
out bool isValid
)
{
isValid = true;
if (!_messageHandlersByMessageType.TryGetValue(
MessageType.REQUEST, out var responder))
if (!_messageHandlersByMessageType.TryGetValue(MessageType.REQUEST, out var responder))
return false;

var msgRequest = new MessageRequest(ProjectSettingsService.GetValidatorKey(settingName),
GetRequestId(), requestContents);
var responseMsg = responder.HandleMessage(msgRequest).OfType<MessageResponse>()
var msgRequest = new MessageRequest(
ProjectSettingsService.GetValidatorKey(settingName),
GetRequestId(),
requestContents
);
var responseMsg = responder
.HandleMessage(msgRequest)
.OfType<MessageResponse>()
.FirstOrDefault();

if (responseMsg == null)
Expand Down
50 changes: 50 additions & 0 deletions c-sharp-tests/NetworkObjects/DummySettingsService.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
using System.Diagnostics.CodeAnalysis;
using System.Text.Json.Nodes;
using Paranext.DataProvider.MessageHandlers;
using Paranext.DataProvider.MessageTransports;
using Paranext.DataProvider.NetworkObjects;
using Paranext.DataProvider.Services;

namespace TestParanextDataProvider;

[ExcludeFromCodeCoverage]
internal class DummySettingsService : DataProvider
{
private readonly Dictionary<string, object> _settingValues = [];
private readonly List<string> _supportedFunctions = ["get"];

public DummySettingsService(PapiClient papiClient)
: base(SettingsService.SETTINGS_SERVICE_NAME, papiClient) { }

public void AddSettingValue(string key, object value)
{
_settingValues.Add(key, value);
}

public void ClearSettingValues()
{
_settingValues.Clear();
}

protected override Task StartDataProvider()
{
return Task.CompletedTask;
}

protected override List<string> GetFunctionNames()
{
return _supportedFunctions;
}

protected override ResponseToRequest HandleRequest(string functionName, JsonArray args)
{
return functionName switch
{
"get"
=> _settingValues.ContainsKey(args[0]!.ToString())
? ResponseToRequest.Succeeded(_settingValues[args[0]!.ToString()])
: ResponseToRequest.Failed($"Could not find value for setting {args[0]}"),
_ => ResponseToRequest.Failed($"Unexpected function: {functionName}")
};
}
}
6 changes: 3 additions & 3 deletions c-sharp-tests/PapiTestBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ internal abstract class PapiTestBase

#region Test setup/teardown
[SetUp]
public virtual void TestSetup()
public virtual async Task TestSetup()

Check warning on line 25 in c-sharp-tests/PapiTestBase.cs

View workflow job for this annotation

GitHub Actions / Build on windows-latest, .Net 8.0.x

This async method lacks 'await' operators and will run synchronously. Consider using the 'await' operator to await non-blocking API calls, or 'await Task.Run(...)' to do CPU-bound work on a background thread.

Check warning on line 25 in c-sharp-tests/PapiTestBase.cs

View workflow job for this annotation

GitHub Actions / Build on macos-latest, .Net 8.0.x

This async method lacks 'await' operators and will run synchronously. Consider using the 'await' operator to await non-blocking API calls, or 'await Task.Run(...)' to do CPU-bound work on a background thread.

Check warning on line 25 in c-sharp-tests/PapiTestBase.cs

View workflow job for this annotation

GitHub Actions / Build on ubuntu-latest, .Net 8.0.x

This async method lacks 'await' operators and will run synchronously. Consider using the 'await' operator to await non-blocking API calls, or 'await Task.Run(...)' to do CPU-bound work on a background thread.
{
if (OperatingSystem.IsMacOS())
Assert.Ignore("Mac is missing ICU support so these tests will not work");
Expand Down Expand Up @@ -97,10 +97,10 @@ protected static ProjectDetails CreateProjectDetails(ScrText scrText)
protected static ProjectDetails CreateProjectDetails(
string id,
string name,
string projectType = ""
List<string>? projectInterfaces = null
)
{
ProjectMetadata metadata = new(id, name, projectType);
ProjectMetadata metadata = new(id, name, projectInterfaces ?? []);
return new ProjectDetails(metadata, "testDirectoryThatDoesNotExist");
}

Expand Down
10 changes: 5 additions & 5 deletions c-sharp-tests/Projects/LocalParatextProjectsTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ public void Initialize_NonStandardFolderName_ProjectIsRetrievable(string folder)
{
CreateTempProject(folder, CreateParatextProjectMetadata("ABC"));

_localProjects.Initialize();
_localProjects.Initialize(false);
var details = _localProjects.GetAllProjectDetails().Single();
Assert.That(details, Is.EqualTo(_localProjects.GetProjectDetails(TEST_ID)));
Assert.That(details.HomeDirectory, Does.EndWith(folder));
Expand All @@ -57,7 +57,7 @@ public void Initialize_NameDoesNotMatch_ProjectIsRetrievable(string folder, stri
{
var metadata = CreateParatextProjectMetadata(name);
CreateTempProject(folder, metadata);
_localProjects.Initialize();
_localProjects.Initialize(false);
var details = _localProjects.GetAllProjectDetails().Single();
Assert.That(details, Is.EqualTo(_localProjects.GetProjectDetails(TEST_ID)));
Assert.That(details.HomeDirectory, Does.EndWith(folder));
Expand All @@ -71,7 +71,7 @@ public void Initialize_IdDoesNotMatch_ProjectIsRetrievable(string folder, string
{
var metadata = CreateParatextProjectMetadata(name);
CreateTempProject(folder, metadata);
_localProjects.Initialize();
_localProjects.Initialize(false);
var details = _localProjects.GetAllProjectDetails().Single();
Assert.That(details, Is.EqualTo(_localProjects.GetProjectDetails(TEST_ID)));
Assert.That(details.HomeDirectory, Does.EndWith(folder));
Expand All @@ -87,7 +87,7 @@ public void Initialize_IdAndNameMatch_ProjectIsRetrievable(string folder, string
{
var metadata = CreateParatextProjectMetadata(name);
CreateTempProject(folder, metadata);
_localProjects.Initialize();
_localProjects.Initialize(false);
var details = _localProjects.GetAllProjectDetails().Single();
Assert.That(details, Is.EqualTo(_localProjects.GetProjectDetails(TEST_ID)));
Assert.That(details.HomeDirectory, Does.EndWith(folder));
Expand All @@ -104,6 +104,6 @@ private void CreateTempProject(string folder, ProjectMetadata metadata)

private static ProjectMetadata CreateParatextProjectMetadata(string name)
{
return new ProjectMetadata(TEST_ID, name, "paratext");
return new ProjectMetadata(TEST_ID, name, ["paratext"]);
}
}
1 change: 1 addition & 0 deletions c-sharp-tests/Projects/MinimalParatextProjectSettings.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ namespace TestParanextDataProvider.Projects
[XmlRoot("ScriptureText")]
public class MinimalParatextProjectSettings
{
public string? Name { get; set; }
public string? Guid { get; set; }
}
}
Loading

0 comments on commit 47bb1ee

Please sign in to comment.