Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions Directory.Build.props
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
<Project>
<ItemGroup>
<PackageReference Include="Microsoft.Net.Compilers.Toolset" Version="3.3.1">
<PackageReference Include="Microsoft.Net.Compilers.Toolset" Version="4.12.0">
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="Microsoft.NETFramework.ReferenceAssemblies" Version="1.0.0-preview.2">
<PackageReference Include="Microsoft.NETFramework.ReferenceAssemblies" Version="1.0.3">
<PrivateAssets>all</PrivateAssets>
</PackageReference>
</ItemGroup>
Expand Down
33 changes: 33 additions & 0 deletions SymSpell.SWS/Controllers/LookupController.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
using Microsoft.AspNetCore.Mvc;

namespace SymSpellSWS.Controllers;

[ApiController]
[Route("[controller]")]
public class LookupController : ControllerBase
{
private readonly SymSpell _symSpell;

public LookupController(SymSpell symSpell)
{
_symSpell = symSpell;
}

[HttpGet]
public IEnumerable<SymSpell.SuggestItem> Get([FromQuery] string word, [FromQuery] string verbosity = "Closest")
{
if (string.IsNullOrWhiteSpace(word))
{
return Enumerable.Empty<SymSpell.SuggestItem>();
}

var verbosityEnum = verbosity.ToLower() switch
{
"top" => SymSpell.Verbosity.Top,
"all" => SymSpell.Verbosity.All,
_ => SymSpell.Verbosity.Closest,
};

return _symSpell.Lookup(word, verbosityEnum);
}
}
35 changes: 35 additions & 0 deletions SymSpell.SWS/Program.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
var builder = WebApplication.CreateBuilder(args);

// Add services to the container.
builder.Services.AddControllers().AddJsonOptions(options =>
{
options.JsonSerializerOptions.IncludeFields = true;
});

// Configure and register SymSpell as a singleton
const int initialCapacity = 82765;
const int maxEditDistance = 2;
const int prefixLength = 7;
var symSpell = new SymSpell(initialCapacity, maxEditDistance, prefixLength);

// Load dictionary
string path = Path.Combine(AppContext.BaseDirectory, "frequency_dictionary_en_82_765.txt");
if (!symSpell.LoadDictionary(path, 0, 1))
{
Console.Error.WriteLine("Dictionary file not found: " + Path.GetFullPath(path));
// App will start, but lookups will be incorrect.
}
builder.Services.AddSingleton(symSpell);


var app = builder.Build();

// Configure the HTTP request pipeline.

app.UseHttpsRedirection();

app.UseAuthorization();

app.MapControllers();

app.Run();
29 changes: 29 additions & 0 deletions SymSpell.SWS/Properties/launchSettings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
{
"$schema": "http://json.schemastore.org/launchsettings.json",
"iisSettings": {
"windowsAuthentication": false,
"anonymousAuthentication": true,
"iisExpress": {
"applicationUrl": "http://localhost:5000",
"sslPort": 5001
}
},
"profiles": {
"IIS Express": {
"commandName": "IISExpress",
"launchBrowser": false,
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"SymSpell.SWS": {
"commandName": "Project",
"dotnetRunMessages": "true",
"launchBrowser": false,
"applicationUrl": "https://localhost:5001;http://localhost:5000",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
}
}
}
20 changes: 20 additions & 0 deletions SymSpell.SWS/SymSpell.SWS.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<Project Sdk="Microsoft.NET.Sdk.Web">

<PropertyGroup>
<TargetFramework>net9.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>

<ItemGroup>
<ProjectReference Include="..\SymSpell\SymSpell.csproj" />
</ItemGroup>

<ItemGroup>
<!-- Assumes frequency_dictionary_en_82_765.txt is in the SymSpell folder -->
<Content Include="..\SymSpell\frequency_dictionary_en_82_765.txt" Link="frequency_dictionary_en_82_765.txt">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
</ItemGroup>

</Project>
8 changes: 8 additions & 0 deletions SymSpell.SWS/appsettings.Development.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Information"
}
}
}
9 changes: 9 additions & 0 deletions SymSpell.SWS/appsettings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*"
}
43 changes: 43 additions & 0 deletions SymSpell.SWS/test_sws.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import json
import ssl
import sys
import urllib.request

