diff --git a/README.md b/README.md index 8999244..eaad68d 100644 --- a/README.md +++ b/README.md @@ -2,8 +2,8 @@ This repository contains the source code of the concordancer which is a part of the [Terminology Portal](https://github.com/clarinsi/rsdo_term_portal). There are two main parts: -- Web application with backend (API interface) written in ASP.NET Core 6 and frontend written in React -- System Manager which is written in .NET 6 and is used to initially set up the system +- Web application with backend (API interface) written in ASP.NET Core 10 and frontend written in React +- System Manager which is written in .NET 10 and is used to initially set up the system ## Prerequisites Web application requires following external dependencies @@ -16,7 +16,7 @@ To build a frontend application successfully, first, [Node.js](https://nodejs.or Use `npm install` to install all required dependencies, and `npm run build` to build the application. For more info see the [Readme](src/apps/Rsdo.Concordancer.Api/WebApp/README.md) inside the frontend [root](src/apps/Rsdo.Concordancer.Api/WebApp) folder. -### Backend (ASP.NET Core 6) and System Manager (.NET 6) +### Backend (ASP.NET Core 10) and System Manager (.NET 10) Open [Rsdo.Concordancer.sln](src/Rsdo.Concordancer.sln) inside of your IDE and build the solution, or use `dotnet build` command to build the solution. All required dependencies will be installed via NuGet packages. ## Creating docker images @@ -38,13 +38,13 @@ Use following [Dockerfile](src/apps/Rsdo.Concordancer.SystemManager/Dockerfile) ### Web application Web application must be configured with the following Environment variables and values: -- `RSDO:Database:MasterConnectionString` holds connection string to the main database; e.g.: `rsdo_db;Username=rsdo;Password=strongpassword;Database=Concordancer` +- `RSDO:Database:MasterConnectionString` holds connection string to the main database; e.g.: `Host=rsdo_db;Username=rsdo;Password=strongpassword;Database=Concordancer` - `RSDO:Elastic:ConnectionString` holds connection string to OpenSearch server; e.g.: `http://rsdo_opensearch:9200` - `RSDO:Tokenizer:ClasslaUrl` holds and url to external service which is used for tokenizing search strings; e.g.: `https://orodja.cjvt.si/oznacevalnik/ajax_api/v1/slv/process` ### System Manager System manager must be configured with the following Environment variables and values: -- `RSDO:Database:MasterConnectionString` holds connection string to the main database; e.g.: `rsdo_db;Username=rsdo;Password=strongpassword;Database=Concordancer` +- `RSDO:Database:MasterConnectionString` holds connection string to the main database; e.g.: `Host=rsdo_db;Username=rsdo;Password=strongpassword;Database=Concordancer` - `RSDO:Elastic:ConnectionString` holds connection string to OpenSearch server; e.g.: `http://rsdo_opensearch:9200` ## Using System Manager diff --git a/local-packages/Rsdo.StyleCop.1.0.0.nupkg b/local-packages/Rsdo.StyleCop.1.0.0.nupkg deleted file mode 100644 index 39accc0..0000000 Binary files a/local-packages/Rsdo.StyleCop.1.0.0.nupkg and /dev/null differ diff --git a/src/.gitignore b/src/.gitignore index 34c8dee..80fa0fa 100644 --- a/src/.gitignore +++ b/src/.gitignore @@ -1,7 +1,7 @@ ## Ignore Visual Studio temporary files, build results, and ## files generated by popular Visual Studio add-ons. ## -## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore +## Get latest from https://github.com/github/gitignore/blob/main/VisualStudio.gitignore # User-specific files *.rsuser @@ -9,6 +9,7 @@ *.user *.userosscache *.sln.docstates +*.env # User-specific files (MonoDevelop/Xamarin Studio) *.userprefs @@ -21,17 +22,37 @@ mono_crash.* [Dd]ebugPublic/ [Rr]elease/ [Rr]eleases/ -x64/ -x86/ + +[Dd]ebug/x64/ +[Dd]ebugPublic/x64/ +[Rr]elease/x64/ +[Rr]eleases/x64/ +bin/x64/ +obj/x64/ + +[Dd]ebug/x86/ +[Dd]ebugPublic/x86/ +[Rr]elease/x86/ +[Rr]eleases/x86/ +bin/x86/ +obj/x86/ + [Ww][Ii][Nn]32/ [Aa][Rr][Mm]/ [Aa][Rr][Mm]64/ +[Aa][Rr][Mm]64[Ee][Cc]/ bld/ -[Bb]in/ [Oo]bj/ +[Oo]ut/ [Ll]og/ [Ll]ogs/ +# Build results on 'Bin' directories +**/[Bb]in/* +# Uncomment if you have tasks that rely on *.refresh files to move binaries +# (https://github.com/github/gitignore/pull/3736) +#!**/[Bb]in/*.refresh + # Visual Studio 2015/2017 cache/options directory .vs/ # Uncomment if you have tasks that create the project's static files in wwwroot @@ -43,12 +64,16 @@ Generated\ Files/ # MSTest test Results [Tt]est[Rr]esult*/ [Bb]uild[Ll]og.* +*.trx # NUnit *.VisualState.xml TestResult.xml nunit-*.xml +# Approval Tests result files +*.received.* + # Build Results of an ATL Project [Dd]ebugPS/ [Rr]eleasePS/ @@ -61,6 +86,7 @@ BenchmarkDotNet.Artifacts/ project.lock.json project.fragment.lock.json artifacts/ +.artifacts/ # ASP.NET Scaffolding ScaffoldingReadMe.txt @@ -75,6 +101,7 @@ StyleCopReport.xml *.ilk *.meta *.obj +*.idb *.iobj *.pch *.pdb @@ -82,6 +109,8 @@ StyleCopReport.xml *.pgc *.pgd *.rsp +# but not Directory.Build.rsp, as it configures directory-level build defaults +!Directory.Build.rsp *.sbr *.tlb *.tli @@ -153,6 +182,7 @@ coverage*.info # NCrunch _NCrunch_* +.NCrunch_* .*crunch*.local.xml nCrunchTemp_* @@ -206,9 +236,6 @@ PublishScripts/ *.nuget.props *.nuget.targets -# Nuget personal access tokens and Credentials -nuget.config - # Microsoft Azure Build Output csx/ *.build.csdef @@ -297,6 +324,14 @@ node_modules/ # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) *.vbw +# Visual Studio 6 workspace and project file (working project files containing files to include in project) +*.dsw +*.dsp + +# Visual Studio 6 technical files +*.ncb +*.aps + # Visual Studio LightSwitch build output **/*.HTMLClient/GeneratedArtifacts **/*.DesktopClient/GeneratedArtifacts @@ -306,22 +341,22 @@ node_modules/ _Pvt_Extensions # Paket dependency manager -.paket/paket.exe +**/.paket/paket.exe paket-files/ # FAKE - F# Make -.fake/ +**/.fake/ # CodeRush personal settings -.cr/personal +**/.cr/personal # Python Tools for Visual Studio (PTVS) -__pycache__/ +**/__pycache__/ *.pyc # Cake - Uncomment if you are using it -# tools/** -# !tools/packages.config +#tools/** +#!tools/packages.config # Tabs Studio *.tss @@ -343,15 +378,22 @@ ASALocalRun/ # MSBuild Binary and Structured Log *.binlog +MSBuild_Logs/ + +# AWS SAM Build and Temporary Artifacts folder +.aws-sam # NVidia Nsight GPU debugger configuration file *.nvuser # MFractors (Xamarin productivity tool) working folder -.mfractor/ +**/.mfractor/ # Local History for Visual Studio -.localhistory/ +**/.localhistory/ + +# Visual Studio History (VSHistory) files +.vshistory/ # BeatPulse healthcheck temp database healthchecksdb @@ -360,7 +402,7 @@ healthchecksdb MigrationBackup/ # Ionide (cross platform F# VS Code tools) working folder -.ionide/ +**/.ionide/ # Fody - auto-generated XML schema FodyWeavers.xsd @@ -371,11 +413,14 @@ FodyWeavers.xsd !.vscode/tasks.json !.vscode/launch.json !.vscode/extensions.json -*.code-workspace +!.vscode/*.code-snippets # Local History for Visual Studio Code .history/ +# Built Visual Studio Code Extensions +*.vsix + # Windows Installer files from build outputs *.cab *.msi @@ -385,4 +430,3 @@ FodyWeavers.xsd # JetBrains Rider .idea/ -*.sln.iml diff --git a/src/Directory.Build.props b/src/Directory.Build.props new file mode 100644 index 0000000..a88c9a0 --- /dev/null +++ b/src/Directory.Build.props @@ -0,0 +1,11 @@ + + + net10.0 + true + true + 10-recommended + true + $(NoWarn);CS1591 + 1.0.4 + + diff --git a/src/Directory.Packages.props b/src/Directory.Packages.props new file mode 100644 index 0000000..5f9772f --- /dev/null +++ b/src/Directory.Packages.props @@ -0,0 +1,42 @@ + + + true + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Rsdo.Concordancer.sln b/src/Rsdo.Concordancer.sln index abc14fc..9f046a7 100644 --- a/src/Rsdo.Concordancer.sln +++ b/src/Rsdo.Concordancer.sln @@ -24,6 +24,13 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Rsdo.Concordancer.Data", "c EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Rsdo.Concordancer.SystemManager", "apps\Rsdo.Concordancer.SystemManager\Rsdo.Concordancer.SystemManager.csproj", "{41B26AD1-C0A5-4796-95D0-CB84FB574A83}" EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{0BC9D8CB-3F46-40F4-B769-E2605F03E0F6}" + ProjectSection(SolutionItems) = preProject + Directory.Build.props = Directory.Build.props + Directory.Packages.props = Directory.Packages.props + .gitignore = .gitignore + EndProjectSection +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU diff --git a/src/Rsdo.Concordancer.sln.DotSettings b/src/Rsdo.Concordancer.sln.DotSettings deleted file mode 100644 index 02b8531..0000000 --- a/src/Rsdo.Concordancer.sln.DotSettings +++ /dev/null @@ -1,357 +0,0 @@ - - C65+,E16+,FF58+,IE11+ - VISIBLE_FILES - DO_NOT_SHOW - DO_NOT_SHOW - <?xml version="1.0" encoding="utf-16"?><Profile name="Reformat Code IDE"><CSReorderTypeMembers>True</CSReorderTypeMembers><CSCodeStyleAttributes ArrangeTypeAccessModifier="True" ArrangeTypeMemberAccessModifier="True" SortModifiers="True" RemoveRedundantParentheses="False" AddMissingParentheses="True" ArrangeBraces="True" ArrangeAttributes="True" ArrangeArgumentsStyle="False" ArrangeCodeBodyStyle="False" /><CSArrangeQualifiers>True</CSArrangeQualifiers><CSOptimizeUsings><OptimizeUsings>True</OptimizeUsings><EmbraceInRegion>False</EmbraceInRegion><RegionName></RegionName></CSOptimizeUsings><CSReformatCode>True</CSReformatCode><CSFixBuiltinTypeReferences>True</CSFixBuiltinTypeReferences><CSShortenReferences>True</CSShortenReferences><XAMLCollapseEmptyTags>False</XAMLCollapseEmptyTags></Profile> - <?xml version="1.0" encoding="utf-16"?><Profile name="StyleCop"><CSUpdateFileHeader>False</CSUpdateFileHeader><CSArrangeQualifiers>True</CSArrangeQualifiers><CSOptimizeUsings><OptimizeUsings>True</OptimizeUsings><EmbraceInRegion>False</EmbraceInRegion><RegionName></RegionName></CSOptimizeUsings><CSReformatCode>True</CSReformatCode><CSReorderTypeMembers>True</CSReorderTypeMembers></Profile> - Required - Required - Required - Required - None - <?xml version="1.0" encoding="utf-16"?> -<Patterns xmlns="urn:schemas-jetbrains-com:member-reordering-patterns"> - <TypePattern DisplayName="Non-reorderable types"> - <TypePattern.Match> - <Or> - <And> - <Kind Is="Interface" /> - <Or> - <HasAttribute Name="System.Runtime.InteropServices.InterfaceTypeAttribute" /> - <HasAttribute Name="System.Runtime.InteropServices.ComImport" /> - </Or> - </And> - <Kind Is="Struct" /> - <HasAttribute Name="JetBrains.Annotations.NoReorderAttribute" /> - <HasAttribute Name="JetBrains.Annotations.NoReorder" /> - </Or> - </TypePattern.Match> - </TypePattern> - <TypePattern DisplayName="xUnit.net Test Classes" RemoveRegions="All"> - <TypePattern.Match> - <And> - <Kind Is="Class" /> - <HasMember> - <And> - <Kind Is="Method" /> - <HasAttribute Name="Xunit.FactAttribute" Inherited="True" /> - </And> - </HasMember> - </And> - </TypePattern.Match> - <Entry DisplayName="Setup/Teardown Methods"> - <Entry.Match> - <Or> - <Kind Is="Constructor" /> - <And> - <Kind Is="Method" /> - <ImplementsInterface Name="System.IDisposable" /> - </And> - </Or> - </Entry.Match> - <Entry.SortBy> - <Kind Order="Constructor" /> - </Entry.SortBy> - </Entry> - <Entry DisplayName="All other members" /> - <Entry DisplayName="Test Methods" Priority="100"> - <Entry.Match> - <And> - <Kind Is="Method" /> - <HasAttribute Name="Xunit.FactAttribute" /> - </And> - </Entry.Match> - <Entry.SortBy> - <Name /> - </Entry.SortBy> - </Entry> - </TypePattern> - <TypePattern DisplayName="NUnit Test Fixtures" RemoveRegions="All"> - <TypePattern.Match> - <And> - <Kind Is="Class" /> - <HasAttribute Name="NUnit.Framework.TestFixtureAttribute" Inherited="True" /> - </And> - </TypePattern.Match> - <Entry DisplayName="Setup/Teardown Methods"> - <Entry.Match> - <And> - <Kind Is="Method" /> - <Or> - <HasAttribute Name="NUnit.Framework.SetUpAttribute" Inherited="True" /> - <HasAttribute Name="NUnit.Framework.TearDownAttribute" Inherited="True" /> - <HasAttribute Name="NUnit.Framework.FixtureSetUpAttribute" Inherited="True" /> - <HasAttribute Name="NUnit.Framework.FixtureTearDownAttribute" Inherited="True" /> - </Or> - </And> - </Entry.Match> - </Entry> - <Entry DisplayName="All other members" /> - <Entry DisplayName="Test Methods" Priority="100"> - <Entry.Match> - <And> - <Kind Is="Method" /> - <HasAttribute Name="NUnit.Framework.TestAttribute" /> - </And> - </Entry.Match> - <Entry.SortBy> - <Name /> - </Entry.SortBy> - </Entry> - </TypePattern> - <TypePattern DisplayName="Default Pattern (StyleCop)" RemoveRegions="All"> - <Entry DisplayName="Constants"> - <Entry.Match> - <Kind Is="Constant" /> - </Entry.Match> - <Entry.SortBy> - <Access Order="Public Internal ProtectedInternal Protected Private" /> - <Name /> - </Entry.SortBy> - </Entry> - <Entry DisplayName="Static fields"> - <Entry.Match> - <And> - <Kind Is="Field" /> - <Static /> - </And> - </Entry.Match> - <Entry.SortBy> - <Access Order="Public Internal ProtectedInternal Protected Private" /> - <Readonly /> - <Name /> - </Entry.SortBy> - </Entry> - <Entry DisplayName="Fields"> - <Entry.Match> - <Kind Is="Field" /> - </Entry.Match> - <Entry.SortBy> - <Access Order="Public Internal ProtectedInternal Protected Private" /> - <Readonly /> - <Name /> - </Entry.SortBy> - </Entry> - <Entry DisplayName="Constructors and Destructors" Priority="200"> - <Entry.Match> - <Or> - <Kind Is="Constructor" /> - <Kind Is="Destructor" /> - </Or> - </Entry.Match> - <Entry.SortBy> - <Static /> - <Kind Order="Constructor Destructor" /> - <Access Order="Public Internal ProtectedInternal Protected Private" /> - </Entry.SortBy> - </Entry> - <Entry DisplayName="Delegates"> - <Entry.Match> - <Kind Is="Delegate" /> - </Entry.Match> - <Entry.SortBy> - <Access Order="Public Internal ProtectedInternal Protected Private" /> - <Static /> - <Name /> - </Entry.SortBy> - </Entry> - <Entry DisplayName="Public events"> - <Entry.Match> - <And> - <Kind Is="Event" /> - <Access Is="Public" /> - </And> - </Entry.Match> - <Entry.SortBy> - <Access Order="Public" /> - <Static /> - <Name /> - </Entry.SortBy> - </Entry> - <Entry DisplayName="Interface events"> - <Entry.Match> - <And> - <Kind Is="Event" /> - <ImplementsInterface /> - </And> - </Entry.Match> - <Entry.SortBy> - <ImplementsInterface Immediate="True" /> - <Name /> - </Entry.SortBy> - </Entry> - <Entry DisplayName="Other events"> - <Entry.Match> - <Kind Is="Event" /> - </Entry.Match> - <Entry.SortBy> - <Access Order="Public Internal ProtectedInternal Protected Private" /> - <Static /> - <Name /> - </Entry.SortBy> - </Entry> - <Entry DisplayName="Enums"> - <Entry.Match> - <Kind Is="Enum" /> - </Entry.Match> - <Entry.SortBy> - <Access Order="Public Internal ProtectedInternal Protected Private" /> - <Name /> - </Entry.SortBy> - </Entry> - <Entry DisplayName="Interfaces"> - <Entry.Match> - <Kind Is="Interface" /> - </Entry.Match> - <Entry.SortBy> - <Access Order="Public Internal ProtectedInternal Protected Private" /> - <Name /> - </Entry.SortBy> - </Entry> - <Entry DisplayName="Public properties"> - <Entry.Match> - <And> - <Kind Is="Property" /> - <Access Is="Public" /> - </And> - </Entry.Match> - <Entry.SortBy> - <Static /> - <Name /> - </Entry.SortBy> - </Entry> - <Entry DisplayName="Interface properties"> - <Entry.Match> - <And> - <Kind Is="Property" /> - <ImplementsInterface /> - </And> - </Entry.Match> - <Entry.SortBy> - <ImplementsInterface Immediate="True" /> - <Name /> - </Entry.SortBy> - </Entry> - <Entry DisplayName="Other properties"> - <Entry.Match> - <Kind Is="Property" /> - </Entry.Match> - <Entry.SortBy> - <Access Order="Public Internal ProtectedInternal Protected Private" /> - <Static /> - <Name /> - </Entry.SortBy> - </Entry> - <Entry DisplayName="Public indexers" Priority="1000"> - <Entry.Match> - <And> - <Kind Is="Indexer" /> - <Access Is="Public" /> - </And> - </Entry.Match> - <Entry.SortBy> - <Static /> - <Name /> - </Entry.SortBy> - </Entry> - <Entry DisplayName="Interface indexers" Priority="1000"> - <Entry.Match> - <And> - <Kind Is="Indexer" /> - <ImplementsInterface /> - </And> - </Entry.Match> - <Entry.SortBy> - <ImplementsInterface Immediate="True" /> - <Name /> - </Entry.SortBy> - </Entry> - <Entry DisplayName="Other indexers" Priority="1000"> - <Entry.Match> - <Kind Is="Indexer" /> - </Entry.Match> - <Entry.SortBy> - <Access Order="Public Internal ProtectedInternal Protected Private" /> - <Static /> - <Name /> - </Entry.SortBy> - </Entry> - <Entry DisplayName="Public methods and operators"> - <Entry.Match> - <And> - <Or> - <Kind Is="Method" /> - <Kind Is="Operator" /> - </Or> - <Access Is="Public" /> - </And> - </Entry.Match> - <Entry.SortBy> - <Static /> - <Name /> - </Entry.SortBy> - </Entry> - <Entry DisplayName="Interface methods"> - <Entry.Match> - <And> - <Kind Is="Method" /> - <ImplementsInterface /> - </And> - </Entry.Match> - <Entry.SortBy> - <ImplementsInterface Immediate="True" /> - <Name /> - </Entry.SortBy> - </Entry> - <Entry DisplayName="Other methods"> - <Entry.Match> - <Kind Is="Method" /> - </Entry.Match> - <Entry.SortBy> - <Access Order="Public Internal ProtectedInternal Protected Private" /> - <Static /> - <Name /> - </Entry.SortBy> - </Entry> - <Entry DisplayName="Operators"> - <Entry.Match> - <Kind Is="Operator" /> - </Entry.Match> - <Entry.SortBy> - <Access Order="Public Internal ProtectedInternal Protected Private" /> - <Static /> - <Name /> - </Entry.SortBy> - </Entry> - <Entry DisplayName="Nested structs" Priority="600"> - <Entry.Match> - <Kind Is="Struct" /> - </Entry.Match> - <Entry.SortBy> - <Static /> - <Access Order="Public Internal ProtectedInternal Protected Private" /> - <Name /> - </Entry.SortBy> - </Entry> - <Entry DisplayName="Nested classes" Priority="700"> - <Entry.Match> - <Kind Is="Class" /> - </Entry.Match> - <Entry.SortBy> - <Static /> - <Access Order="Public Internal ProtectedInternal Protected Private" /> - <Name /> - </Entry.SortBy> - </Entry> - <Entry DisplayName="All other members" /> - </TypePattern> -</Patterns> - System - System.Linq - MB - <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /> - <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> - <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> - True - True \ No newline at end of file diff --git a/src/apps/Rsdo.Concordancer.Api/Controllers/ServiceApiInfo.cs b/src/apps/Rsdo.Concordancer.Api/Controllers/ServiceApiInfo.cs index c7db546..2ce7293 100644 --- a/src/apps/Rsdo.Concordancer.Api/Controllers/ServiceApiInfo.cs +++ b/src/apps/Rsdo.Concordancer.Api/Controllers/ServiceApiInfo.cs @@ -1,5 +1,5 @@ using System; -using Microsoft.OpenApi.Models; +using Microsoft.OpenApi; namespace Rsdo.Concordancer.Api.Controllers; diff --git a/src/apps/Rsdo.Concordancer.Api/Dockerfile b/src/apps/Rsdo.Concordancer.Api/Dockerfile index ede4bf3..118f711 100644 --- a/src/apps/Rsdo.Concordancer.Api/Dockerfile +++ b/src/apps/Rsdo.Concordancer.Api/Dockerfile @@ -1,8 +1,12 @@ -FROM mcr.microsoft.com/dotnet/aspnet:6.0 AS base +FROM mcr.microsoft.com/dotnet/aspnet:10.0 AS base WORKDIR /app EXPOSE 80 -FROM mcr.microsoft.com/dotnet/sdk:6.0 AS build +FROM mcr.microsoft.com/dotnet/sdk:10.0 AS build + +# COPY SHARED MSBUILD CONFIGURATION +COPY src/Directory.Build.props /src/Directory.Build.props +COPY src/Directory.Packages.props /src/Directory.Packages.props # INSTALL NODE.JS RUN curl -fsSL https://deb.nodesource.com/setup_16.x | bash - \ @@ -10,13 +14,6 @@ RUN curl -fsSL https://deb.nodesource.com/setup_16.x | bash - \ nodejs \ && rm -rf /var/lib/apt/lists/* -# COPY LOCAL NUGET PACKAGES -WORKDIR /nuget -COPY local-packages/. . - -# ADD LOCAL NUGET SOURCE -RUN dotnet nuget add source /nuget - # RESTORE PACKAGES (with project files only) WORKDIR /src/apps COPY src/apps/Rsdo.Concordancer.Api/Rsdo.Concordancer.Api.csproj Rsdo.Concordancer.Api/Rsdo.Concordancer.Api.csproj @@ -57,4 +54,4 @@ RUN dotnet publish "Rsdo.Concordancer.Api.csproj" -c Release -o /app/publish FROM base AS final WORKDIR /app COPY --from=publish /app/publish . -ENTRYPOINT ["dotnet", "Rsdo.Concordancer.Api.dll"] \ No newline at end of file +ENTRYPOINT ["dotnet", "Rsdo.Concordancer.Api.dll"] diff --git a/src/apps/Rsdo.Concordancer.Api/Framework/SerilogExtensions.cs b/src/apps/Rsdo.Concordancer.Api/Framework/SerilogExtensions.cs index a604bff..314b1ae 100644 --- a/src/apps/Rsdo.Concordancer.Api/Framework/SerilogExtensions.cs +++ b/src/apps/Rsdo.Concordancer.Api/Framework/SerilogExtensions.cs @@ -1,9 +1,9 @@ using System; using System.Net; +using MailKit.Security; using Serilog; using Serilog.Configuration; using Serilog.Events; -using Serilog.Sinks.Email; namespace Rsdo.Concordancer.Api.Framework; @@ -23,25 +23,25 @@ public static class SerilogExtensions int batchPostingLimit = 100, string mailSubject = "Log Email") { - var connectionInfo = new EmailConnectionInfo() - { - FromEmail = fromEmail, - ToEmail = toEmail, - EmailSubject = mailSubject, - MailServer = mailServer, - Port = port, - EnableSsl = enableSsl, - }; - + NetworkCredential credentials = null; if (!string.IsNullOrEmpty(userName) && !string.IsNullOrEmpty(password)) { - connectionInfo.NetworkCredentials = new NetworkCredential() + credentials = new NetworkCredential() { UserName = userName, Password = password, }; } - return sinkConfiguration.Email(connectionInfo: connectionInfo, restrictedToMinimumLevel: restrictedToMinimumLevel); + return sinkConfiguration.Email( + from: fromEmail, + to: toEmail, + host: mailServer, + port: port, + connectionSecurity: enableSsl ? SecureSocketOptions.Auto : SecureSocketOptions.None, + credentials: credentials, + subject: mailSubject, + body: outputTemplate, + restrictedToMinimumLevel: restrictedToMinimumLevel); } } \ No newline at end of file diff --git a/src/apps/Rsdo.Concordancer.Api/Program.cs b/src/apps/Rsdo.Concordancer.Api/Program.cs index 507919a..54578a4 100644 --- a/src/apps/Rsdo.Concordancer.Api/Program.cs +++ b/src/apps/Rsdo.Concordancer.Api/Program.cs @@ -1,52 +1,146 @@ using System; +using System.Collections.Generic; using System.IO; +using System.Reflection; +using System.Text.Json.Serialization; +using Autofac; using Autofac.Extensions.DependencyInjection; -using Microsoft.AspNetCore.Hosting; -using Microsoft.Extensions.Configuration; +using Hangfire; +using Hangfire.PostgreSql; +using Microsoft.AspNetCore.Builder; +using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; +using Microsoft.OpenApi; +using Rsdo.Concordancer.Api.Controllers; +using Rsdo.Concordancer.Api.Framework; +using Rsdo.Concordancer.Core.Constants; +using Rsdo.Concordancer.Data.CompositionRoot; +using Rsdo.Concordancer.Infrastructure.CompositionRoot; +using Rsdo.Concordancer.ServiceModel.Shared; +using Rsdo.Concordancer.Services.CompositionRoot; +using Rsdo.Concordancer.Services.Framework.Cache; using Serilog; -namespace Rsdo.Concordancer.Api; - -public class Program -{ - public static int Main(string[] args) +var builder = WebApplication.CreateBuilder( + new WebApplicationOptions() { - // Read configuration file - var configuration = new ConfigurationBuilder().SetBasePath(Directory.GetCurrentDirectory()) - .AddJsonFile("appsettings.json") - .AddJsonFile($"appsettings.{Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT") ?? "Production"}.json", true) - .Build(); + Args = args, + WebRootPath = "./WebApp/build", + }); - // Create logger - Log.Logger = new LoggerConfiguration().ReadFrom.Configuration(configuration).CreateLogger(); +builder.Host.UseSerilog( + (context, _, loggerConfiguration) => + { + loggerConfiguration.ReadFrom.Configuration(context.Configuration); + }); - // Run application - try - { - Log.Information("Starting web host"); - CreateHostBuilder(args).Build().Run(); - return 0; - } - catch (Exception e) - { - Log.Fatal(e, "Host terminated unexpectedly"); - return 1; - } - finally - { - Log.CloseAndFlush(); - } - } - - public static IHostBuilder CreateHostBuilder(string[] args) => - Host.CreateDefaultBuilder(args) - .UseSerilog() - .UseServiceProviderFactory(new AutofacServiceProviderFactory()) - .ConfigureWebHostDefaults( - webBuilder => +builder.Host.UseServiceProviderFactory(new AutofacServiceProviderFactory()); +builder.Host.ConfigureContainer( + containerBuilder => + { + containerBuilder.RegisterModule(new ServicesModule()); + containerBuilder.RegisterModule(new InfrastructureModule()); + containerBuilder.RegisterModule(new DataModule()); + containerBuilder.RegisterBuildCallback( + (c) => + { + var warmUps = c.Resolve>(); + foreach (var warmUp in warmUps) { - webBuilder.UseStartup(); - webBuilder.UseWebRoot("./WebApp/build"); - }); -} \ No newline at end of file + warmUp.WarmUp(); + } + }); + }); + +builder.Services.AddCors( + opt => + { + opt.AddPolicy( + "CorsPolicy", + policy => + { + policy.AllowAnyOrigin().AllowAnyMethod().AllowAnyHeader(); + }); + }); + +builder.Services.AddControllers() + .AddJsonOptions( + opts => + { + // Bind strings to enums + opts.JsonSerializerOptions.Converters.Add(new JsonStringEnumConverter()); + }); + +builder.Services.AddSwaggerGen( + c => + { + c.SwaggerDoc( + ServiceApiInfo.ApiGroupConcordancer, + info: new OpenApiInfo() + { + Title = $"{ServiceApiInfo.ServiceName} Concordancer API", + Version = $"v{ServiceApiInfo.ServiceVersion.ToString(3)}", + }); + c.SwaggerDoc( + ServiceApiInfo.ApiGroupDashboard, + info: new OpenApiInfo() + { + Title = $"{ServiceApiInfo.ServiceName} Dashboard API", + Version = $"v{ServiceApiInfo.ServiceVersion.ToString(3)}", + }); + + // enable attribute annotations + c.EnableAnnotations(); + + // include code documentation to the swagger doc + foreach (var assembly in new[] { Assembly.GetExecutingAssembly(), typeof(ExecutionResult).Assembly }) + { + var xmlFile = $"{assembly.GetName().Name}.xml"; + var xmlPath = Path.Combine(AppContext.BaseDirectory, xmlFile); + c.IncludeXmlComments(xmlPath); + } + }); + +builder.Services.AddHangfire( + x => + { + // It would be better to use ConnectionStringProvider, but in this case + // we would have to build the container which would (at this point) double singletons. + // So we are duplicated code from ConnectionStringProvider + var connectionString = builder.Configuration[ConfigurationKey.Database.MasterConnectionString]; + x.UsePostgreSqlStorage( + options => + { + options.UseNpgsqlConnection(connectionString); + }); + x.UseMediator(); + }); + +builder.Services.AddHangfireServer(); +builder.Services.AddHttpClient(); + +var app = builder.Build(); + +if (app.Environment.IsDevelopment()) +{ + app.UseDeveloperExceptionPage(); +} + +app.UseSwagger(); +app.UseSwaggerUI( + c => + { + c.SwaggerEndpoint($"{ServiceApiInfo.ApiGroupConcordancer}/swagger.json", $"{ServiceApiInfo.ServiceName} Concordancer API"); + c.SwaggerEndpoint($"{ServiceApiInfo.ApiGroupDashboard}/swagger.json", $"{ServiceApiInfo.ServiceName} Dashboard API"); + }); + +app.UseStaticFiles(); +app.UseRouting(); +app.UseCors("CorsPolicy"); +app.UseAuthorization(); + +app.MapControllers(); +app.MapHangfireDashboard("/hangfire"); +app.MapFallbackToFile("index.html"); + +app.Run(); \ No newline at end of file diff --git a/src/apps/Rsdo.Concordancer.Api/Properties/launchSettings.json b/src/apps/Rsdo.Concordancer.Api/Properties/launchSettings.json index 1312489..ba901d4 100644 --- a/src/apps/Rsdo.Concordancer.Api/Properties/launchSettings.json +++ b/src/apps/Rsdo.Concordancer.Api/Properties/launchSettings.json @@ -1,31 +1,25 @@ { - "$schema": "http://json.schemastore.org/launchsettings.json", - "iisSettings": { - "windowsAuthentication": false, - "anonymousAuthentication": true, - "iisExpress": { - "applicationUrl": "http://localhost:9184", - "sslPort": 44312 + "$schema": "https://json.schemastore.org/launchsettings.json", + "profiles": { + "http": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": true, + "launchUrl": "swagger/index.html", + "applicationUrl": "http://localhost:5000", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "https": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": true, + "launchUrl": "swagger/index.html", + "applicationUrl": "https://localhost:5001;http://localhost:5000", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + } } - }, - "profiles": { - "IIS Express": { - "commandName": "IISExpress", - "launchBrowser": true, - "launchUrl": "swagger", - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - } - }, - "Rsdo.Concordancer.Api": { - "commandName": "Project", - "dotnetRunMessages": "true", - "launchBrowser": true, - "launchUrl": "swagger", - "applicationUrl": "https://localhost:5001;http://localhost:5000", - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - } - } - } -} +} \ No newline at end of file diff --git a/src/apps/Rsdo.Concordancer.Api/Rsdo.Concordancer.Api.csproj b/src/apps/Rsdo.Concordancer.Api/Rsdo.Concordancer.Api.csproj index 53e5433..8824262 100644 --- a/src/apps/Rsdo.Concordancer.Api/Rsdo.Concordancer.Api.csproj +++ b/src/apps/Rsdo.Concordancer.Api/Rsdo.Concordancer.Api.csproj @@ -1,27 +1,19 @@ - - net6.0 - true - false - 1.0.4 - - - - - - - - - - - - - - - - + + + + + + + + + + + + + diff --git a/src/apps/Rsdo.Concordancer.Api/Startup.cs b/src/apps/Rsdo.Concordancer.Api/Startup.cs deleted file mode 100644 index 40f9f0a..0000000 --- a/src/apps/Rsdo.Concordancer.Api/Startup.cs +++ /dev/null @@ -1,150 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Reflection; -using System.Text.Json.Serialization; -using Autofac; -using Hangfire; -using Hangfire.PostgreSql; -using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.Hosting; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Hosting; -using Microsoft.OpenApi.Models; -using Rsdo.Concordancer.Api.Controllers; -using Rsdo.Concordancer.Api.Framework; -using Rsdo.Concordancer.Core.Constants; -using Rsdo.Concordancer.Data.CompositionRoot; -using Rsdo.Concordancer.Infrastructure.CompositionRoot; -using Rsdo.Concordancer.ServiceModel.Shared; -using Rsdo.Concordancer.Services.CompositionRoot; -using Rsdo.Concordancer.Services.Framework.Cache; -using Swashbuckle.AspNetCore.SwaggerGen; - -namespace Rsdo.Concordancer.Api; - -public class Startup -{ - public Startup(IConfiguration configuration) - { - Configuration = configuration; - } - - public IConfiguration Configuration { get; } - - // This method gets called by the runtime. Use this method to add services to the container. - public void ConfigureServices(IServiceCollection services) - { - services.AddCors( - opt => - { - opt.AddPolicy( - "CorsPolicy", - policy => - { - policy.AllowAnyOrigin().AllowAnyMethod().AllowAnyHeader(); - }); - }); - services.AddControllers() - .AddJsonOptions( - opts => - { - // Bind strings to enums - opts.JsonSerializerOptions.Converters.Add(new JsonStringEnumConverter()); - }); - services.AddSwaggerGen( - c => - { - c.SwaggerDoc( - ServiceApiInfo.ApiGroupConcordancer, - info: new OpenApiInfo() - { - Title = $"{ServiceApiInfo.ServiceName} Concordancer API", - Version = $"v{ServiceApiInfo.ServiceVersion.ToString(3)}", - }); - c.SwaggerDoc( - ServiceApiInfo.ApiGroupDashboard, - info: new OpenApiInfo() - { - Title = $"{ServiceApiInfo.ServiceName} Dashboard API", - Version = $"v{ServiceApiInfo.ServiceVersion.ToString(3)}", - }); - - // enable attribute annotations - c.EnableAnnotations(); - - // include code documentation to the swagger doc - ConfigureSwaggerApiDoc(c, Assembly.GetExecutingAssembly(), typeof(ExecutionResult).Assembly); - }); - services.AddHangfire( - x => - { - // It would be better to use ConnectionStringProvider, but in this case - // we would have to build the container which would (at this point) double singletons. - // So we are duplicated code from ConnectionStringProvider - var connectionString = Configuration[ConfigurationKey.Database.MasterConnectionString]; - x.UsePostgreSqlStorage(connectionString); - x.UseMediator(); - }); - services.AddHangfireServer(); - services.AddHttpClient(); - } - - public void ConfigureSwaggerApiDoc(SwaggerGenOptions options, params Assembly[] assemblies) - { - foreach (var assembly in assemblies) - { - var xmlFile = $"{assembly.GetName().Name}.xml"; - var xmlPath = Path.Combine(AppContext.BaseDirectory, xmlFile); - options.IncludeXmlComments(xmlPath); - } - } - - public void ConfigureContainer(ContainerBuilder builder) - { - builder.RegisterModule(new ServicesModule()); - builder.RegisterModule(new InfrastructureModule()); - builder.RegisterModule(new DataModule()); - builder.RegisterBuildCallback( - (c) => - { - var warmUps = c.Resolve>(); - foreach (var warmUp in warmUps) - { - warmUp.WarmUp(); - } - }); - } - - // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. - public void Configure(IApplicationBuilder app, IWebHostEnvironment env) - { - if (env.IsDevelopment()) - { - app.UseDeveloperExceptionPage(); - } - - app.UseSwagger(); - app.UseSwaggerUI( - c => - { - c.SwaggerEndpoint($"{ServiceApiInfo.ApiGroupConcordancer}/swagger.json", $"{ServiceApiInfo.ServiceName} Concordancer API"); - c.SwaggerEndpoint($"{ServiceApiInfo.ApiGroupDashboard}/swagger.json", $"{ServiceApiInfo.ServiceName} Dashboard API"); - }); - - app.UseStaticFiles(); - app.UseRouting(); - app.UseCors("CorsPolicy"); - app.UseAuthorization(); - - app.UseEndpoints( - endpoints => - { - endpoints.MapControllers(); - endpoints.MapHangfireDashboard("/hangfire"); - endpoints.MapFallbackToFile("index.html"); - }); - } -} \ No newline at end of file diff --git a/src/apps/Rsdo.Concordancer.Api/WebApp/src/assets/chevron-right.svg b/src/apps/Rsdo.Concordancer.Api/WebApp/src/assets/chevron-right.svg new file mode 100644 index 0000000..424b5d0 --- /dev/null +++ b/src/apps/Rsdo.Concordancer.Api/WebApp/src/assets/chevron-right.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/apps/Rsdo.Concordancer.Api/WebApp/src/assets/square-checkbox-solid.svg b/src/apps/Rsdo.Concordancer.Api/WebApp/src/assets/square-checkbox-solid.svg new file mode 100644 index 0000000..4502e84 --- /dev/null +++ b/src/apps/Rsdo.Concordancer.Api/WebApp/src/assets/square-checkbox-solid.svg @@ -0,0 +1,4 @@ + + + + diff --git a/src/apps/Rsdo.Concordancer.Api/WebApp/src/components/concordance/ConcordanceAggregationItem.js b/src/apps/Rsdo.Concordancer.Api/WebApp/src/components/concordance/ConcordanceAggregationItem.js index e0fc648..0bf5ece 100644 --- a/src/apps/Rsdo.Concordancer.Api/WebApp/src/components/concordance/ConcordanceAggregationItem.js +++ b/src/apps/Rsdo.Concordancer.Api/WebApp/src/components/concordance/ConcordanceAggregationItem.js @@ -1,15 +1,27 @@ import { Link } from 'react-router-dom'; import useSearch from '../../hooks/use-search'; import styles from './ConcordanceAggregationItem.module.scss'; +import { useTranslation } from 'react-i18next'; + +import selectIcon from '../../assets/chevron-right.svg'; +import selectedIcon from '../../assets/square-checkbox-solid.svg'; const ConcordanceAggregationItem = (props) => { + const { t } = useTranslation(); const search = useSearch(); const isSelected = search.isFiltered('TextIds', props.filterKey) === true; const link = isSelected ? search.getClearFilterLink('TextIds', props.filterKey) : search.getFilterLink('TextIds', props.filterKey); + let icon; + if (isSelected) { + icon = {t('shared.unselect')}; + } else { + icon = {t('shared.select')}; + } + return ( -
  • {isSelected && X}{props.title}{props.count}
  • +
  • {icon}{props.title}{props.count}
  • ); }; -export default ConcordanceAggregationItem; \ No newline at end of file +export default ConcordanceAggregationItem; diff --git a/src/apps/Rsdo.Concordancer.Api/WebApp/src/components/concordance/ConcordanceAggregationItem.module.scss b/src/apps/Rsdo.Concordancer.Api/WebApp/src/components/concordance/ConcordanceAggregationItem.module.scss index f61b0ac..3c2caa4 100644 --- a/src/apps/Rsdo.Concordancer.Api/WebApp/src/components/concordance/ConcordanceAggregationItem.module.scss +++ b/src/apps/Rsdo.Concordancer.Api/WebApp/src/components/concordance/ConcordanceAggregationItem.module.scss @@ -9,16 +9,13 @@ color: $text-color; display: flex; text-decoration: none; - - .remove { - margin-right: 0.8rem; - } - + .title { + margin-left: 1.2rem; flex-grow: 1; margin-right: 0.8rem; overflow: hidden; text-overflow: clip; } } -} \ No newline at end of file +} diff --git a/src/apps/Rsdo.Concordancer.Api/WebApp/src/components/index/IndexMenu.js b/src/apps/Rsdo.Concordancer.Api/WebApp/src/components/index/IndexMenu.js index c1d1321..220ebb6 100644 --- a/src/apps/Rsdo.Concordancer.Api/WebApp/src/components/index/IndexMenu.js +++ b/src/apps/Rsdo.Concordancer.Api/WebApp/src/components/index/IndexMenu.js @@ -18,7 +18,7 @@ const IndexTopBar = () => {
    -
    Logo
    +
    Logo
    @@ -26,4 +26,4 @@ const IndexTopBar = () => { ); }; -export default IndexTopBar; \ No newline at end of file +export default IndexTopBar; diff --git a/src/apps/Rsdo.Concordancer.Api/WebApp/src/i18n.js b/src/apps/Rsdo.Concordancer.Api/WebApp/src/i18n.js index 722fd3a..cbc2a2d 100644 --- a/src/apps/Rsdo.Concordancer.Api/WebApp/src/i18n.js +++ b/src/apps/Rsdo.Concordancer.Api/WebApp/src/i18n.js @@ -22,7 +22,9 @@ const resources = { 'searchError': 'Server returned error when searching.', 'searchHelp': 'Help', 'searchHistory': 'Search history', - 'searchRecords': 'Showing {{start}}-{{end}} from {{total}} records.' + 'searchRecords': 'Showing {{start}}-{{end}} from {{total}} records.', + 'select': 'Select', + 'unselect': 'Remove selected' }, concordance: { 'title': 'Search concordances', @@ -63,7 +65,9 @@ const resources = { 'searchError': 'Strežnik je vrnil napako pri iskanju.', 'searchHelp': 'Pomoč', 'searchHistory': 'Zgodovina iskanj', - 'searchRecords': 'Prikazujem {{start}}-{{end}} od {{total}} zapisov.' + 'searchRecords': 'Prikazujem {{start}}-{{end}} od {{total}} zapisov.', + 'select': 'Izberi', + 'unselect': 'Odstrani izbrano' }, concordance: { 'title': 'Iskanje po konkordancah', @@ -96,4 +100,4 @@ i18n } }); -export default i18n; \ No newline at end of file +export default i18n; diff --git a/src/apps/Rsdo.Concordancer.Api/WebApp/src/layout/ResultsLayout.js b/src/apps/Rsdo.Concordancer.Api/WebApp/src/layout/ResultsLayout.js index bfba73b..c828129 100644 --- a/src/apps/Rsdo.Concordancer.Api/WebApp/src/layout/ResultsLayout.js +++ b/src/apps/Rsdo.Concordancer.Api/WebApp/src/layout/ResultsLayout.js @@ -49,7 +49,7 @@ const ResultsLayout = (props) => {
    - Logo + Logo
    • Concordance
    • List
    • diff --git a/src/apps/Rsdo.Concordancer.Api/WebApp/src/pages/concordance/ConcordanceResults.js b/src/apps/Rsdo.Concordancer.Api/WebApp/src/pages/concordance/ConcordanceResults.js index 33472c6..7eb8480 100644 --- a/src/apps/Rsdo.Concordancer.Api/WebApp/src/pages/concordance/ConcordanceResults.js +++ b/src/apps/Rsdo.Concordancer.Api/WebApp/src/pages/concordance/ConcordanceResults.js @@ -73,7 +73,7 @@ const ConcordanceResults = () => { content =

      {t('shared.noResults')}

      } return ( - + }>
      {content} diff --git a/src/apps/Rsdo.Concordancer.Api/WebApp/src/pages/list/ListResults.js b/src/apps/Rsdo.Concordancer.Api/WebApp/src/pages/list/ListResults.js index af5983d..bc04a96 100644 --- a/src/apps/Rsdo.Concordancer.Api/WebApp/src/pages/list/ListResults.js +++ b/src/apps/Rsdo.Concordancer.Api/WebApp/src/pages/list/ListResults.js @@ -11,6 +11,7 @@ import ResultsHeader from '../../components/results/ResultsHeader'; import ResultsPager from '../../components/results/ResultsPager'; import ResultsExport from '../../components/results/ResultsExport'; import ListItems from '../../components/list/ListItems'; +import ListHelp from '../../components/list/ListHelp'; import Spinner from '../../components/shared/Spinner'; const ListResults = () => { @@ -48,7 +49,7 @@ const ListResults = () => { if (!isLoading && isSuccess && data.items.length > 0) { return ( - + }>
      setIsVisible(prevIsVisible => !prevIsVisible)} /> @@ -69,7 +70,7 @@ const ListResults = () => { content =

      {t('shared.noResults')}

      } return ( - + }>
      {content} diff --git a/src/apps/Rsdo.Concordancer.SystemManager/Dockerfile b/src/apps/Rsdo.Concordancer.SystemManager/Dockerfile index cfb042d..33bafc3 100644 --- a/src/apps/Rsdo.Concordancer.SystemManager/Dockerfile +++ b/src/apps/Rsdo.Concordancer.SystemManager/Dockerfile @@ -1,14 +1,11 @@ -FROM mcr.microsoft.com/dotnet/sdk:6.0 AS base +FROM mcr.microsoft.com/dotnet/sdk:10.0 AS base WORKDIR /app -FROM mcr.microsoft.com/dotnet/sdk:6.0 AS build +FROM mcr.microsoft.com/dotnet/sdk:10.0 AS build -# COPY LOCAL NUGET PACKAGES -WORKDIR /nuget -COPY local-packages/. . - -# ADD LOCAL NUGET SOURCE -RUN dotnet nuget add source /nuget +# COPY SHARED MSBUILD CONFIGURATION +COPY src/Directory.Build.props /src/Directory.Build.props +COPY src/Directory.Packages.props /src/Directory.Packages.props # RESTORE PACKAGES (with project files only) WORKDIR /src/apps @@ -44,4 +41,4 @@ RUN dotnet publish "Rsdo.Concordancer.SystemManager.csproj" -c Release -o /app/p FROM base AS final WORKDIR /app COPY --from=publish /app/publish . -CMD tail -f /dev/null \ No newline at end of file +CMD tail -f /dev/null diff --git a/src/apps/Rsdo.Concordancer.SystemManager/Rsdo.Concordancer.SystemManager.csproj b/src/apps/Rsdo.Concordancer.SystemManager/Rsdo.Concordancer.SystemManager.csproj index 4e225c1..c41de05 100644 --- a/src/apps/Rsdo.Concordancer.SystemManager/Rsdo.Concordancer.SystemManager.csproj +++ b/src/apps/Rsdo.Concordancer.SystemManager/Rsdo.Concordancer.SystemManager.csproj @@ -2,16 +2,11 @@ Exe - net6.0 - true - false - 1.0.4 - - - + + diff --git a/src/core/Rsdo.Concordancer.Core/Extensions/StringExtensions.cs b/src/core/Rsdo.Concordancer.Core/Extensions/StringExtensions.cs new file mode 100644 index 0000000..62b92f9 --- /dev/null +++ b/src/core/Rsdo.Concordancer.Core/Extensions/StringExtensions.cs @@ -0,0 +1,9 @@ +namespace Rsdo.Concordancer.Core.Extensions; + +public static class StringExtensions +{ + public static bool IsWildcardSearch(this string value) + { + return !string.IsNullOrEmpty(value) && (value.Contains('*') || value.Contains('?')); + } +} \ No newline at end of file diff --git a/src/core/Rsdo.Concordancer.Core/Rsdo.Concordancer.Core.csproj b/src/core/Rsdo.Concordancer.Core/Rsdo.Concordancer.Core.csproj index c48fb2e..df332b2 100644 --- a/src/core/Rsdo.Concordancer.Core/Rsdo.Concordancer.Core.csproj +++ b/src/core/Rsdo.Concordancer.Core/Rsdo.Concordancer.Core.csproj @@ -1,16 +1,5 @@ - - net6.0 - true - false - 1.0.4 - - - - - - diff --git a/src/core/Rsdo.Concordancer.Data/Framework/VersionInfoTableMetadata.cs b/src/core/Rsdo.Concordancer.Data/Framework/VersionInfoTableMetadata.cs index 5c4d28f..1b225fd 100644 --- a/src/core/Rsdo.Concordancer.Data/Framework/VersionInfoTableMetadata.cs +++ b/src/core/Rsdo.Concordancer.Data/Framework/VersionInfoTableMetadata.cs @@ -14,6 +14,8 @@ public class VersionInfoTableMetadata : IVersionTableMetaData public string UniqueIndexName => "uc_version"; + public bool CreateWithPrimaryKey => false; + public object ApplicationContext { get; set; } public bool OwnsSchema => true; diff --git a/src/core/Rsdo.Concordancer.Data/Rsdo.Concordancer.Data.csproj b/src/core/Rsdo.Concordancer.Data/Rsdo.Concordancer.Data.csproj index 0e640da..0bd9599 100644 --- a/src/core/Rsdo.Concordancer.Data/Rsdo.Concordancer.Data.csproj +++ b/src/core/Rsdo.Concordancer.Data/Rsdo.Concordancer.Data.csproj @@ -1,19 +1,11 @@ - - net6.0 - true - false - 1.0.4 - - - - - - - - + + + + + diff --git a/src/core/Rsdo.Concordancer.Infrastructure/Extensions/ElasticQueryExtensions.cs b/src/core/Rsdo.Concordancer.Infrastructure/Extensions/ElasticQueryExtensions.cs index 674afd1..239a183 100644 --- a/src/core/Rsdo.Concordancer.Infrastructure/Extensions/ElasticQueryExtensions.cs +++ b/src/core/Rsdo.Concordancer.Infrastructure/Extensions/ElasticQueryExtensions.cs @@ -1,5 +1,7 @@ using System.Collections.Generic; +using System.Linq; using OpenSearch.Client; +using Rsdo.Concordancer.Core.Extensions; namespace Rsdo.Concordancer.Infrastructure.Extensions; @@ -30,4 +32,36 @@ public static class ElasticQueryExtensions }, }; } + + public static QueryContainer ToQuery(this string value, string field) + { + if (value.IsWildcardSearch()) + { + return new WildcardQuery() + { + Field = field, + Value = value, + }; + } + + return new TermQuery() + { + Field = field, + Value = value, + }; + } + + public static QueryContainer ToQuery(this List values, string field) + { + if (values.Any(v => v.IsWildcardSearch())) + { + return values.Select(value => value.ToQuery(field)).ToList().ToBooleanOrQuery(); + } + + return new TermsQuery() + { + Field = field, + Terms = values, + }; + } } \ No newline at end of file diff --git a/src/core/Rsdo.Concordancer.Infrastructure/Rsdo.Concordancer.Infrastructure.csproj b/src/core/Rsdo.Concordancer.Infrastructure/Rsdo.Concordancer.Infrastructure.csproj index eaf8368..461cdc7 100644 --- a/src/core/Rsdo.Concordancer.Infrastructure/Rsdo.Concordancer.Infrastructure.csproj +++ b/src/core/Rsdo.Concordancer.Infrastructure/Rsdo.Concordancer.Infrastructure.csproj @@ -1,18 +1,10 @@ - - net6.0 - true - false - 1.0.4 - - - - - - - + + + + diff --git a/src/core/Rsdo.Concordancer.Infrastructure/Search/QueryBuilders/TermListQueryBuilder.cs b/src/core/Rsdo.Concordancer.Infrastructure/Search/QueryBuilders/TermListQueryBuilder.cs index 806cbda..36886e9 100644 --- a/src/core/Rsdo.Concordancer.Infrastructure/Search/QueryBuilders/TermListQueryBuilder.cs +++ b/src/core/Rsdo.Concordancer.Infrastructure/Search/QueryBuilders/TermListQueryBuilder.cs @@ -35,22 +35,12 @@ public class TermListQueryBuilder : IQueryBuilder if (!string.IsNullOrEmpty(wordQuery.Form)) { - queries.Add( - new TermQuery() - { - Field = $"{tokenField}.formLower", - Value = wordQuery.Form.ToLower(), - }); + queries.Add(wordQuery.Form.ToLower().ToQuery($"{tokenField}.formLower")); } if (!wordQuery.Lemmas.IsNullOrEmpty()) { - queries.Add( - new TermsQuery() - { - Field = $"{tokenField}.lemma", - Terms = wordQuery.Lemmas, - }); + queries.Add(wordQuery.Lemmas.ToQuery($"{tokenField}.lemma")); } return queries.Count == 0 ? new MatchNoneQuery() : queries.ToBooleanAndQuery(); diff --git a/src/core/Rsdo.Concordancer.ServiceModel/Rsdo.Concordancer.ServiceModel.csproj b/src/core/Rsdo.Concordancer.ServiceModel/Rsdo.Concordancer.ServiceModel.csproj index af24a0a..5e10e1c 100644 --- a/src/core/Rsdo.Concordancer.ServiceModel/Rsdo.Concordancer.ServiceModel.csproj +++ b/src/core/Rsdo.Concordancer.ServiceModel/Rsdo.Concordancer.ServiceModel.csproj @@ -1,15 +1,7 @@ - - net6.0 - true - false - 1.0.4 - - - - + diff --git a/src/core/Rsdo.Concordancer.Services/CompositionRoot/ServicesModule.cs b/src/core/Rsdo.Concordancer.Services/CompositionRoot/ServicesModule.cs index c371d2f..66b0061 100644 --- a/src/core/Rsdo.Concordancer.Services/CompositionRoot/ServicesModule.cs +++ b/src/core/Rsdo.Concordancer.Services/CompositionRoot/ServicesModule.cs @@ -1,10 +1,14 @@ using System; using System.Reflection; using Autofac; +using Autofac.Core; using FluentValidation; using Microsoft.Extensions.Caching.Memory; using Rsdo.Concordancer.Core.Interfaces; +using Rsdo.Concordancer.Core.Search.Queries.Concordances; +using Rsdo.Concordancer.Core.Search.Queries.TermLists; using Rsdo.Concordancer.ServiceModel.Requests.Concordances; +using Rsdo.Concordancer.ServiceModel.Requests.TermLists; using Rsdo.Concordancer.ServiceModel.Types; using Rsdo.Concordancer.Services.Framework; using Rsdo.Concordancer.Services.Framework.BulkLoaders; @@ -15,6 +19,8 @@ using Rsdo.Concordancer.Services.Framework.Decorators; using Rsdo.Concordancer.Services.Search.Aggregations; using Rsdo.Concordancer.Services.Search.AlternateSearches; using Rsdo.Concordancer.Services.Search.QueryFactories; +using Rsdo.Concordancer.Services.Search.QueryFactories.Concordances; +using Rsdo.Concordancer.Services.Search.QueryFactories.TermLists; using Rsdo.Concordancer.Services.Services.InputQueryParser; using Rsdo.Concordancer.Services.Services.LemmatizationService; using Rsdo.Concordancer.Services.Services.ParagraphService; @@ -83,7 +89,8 @@ public class ServicesModule : Module builder.RegisterType().Keyed(TokenizerType.Default).SingleInstance(); builder.RegisterType().As().SingleInstance(); builder.RegisterType().As().InstancePerLifetimeScope(); - builder.RegisterType().As().InstancePerLifetimeScope(); + builder.RegisterType().Keyed(LemmatizationType.Simple).InstancePerLifetimeScope(); + builder.RegisterType().Keyed(LemmatizationType.Wildcard).InstancePerLifetimeScope(); builder.RegisterType().As().InstancePerLifetimeScope(); } @@ -103,7 +110,33 @@ public class ServicesModule : Module private void RegisterSearch(ContainerBuilder builder) { // Query factories - builder.RegisterAssemblyTypes(ServicesAssembly).AsClosedTypesOf(typeof(IQueryFactory<,>)).AsImplementedInterfaces().InstancePerLifetimeScope(); + var wildcardLemmatizationServiceParameter = new ResolvedParameter( + (p, ctx) => p.ParameterType == typeof(ILemmatizationService), + (p, ctx) => ctx.ResolveKeyed(LemmatizationType.Wildcard)); + var simpleLemmatizationServiceParameter = new ResolvedParameter( + (p, ctx) => p.ParameterType == typeof(ILemmatizationService), + (p, ctx) => ctx.ResolveKeyed(LemmatizationType.Simple)); + + builder.RegisterType() + .As>() + .WithParameter(simpleLemmatizationServiceParameter) + .InstancePerLifetimeScope(); + builder.RegisterType() + .As>() + .WithParameter(simpleLemmatizationServiceParameter) + .InstancePerLifetimeScope(); + builder.RegisterType() + .As>() + .WithParameter(simpleLemmatizationServiceParameter) + .InstancePerLifetimeScope(); + builder.RegisterType() + .As>() + .WithParameter(wildcardLemmatizationServiceParameter) + .InstancePerLifetimeScope(); + builder.RegisterType() + .As>() + .WithParameter(wildcardLemmatizationServiceParameter) + .InstancePerLifetimeScope(); // Aggregations builder.RegisterType().As().InstancePerLifetimeScope(); @@ -112,6 +145,7 @@ public class ServicesModule : Module // Alternate searches builder.RegisterType() .As>() + .WithParameter(simpleLemmatizationServiceParameter) .InstancePerLifetimeScope(); } } \ No newline at end of file diff --git a/src/core/Rsdo.Concordancer.Services/Rsdo.Concordancer.Services.csproj b/src/core/Rsdo.Concordancer.Services/Rsdo.Concordancer.Services.csproj index 87194bb..024eb0d 100644 --- a/src/core/Rsdo.Concordancer.Services/Rsdo.Concordancer.Services.csproj +++ b/src/core/Rsdo.Concordancer.Services/Rsdo.Concordancer.Services.csproj @@ -1,25 +1,17 @@ - - net6.0 - true - false - 1.0.4 - - - - - - - - - - - - - - + + + + + + + + + + + diff --git a/src/core/Rsdo.Concordancer.Services/Services/InputQueryParser/InputQueryParser.cs b/src/core/Rsdo.Concordancer.Services/Services/InputQueryParser/InputQueryParser.cs index 213ea9a..7c78f0c 100644 --- a/src/core/Rsdo.Concordancer.Services/Services/InputQueryParser/InputQueryParser.cs +++ b/src/core/Rsdo.Concordancer.Services/Services/InputQueryParser/InputQueryParser.cs @@ -4,6 +4,7 @@ using System.Linq; using System.Text; using System.Threading.Tasks; using Autofac.Features.Indexed; +using Rsdo.Concordancer.Core.Extensions; using Rsdo.Concordancer.ServiceModel.Requests.Concordances; using Rsdo.Concordancer.ServiceModel.Types; using Rsdo.Concordancer.Services.Services.TokenizerService; @@ -21,6 +22,12 @@ public class InputQueryParser : IInputQueryParser public async Task> Tokenize(string query) { + if (query.IsWildcardSearch()) + { + // Classla tokenizer doesn't support wildcards, so use default tokenizer + return await tokenizers[TokenizerType.Default].Tokenize(query); + } + try { return await tokenizers[TokenizerType.Classla].Tokenize(query); diff --git a/src/core/Rsdo.Concordancer.Services/Services/LemmatizationService/LemmatizationType.cs b/src/core/Rsdo.Concordancer.Services/Services/LemmatizationService/LemmatizationType.cs new file mode 100644 index 0000000..82e0a0b --- /dev/null +++ b/src/core/Rsdo.Concordancer.Services/Services/LemmatizationService/LemmatizationType.cs @@ -0,0 +1,7 @@ +namespace Rsdo.Concordancer.Services.Services.LemmatizationService; + +public enum LemmatizationType +{ + Simple, + Wildcard, +} \ No newline at end of file diff --git a/src/core/Rsdo.Concordancer.Services/Services/LemmatizationService/LemmatizationService.cs b/src/core/Rsdo.Concordancer.Services/Services/LemmatizationService/SimpleLemmatizationService.cs similarity index 71% rename from src/core/Rsdo.Concordancer.Services/Services/LemmatizationService/LemmatizationService.cs rename to src/core/Rsdo.Concordancer.Services/Services/LemmatizationService/SimpleLemmatizationService.cs index db25879..40cf37a 100644 --- a/src/core/Rsdo.Concordancer.Services/Services/LemmatizationService/LemmatizationService.cs +++ b/src/core/Rsdo.Concordancer.Services/Services/LemmatizationService/SimpleLemmatizationService.cs @@ -7,17 +7,18 @@ using Rsdo.Concordancer.Services.Framework.DbContext; namespace Rsdo.Concordancer.Services.Services.LemmatizationService; -public class LemmatizationService : ILemmatizationService +public class SimpleLemmatizationService : ILemmatizationService { private readonly MasterDbContext dbContext; - public LemmatizationService(MasterDbContext dbContext) + public SimpleLemmatizationService(MasterDbContext dbContext) { this.dbContext = dbContext; } - public async Task> GetLemmas(string form) + public virtual async Task> GetLemmas(string form) { + // Retrieving lemmas without support for wildcard search var lemmas = await dbContext.LemmaFormPair.Where(f => f.Form.ToLower() == form.ToLower()).Select(f => f.Lemma).Distinct().ToListAsync(); if (lemmas.IsNullOrEmpty()) { diff --git a/src/core/Rsdo.Concordancer.Services/Services/LemmatizationService/WildcardLemmatizationService.cs b/src/core/Rsdo.Concordancer.Services/Services/LemmatizationService/WildcardLemmatizationService.cs new file mode 100644 index 0000000..85fa43c --- /dev/null +++ b/src/core/Rsdo.Concordancer.Services/Services/LemmatizationService/WildcardLemmatizationService.cs @@ -0,0 +1,38 @@ +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.EntityFrameworkCore; +using Rsdo.Concordancer.Core.Extensions; +using Rsdo.Concordancer.Services.Framework.DbContext; + +namespace Rsdo.Concordancer.Services.Services.LemmatizationService; + +public class WildcardLemmatizationService : SimpleLemmatizationService +{ + private readonly MasterDbContext dbContext; + + public WildcardLemmatizationService(MasterDbContext dbContext) + : base(dbContext) + { + this.dbContext = dbContext; + } + + public override async Task> GetLemmas(string form) + { + if (!form.IsWildcardSearch()) + { + return await base.GetLemmas(form); + } + + var lemmas = await dbContext.LemmaFormPair.Where(f => EF.Functions.Like(f.Form.ToLower(), form.Replace("*", "%").Replace("?", "_").ToLower())) + .Select(f => f.Lemma) + .Distinct() + .ToListAsync(); + if (lemmas.IsNullOrEmpty()) + { + lemmas.Add(form); + } + + return lemmas; + } +} \ No newline at end of file diff --git a/src/tests/Rsdo.Concordancer.Tests.Integration/Rsdo.Concordancer.Tests.Integration.csproj b/src/tests/Rsdo.Concordancer.Tests.Integration/Rsdo.Concordancer.Tests.Integration.csproj index fc95c63..12e3c27 100644 --- a/src/tests/Rsdo.Concordancer.Tests.Integration/Rsdo.Concordancer.Tests.Integration.csproj +++ b/src/tests/Rsdo.Concordancer.Tests.Integration/Rsdo.Concordancer.Tests.Integration.csproj @@ -1,21 +1,17 @@ - net6.0 false - true - false - - - - + + + runtime; build; native; contentfiles; analyzers; buildtransitive all - + runtime; build; native; contentfiles; analyzers; buildtransitive all diff --git a/src/tests/Rsdo.Concordancer.Tests.Unit/Rsdo.Concordancer.Tests.Unit.csproj b/src/tests/Rsdo.Concordancer.Tests.Unit/Rsdo.Concordancer.Tests.Unit.csproj index fc95c63..12e3c27 100644 --- a/src/tests/Rsdo.Concordancer.Tests.Unit/Rsdo.Concordancer.Tests.Unit.csproj +++ b/src/tests/Rsdo.Concordancer.Tests.Unit/Rsdo.Concordancer.Tests.Unit.csproj @@ -1,21 +1,17 @@ - net6.0 false - true - false - - - - + + + runtime; build; native; contentfiles; analyzers; buildtransitive all - + runtime; build; native; contentfiles; analyzers; buildtransitive all