diff --git a/Taskfile.yml b/Taskfile.yml index 7acb703b9..ad80af9d0 100644 --- a/Taskfile.yml +++ b/Taskfile.yml @@ -37,7 +37,7 @@ tasks: setup-win: platforms: [ windows ] cmds: - - cmd: powershell rm -r {{.HG_REPO_DIR}}/sena-3 + - cmd: powershell "if (test-path {{.HG_REPO_DIR}}/sena-3) { rm -r {{.HG_REPO_DIR}}/sena-3 }" ignore_error: true silent: true - powershell -Command "Invoke-WebRequest 'https://drive.google.com/uc?export=download&id=1I-hwc0RHoQqW774gbS5qR-GHa1E7BlsS' -OutFile sena-3.zip" diff --git a/backend/LfClassicData/LfClassicLexboxApi.cs b/backend/LfClassicData/LfClassicLexboxApi.cs index 1beee95e5..3c87675bd 100644 --- a/backend/LfClassicData/LfClassicLexboxApi.cs +++ b/backend/LfClassicData/LfClassicLexboxApi.cs @@ -30,8 +30,13 @@ public async Task GetWritingSystems() Abbreviation = inputSystem.Abbreviation }; //ws type might not be stored, we will add it anyway, otherwise nothing works - if (inputSystem.AnalysisWS is not false) analysis.Add(writingSystem); - if (inputSystem.VernacularWS is not false) vernacular.Add(writingSystem); + if (inputSystem is { AnalysisWS: null, VernacularWS: null }) + { + analysis.Add(writingSystem); + vernacular.Add(writingSystem); + } + if (inputSystem.AnalysisWS is true) analysis.Add(writingSystem); + if (inputSystem.VernacularWS is true) vernacular.Add(writingSystem); } return new WritingSystems { @@ -59,29 +64,28 @@ public IAsyncEnumerable SearchEntries(string query, QueryOptions? options private async IAsyncEnumerable Query(QueryOptions? options = null) { options ??= QueryOptions.Default; - var ws = await GetWritingSystems(); - if (ws is { Vernacular: [], Analysis: [] }) - { - yield break; - } + var sortWs = options.Order.WritingSystem; if (sortWs == "default") { + var ws = await GetWritingSystems(); + if (ws is { Vernacular: [], Analysis: [] }) + { + yield break; + } sortWs = ws.Vernacular[0].Id; } - await foreach (var entry in Entries.ToAsyncEnumerable() + await foreach (var entry in Entries.AsQueryable() //todo, you can only sort by headword for now - .OrderBy(e => e.CitationForm?.TryGetValue(sortWs, out var val) == true - ? val.Value - : e.Lexeme?.TryGetValue(sortWs, out val) == true - ? val.Value - : string.Empty) - .ThenBy(e => e.MorphologyType) - .ThenBy(e => e.Guid)//todo should sort by homograph number + .Select(entry => new {entry, headword = entry.CitationForm![sortWs].Value ?? entry.Lexeme![sortWs].Value ?? string.Empty}) + .OrderBy(e => e.headword) + .ThenBy(e => e.entry.MorphologyType) + .ThenBy(e => e.entry.Guid) //todo should sort by homograph number .Skip(options.Offset) .Take(options.Count) - .Select(ToEntry)) + .ToAsyncEnumerable() + .Select(e => ToEntry(e.entry))) { yield return entry; } diff --git a/backend/LfClassicData/MongoExtensions.cs b/backend/LfClassicData/MongoExtensions.cs index 96c328e5a..7179ba063 100644 --- a/backend/LfClassicData/MongoExtensions.cs +++ b/backend/LfClassicData/MongoExtensions.cs @@ -15,4 +15,15 @@ public static async IAsyncEnumerable ToAsyncEnumerable(this IMongoCollecti } } } + public static async IAsyncEnumerable ToAsyncEnumerable(this IAsyncCursorSource cursorSource) + { + using var entriesCursor = await cursorSource.ToCursorAsync(); + while (await entriesCursor.MoveNextAsync()) + { + foreach (var entry in entriesCursor.Current) + { + yield return entry; + } + } + } } diff --git a/backend/Testing/ApiTests/NewProjectRaceCondition.cs b/backend/Testing/ApiTests/NewProjectRaceCondition.cs index 8d4298a1e..2eed7b63b 100644 --- a/backend/Testing/ApiTests/NewProjectRaceCondition.cs +++ b/backend/Testing/ApiTests/NewProjectRaceCondition.cs @@ -40,6 +40,7 @@ private async Task CreateQueryAndVerifyProject(Guid id) type: FL_EX, id: "{{id}}", code: "{{id}}", + isConfidential: false, description: "this is just a testing project for testing a race condition", retentionPolicy: DEV }) { diff --git a/backend/Testing/Fixtures/IntegrationFixture.cs b/backend/Testing/Fixtures/IntegrationFixture.cs index e37d478b8..36be546fb 100644 --- a/backend/Testing/Fixtures/IntegrationFixture.cs +++ b/backend/Testing/Fixtures/IntegrationFixture.cs @@ -1,6 +1,4 @@ using System.IO.Compression; -using System.Net; -using System.Reflection; using System.Runtime.CompilerServices; using LexCore.Utils; using Shouldly; @@ -13,11 +11,15 @@ namespace Testing.Fixtures; public class IntegrationFixture : IAsyncLifetime { - private static readonly string _templateRepoName = "test-template-repo.zip"; - public FileInfo TemplateRepoZip { get; } = new(_templateRepoName); - public DirectoryInfo TemplateRepo { get; } = new(Path.Join(BasePath, "_template-repo_")); + private const string TemplateRepoZipName = "test-template-repo.zip"; + public static readonly FileInfo TemplateRepoZip = new(TemplateRepoZipName); + public static readonly DirectoryInfo TemplateRepo = new(Path.Join(BasePath, "_template-repo_")); public ApiTestBase AdminApiTester { get; private set; } = new(); - private string AdminJwt = string.Empty; + + static IntegrationFixture() + { + DeletePreviousTestFiles(); + } public async Task InitializeAsync(ApiTestBase apiTester) { @@ -27,10 +29,9 @@ public async Task InitializeAsync(ApiTestBase apiTester) public async Task InitializeAsync() { - DeletePreviousTestFiles(); Directory.CreateDirectory(BasePath); InitTemplateRepo(); - AdminJwt = await AdminApiTester.LoginAs(AdminAuth.Username, AdminAuth.Password); + await AdminApiTester.LoginAs(AdminAuth.Username, AdminAuth.Password); } public Task DisposeAsync() @@ -43,11 +44,15 @@ private static void DeletePreviousTestFiles() if (Directory.Exists(BasePath)) Directory.Delete(BasePath, true); } - private void InitTemplateRepo() + private static void InitTemplateRepo() { - if (TemplateRepo.Exists) return; - using var stream = TemplateRepoZip.OpenRead(); - ZipFile.ExtractToDirectory(stream, TemplateRepo.FullName); + lock (TemplateRepo) + { + if (TemplateRepo.Exists) return; + using var stream = TemplateRepoZip.OpenRead(); + ZipFile.ExtractToDirectory(stream, TemplateRepo.FullName); + TemplateRepo.Refresh(); + } } public ProjectConfig InitLocalFlexProjectWithRepo(HgProtocol? protocol = null, [CallerMemberName] string projectName = "") diff --git a/backend/Testing/Fixtures/IntegrationFixtureTests.cs b/backend/Testing/Fixtures/IntegrationFixtureTests.cs index 55c3b835a..18697e956 100644 --- a/backend/Testing/Fixtures/IntegrationFixtureTests.cs +++ b/backend/Testing/Fixtures/IntegrationFixtureTests.cs @@ -1,7 +1,6 @@ using Moq; using Shouldly; using Testing.ApiTests; -using Testing.Services; namespace Testing.Fixtures; @@ -10,12 +9,14 @@ namespace Testing.Fixtures; /// public class IntegrationFixtureTests { + // A static shared instance for the whole test class, because that's how xunit uses fixtures + private static readonly IntegrationFixture fixture = new(); + [Fact] public async Task InitCreatesARepoWithTheProject() { - var fixture = new IntegrationFixture(); await fixture.InitializeAsync(Mock.Of()); - fixture.TemplateRepo.EnumerateFiles() + IntegrationFixture.TemplateRepo.EnumerateFiles() .Select(f => f.Name) .ShouldContain("kevin-test-01.fwdata"); } @@ -23,33 +24,18 @@ public async Task InitCreatesARepoWithTheProject() [Fact] public async Task CanFindTheProjectZipFile() { - var fixture = new IntegrationFixture(); await fixture.InitializeAsync(Mock.Of()); - fixture.TemplateRepoZip + IntegrationFixture.TemplateRepoZip .Directory!.EnumerateFiles().Select(f => f.Name) - .ShouldContain(fixture.TemplateRepoZip.Name); + .ShouldContain(IntegrationFixture.TemplateRepoZip.Name); } [Fact] public async Task CanInitFlexProjectRepo() { - var fixture = new IntegrationFixture(); await fixture.InitializeAsync(Mock.Of()); var projectConfig = fixture.InitLocalFlexProjectWithRepo(); Directory.EnumerateFiles(projectConfig.Dir) .ShouldContain(projectConfig.FwDataFile); } - - [Fact] - public async Task InitCleansUpPreviousRun() - { - var fixture = new IntegrationFixture(); - await fixture.InitializeAsync(Mock.Of()); - var filePath = Path.Join(Constants.BasePath, "test.txt"); - await File.WriteAllTextAsync(filePath, "test"); - Directory.EnumerateFiles(Constants.BasePath).ShouldContain(filePath); - - await fixture.InitializeAsync(); - Directory.EnumerateFiles(Constants.BasePath).ShouldNotContain(filePath); - } } diff --git a/backend/Testing/Services/Utils.cs b/backend/Testing/Services/Utils.cs index 2aaa001e9..76cf059cd 100644 --- a/backend/Testing/Services/Utils.cs +++ b/backend/Testing/Services/Utils.cs @@ -43,6 +43,7 @@ await apiTester.ExecuteGql($$""" type: FL_EX, id: "{{config.Id}}", code: "{{config.Code}}", + isConfidential: false, description: "Project created by an integration test", retentionPolicy: DEV }) { diff --git a/deployment/base/hg-deployment.yaml b/deployment/base/hg-deployment.yaml index f71642af7..8f2e1272b 100644 --- a/deployment/base/hg-deployment.yaml +++ b/deployment/base/hg-deployment.yaml @@ -44,6 +44,13 @@ spec: podSelector: matchLabels: app: lexbox + - namespaceSelector: + matchLabels: + kubernetes.io/metadata.name: languagedepot-dev + # Do NOT put a hyphen in front of podSelector on the next line + podSelector: + matchLabels: + app: lexbox --- diff --git a/frontend/src/lib/components/Projects/ProjectConfidentialityCombobox.svelte b/frontend/src/lib/components/Projects/ProjectConfidentialityCombobox.svelte index 8e44e0784..c761d02e8 100644 --- a/frontend/src/lib/components/Projects/ProjectConfidentialityCombobox.svelte +++ b/frontend/src/lib/components/Projects/ProjectConfidentialityCombobox.svelte @@ -10,6 +10,7 @@ import { Select } from '$lib/forms'; import t, { type I18nKey } from '$lib/i18n'; + import { helpLinks } from '../help'; import type { Confidentiality } from './ProjectFilter.svelte'; // eslint-disable-next-line @typescript-eslint/no-redundant-type-constituents -- false positive @@ -13,7 +14,7 @@
- {#each Object.entries(options) as [value, label]} diff --git a/frontend/src/lib/components/Projects/ProjectFilter.svelte b/frontend/src/lib/components/Projects/ProjectFilter.svelte index 6096a59aa..04ff9b8c6 100644 --- a/frontend/src/lib/components/Projects/ProjectFilter.svelte +++ b/frontend/src/lib/components/Projects/ProjectFilter.svelte @@ -47,6 +47,8 @@ import t from '$lib/i18n'; import IconButton from '../IconButton.svelte'; import ProjectConfidentialityFilterSelect from './ProjectConfidentialityFilterSelect.svelte'; + import SupHelp from '../help/SupHelp.svelte'; + import { helpLinks } from '../help'; type Filters = Partial & Pick; export let filters: Writable; @@ -157,7 +159,10 @@