def main():
if len(sys.argv) < 2:
print("Certificate path not provided.", file=sys.stderr)
sys.exit(1)

cert_path = sys.argv[1]
context = ssl.create_default_context(cafile=cert_path)

try:
# The service redirects http to https, so let's use https directly
# We request Verbosity.All to get all suggestions within maxEditDistance
url = "https://localhost:5001/lookup?word=watevr&verbosity=All"
with urllib.request.urlopen(url, context=context) as response:
if response.status != 200:
print(f"Error: Received status code {response.status}", file=sys.stderr)
sys.exit(1)

data = json.loads(response.read().decode())

if not data:
print("Error: Received empty response.", file=sys.stderr)
sys.exit(1)

# Check if 'whatever' is in the list of suggestions
found = any(s.get('term') == 'whatever' for s in data if isinstance(s, dict))

if found:
print("Test passed!")
else:
print(f"Test failed: Expected a suggestion of 'whatever' in the results, but got: {json.dumps(data)}", file=sys.stderr)
sys.exit(1)

except Exception as e:
print(f"An error occurred: {e}", file=sys.stderr)
sys.exit(1)

if __name__ == "__main__":
main()
8 changes: 7 additions & 1 deletion SymSpell.sln
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
Microsoft Visual Studio Solution File, Format Version 12.00
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 15
VisualStudioVersion = 15.0.27130.2027
MinimumVisualStudioVersion = 15.0.26124.0
Expand All @@ -25,6 +25,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "miscs", "miscs", "{B96EF85F
RunThisFirst_dotnetrestore.bat = RunThisFirst_dotnetrestore.bat
EndProjectSection
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SymSpell.SWS", "SymSpell.SWS\SymSpell.SWS.csproj", "{6BFFBDBC-CB71-4451-BDF3-246446329B2B}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -59,6 +61,10 @@ Global
{9C78B67B-6782-4F28-B5F7-14C94E2CE017}.Debug|Any CPU.Build.0 = Debug|Any CPU
{9C78B67B-6782-4F28-B5F7-14C94E2CE017}.Release|Any CPU.ActiveCfg = Release|Any CPU
{9C78B67B-6782-4F28-B5F7-14C94E2CE017}.Release|Any CPU.Build.0 = Release|Any CPU
{6BFFBDBC-CB71-4451-BDF3-246446329B2B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{6BFFBDBC-CB71-4451-BDF3-246446329B2B}.Debug|Any CPU.Build.0 = Debug|Any CPU
{6BFFBDBC-CB71-4451-BDF3-246446329B2B}.Release|Any CPU.ActiveCfg = Release|Any CPU
{6BFFBDBC-CB71-4451-BDF3-246446329B2B}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand Down
19 changes: 19 additions & 0 deletions test_sws.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
#!/bin/bash
set -e

CERT_PATH=SymSpell.SWS/sws-test-cert.pem
echo "Exporting dev cert for testing to $CERT_PATH..."
dotnet dev-certs https --format PEM --export-path $CERT_PATH

# Ensure cert is removed and web service is killed on exit
trap 'echo "Stopping web service..."; kill $SWS_PID; echo "Cleaning up cert file..."; rm -f $CERT_PATH' EXIT

echo "Starting SymSpell.SWS web service..."
dotnet run --project SymSpell.SWS/SymSpell.SWS.csproj &
SWS_PID=$!

echo "Waiting for service to start..."
sleep 10

echo "Running test script..."
python3 SymSpell.SWS/test_sws.py $CERT_PATH