Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use new diff update API #1249

Merged
merged 6 commits into from
Nov 19, 2024
Merged
Show file tree
Hide file tree
Changes from 3 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
6 changes: 3 additions & 3 deletions backend/FwLite/FwLiteProjectSync.Tests/EntrySyncTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ namespace FwLiteProjectSync.Tests;

public class EntrySyncTests : IClassFixture<SyncFixture>
{
private readonly AutoFaker _autoFaker = new(builder => builder.WithOverride(new MultiStringOverride()).WithOverride(new ObjectWithIdOverride()));
private static readonly AutoFaker AutoFaker = new(builder => builder.WithOverride(new MultiStringOverride()).WithOverride(new ObjectWithIdOverride()));
public EntrySyncTests(SyncFixture fixture)
{
_fixture = fixture;
Expand All @@ -20,8 +20,8 @@ public EntrySyncTests(SyncFixture fixture)
[Fact]
public async Task CanSyncRandomEntries()
{
var createdEntry = await _fixture.CrdtApi.CreateEntry(await _autoFaker.EntryReadyForCreation(_fixture.CrdtApi));
var after = await _autoFaker.EntryReadyForCreation(_fixture.CrdtApi, entryId: createdEntry.Id);
var createdEntry = await _fixture.CrdtApi.CreateEntry(await AutoFaker.EntryReadyForCreation(_fixture.CrdtApi));
var after = await AutoFaker.EntryReadyForCreation(_fixture.CrdtApi, entryId: createdEntry.Id);
await EntrySync.Sync(after, createdEntry, _fixture.CrdtApi);
var actual = await _fixture.CrdtApi.GetEntry(after.Id);
actual.Should().NotBeNull();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.11.0" />
<PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="8.0.1"/>
<PackageReference Include="FluentAssertions" Version="6.12.0"/>
<PackageReference Include="Soenneker.Utils.AutoBogus" Version="2.1.278" />
<PackageReference Include="Soenneker.Utils.AutoBogus" Version="3.0.410" />
<PackageReference Include="xunit" Version="2.9.0" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.8.2">
<PrivateAssets>all</PrivateAssets>
Expand Down
8 changes: 4 additions & 4 deletions backend/FwLite/FwLiteProjectSync.Tests/UpdateDiffTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ namespace FwLiteProjectSync.Tests;

public class UpdateDiffTests
{
private readonly AutoFaker _autoFaker = new(new AutoFakerConfig()
private static readonly AutoFaker AutoFaker = new(new AutoFakerConfig()
{
Overrides = [new MultiStringOverride(), new WritingSystemIdOverride()]
});
Expand All @@ -18,7 +18,7 @@ public class UpdateDiffTests
public void EntryDiffShouldUpdateAllFields()
{
var before = new Entry();
var after = _autoFaker.Generate<Entry>();
var after = AutoFaker.Generate<Entry>();
var entryDiffToUpdate = EntrySync.EntryDiffToUpdate(before, after);
ArgumentNullException.ThrowIfNull(entryDiffToUpdate);
entryDiffToUpdate.Apply(before);
Expand All @@ -33,7 +33,7 @@ public void EntryDiffShouldUpdateAllFields()
public async Task SenseDiffShouldUpdateAllFields()
{
var before = new Sense();
var after = _autoFaker.Generate<Sense>();
var after = AutoFaker.Generate<Sense>();
var senseDiffToUpdate = await SenseSync.SenseDiffToUpdate(before, after);
ArgumentNullException.ThrowIfNull(senseDiffToUpdate);
senseDiffToUpdate.Apply(before);
Expand All @@ -44,7 +44,7 @@ public async Task SenseDiffShouldUpdateAllFields()
public void ExampleSentenceDiffShouldUpdateAllFields()
{
var before = new ExampleSentence();
var after = _autoFaker.Generate<ExampleSentence>();
var after = AutoFaker.Generate<ExampleSentence>();
var exampleSentenceDiffToUpdate = ExampleSentenceSync.DiffToUpdate(before, after);
ArgumentNullException.ThrowIfNull(exampleSentenceDiffToUpdate);
exampleSentenceDiffToUpdate.Apply(before);
Expand Down
4 changes: 2 additions & 2 deletions backend/FwLite/LcmCrdt.Tests/DataModelSnapshotTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ namespace LcmCrdt.Tests;

public class DataModelSnapshotTests : IAsyncLifetime
{
private static AutoFaker _faker = new AutoFaker(new AutoFakerConfig()
private static readonly AutoFaker Faker = new AutoFaker(new AutoFakerConfig()
{
Overrides = [new MultiStringOverride(), new WritingSystemIdOverride()]
});
Expand Down Expand Up @@ -90,7 +90,7 @@ public void VerifyIObjectWithIdsMatchAdapterGetObjectTypeName()
foreach (var jsonDerivedType in types)
{
var typeDiscriminator = jsonDerivedType.TypeDiscriminator.Should().BeOfType<string>().Subject;
var obj = _faker.Generate(jsonDerivedType.DerivedType);
var obj = Faker.Generate(jsonDerivedType.DerivedType);
new MiniLcmCrdtAdapter((IObjectWithId)obj).GetObjectTypeName().Should().Be(typeDiscriminator);
}
}
Expand Down
4 changes: 2 additions & 2 deletions backend/FwLite/LcmCrdt.Tests/EntityCopyMethodTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ namespace LcmCrdt.Tests;

public class EntityCopyMethodTests
{
private readonly AutoFaker _autoFaker = new(new AutoFakerConfig()
private static readonly AutoFaker AutoFaker = new(new AutoFakerConfig()
{
Overrides = [new MultiStringOverride(), new WritingSystemIdOverride()]
});
Expand All @@ -35,7 +35,7 @@ public static IEnumerable<object[]> GetEntityTypes()
public void EntityCopyMethodShouldCopyAllFields(Type type)
{
type.IsAssignableTo(typeof(IObjectWithId)).Should().BeTrue();
var entity = (IObjectWithId) _autoFaker.Generate(type);
var entity = (IObjectWithId) AutoFaker.Generate(type);
var copy = entity.Copy();
copy.Should().BeEquivalentTo(entity, options => options.IncludingAllRuntimeProperties());
}
Expand Down
2 changes: 1 addition & 1 deletion backend/FwLite/LcmCrdt.Tests/LcmCrdt.Tests.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
<PackageReference Include="Microsoft.Extensions.Hosting" Version="8.0.1" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.11.0" />
<PackageReference Include="FluentAssertions" Version="6.12.0"/>
<PackageReference Include="Soenneker.Utils.AutoBogus" Version="2.1.278" />
<PackageReference Include="Soenneker.Utils.AutoBogus" Version="3.0.410" />
<PackageReference Include="Verify.Xunit" Version="27.0.1" />
<PackageReference Include="xunit" Version="2.9.2" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.8.2">
Expand Down
4 changes: 2 additions & 2 deletions backend/FwLite/LocalWebApp/Hubs/MiniLcmApiHubBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -65,9 +65,9 @@ public virtual async Task<Entry> CreateEntry(Entry entry)
return newEntry;
}

public virtual async Task<Entry> UpdateEntry(Guid id, JsonPatchDocument<Entry> update)
public virtual async Task<Entry> UpdateEntry(Entry before, Entry after)
{
var entry = await miniLcmApi.UpdateEntry(id, new UpdateObjectInput<Entry>(update));
var entry = await miniLcmApi.UpdateEntry(before, after);
await NotifyEntryUpdated(entry);
return entry;
}
Expand Down
2 changes: 1 addition & 1 deletion backend/FwLite/MiniLcm.Tests/MiniLcm.Tests.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="FluentAssertions" Version="6.12.0"/>
<PackageReference Include="Soenneker.Utils.AutoBogus" Version="2.1.278"/>
<PackageReference Include="Soenneker.Utils.AutoBogus" Version="3.0.410" />
<PackageReference Include="System.Linq.Async" Version="6.0.1"/>
</ItemGroup>

Expand Down
2 changes: 1 addition & 1 deletion backend/FwLite/MiniLcm.Tests/MiniLcmTestBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ namespace MiniLcm.Tests;
public abstract class MiniLcmTestBase : IAsyncLifetime
{

protected readonly AutoFaker AutoFaker = new(builder =>
protected static readonly AutoFaker AutoFaker = new(builder =>
builder.WithOverride(new MultiStringOverride(["en"]))
.WithOverride(new ObjectWithIdOverride())
);
Expand Down
75 changes: 1 addition & 74 deletions frontend/viewer/src/lib/Editor.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,7 @@
import type {IEntry, IExampleSentence, ISense} from './mini-lcm';
import EntryEditor from './entry-editor/object-editors/EntryEditor.svelte';
import {createEventDispatcher, getContext} from 'svelte';
import jsonPatch from 'fast-json-patch';
import {useLexboxApi} from './services/service-provider';
import {isEmptyId} from './utils';
import type { SaveHandler } from './services/save-event-service';
import {useViewSettings} from './services/view-service';

Expand All @@ -27,26 +25,8 @@

const viewSettings = useViewSettings();

function withoutSenses(entry: IEntry): Omit<IEntry, 'senses'> {
let {senses, ...rest} = entry;
return rest;
}
function withoutExamples(sense: ISense): Omit<ISense, 'exampleSentences'> {
let {exampleSentences, ...rest} = sense;
return rest;
}

async function onChange(e: { entry: IEntry, sense?: ISense, example?: IExampleSentence }) {
await updateEntry(e.entry);
if (e.sense !== undefined) {
await updateSense(e.sense);
detectSenseIndexChanges(e.entry, e.sense);
if (e.example !== undefined) {
await updateExample(e.sense.id, e.example);
detectExampleIndexChanges(e.entry, e.sense, e.example);
}
}

dispatch('change', {entry: e.entry});
updateInitialEntry();
}
Expand All @@ -66,60 +46,7 @@

async function updateEntry(updatedEntry: IEntry) {
if (entry.id != updatedEntry.id) throw new Error('Entry id mismatch');
let operations = jsonPatch.compare(withoutSenses(initialEntry), withoutSenses(updatedEntry));
if (operations.length == 0) return;
console.debug('updateEntry', operations);
await saveHandler(() => lexboxApi.UpdateEntry(updatedEntry.id, operations));
}

async function updateSense(updatedSense: ISense) {
if (isEmptyId(updatedSense.id)) {
updatedSense.id = crypto.randomUUID();
await saveHandler(() => lexboxApi.CreateSense(entry.id, updatedSense));
return;
}
const initialSense = initialEntry.senses.find(s => s.id === updatedSense.id);
if (!initialSense) throw new Error('Sense not found in initial entry');
let operations = jsonPatch.compare(withoutExamples(initialSense), withoutExamples(updatedSense));
if (operations.length == 0) return;
console.debug('updateSense', operations);
await saveHandler(() => lexboxApi.UpdateSense(entry.id, updatedSense.id, operations));
}

async function updateExample(senseId: string, updatedExample: IExampleSentence) {
const initialSense = initialEntry.senses.find(s => s.id === senseId);
if (!initialSense) throw new Error('Sense not found in initial entry');
if (isEmptyId(updatedExample.id)) {
updatedExample.id = crypto.randomUUID();
await saveHandler(() => lexboxApi.CreateExampleSentence(entry.id, senseId, updatedExample));
return;
}
const initialExample = initialSense.exampleSentences.find(e => e.id === updatedExample.id);
if (!initialExample) throw new Error('Example not found in initial sense');
let operations = jsonPatch.compare(initialExample, updatedExample);
if (operations.length == 0) return;
console.debug('updateExample', operations);
await saveHandler(() => lexboxApi.UpdateExampleSentence(entry.id, senseId, updatedExample.id, operations));
}

function detectSenseIndexChanges(entry: IEntry, sense: ISense) {
const initialIndex = initialEntry.senses.findIndex(s => s.id === sense.id);
if (initialIndex === -1) return;
const currentIndex = entry.senses.findIndex(s => s.id === sense.id);
if (currentIndex === -1) return;
if (initialIndex !== currentIndex) {
// todo figure out how to send this to the server
}
}

function detectExampleIndexChanges(entry: IEntry, sense: ISense, example: IExampleSentence) {
const initialIndex = initialEntry.senses.find(s => s.id == sense.id)?.exampleSentences.findIndex(s => s.id === example.id);
if (initialIndex === -1 || initialIndex === undefined) return;
const currentIndex = sense.exampleSentences.findIndex(s => s.id === example.id);
if (currentIndex === -1) return;
if (initialIndex !== currentIndex) {
// todo figure out how to send this to the server
}
await saveHandler(() => lexboxApi.UpdateEntry(initialEntry, updatedEntry));
}
</script>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,10 @@
/* tslint:disable */

import type {ComplexFormType, Entry, ExampleSentence, PartOfSpeech, QueryOptions, SemanticDomain, Sense, WritingSystem, WritingSystems} from '../../mini-lcm';
import type { ILexboxApiHub, ILexboxClient } from './Lexbox.ClientServer.Hubs';
import type {ILexboxApiHub, ILexboxClient} from './Lexbox.ClientServer.Hubs';

import { HubConnection } from '@microsoft/signalr';
import type { JsonOperation } from '../Lexbox.ClientServer.Hubs';
import {HubConnection} from '@microsoft/signalr';
import type {JsonOperation} from '../Lexbox.ClientServer.Hubs';
import type {WritingSystemType} from '../../services/lexbox-api';

// components
Expand Down Expand Up @@ -174,8 +174,8 @@ class ILexboxApiHub_HubProxy implements ILexboxApiHub {
return await this.connection.invoke("CreateEntry", entry);
}

public readonly UpdateEntry = async (id: string, update: JsonOperation[]): Promise<Entry> => {
return await this.connection.invoke("UpdateEntry", id, update);
public readonly UpdateEntry = async (before: Entry, after: Entry): Promise<Entry> => {
return await this.connection.invoke("UpdateEntry", before, after);
}

public readonly DeleteEntry = async (id: string): Promise<void> => {
Expand Down
11 changes: 6 additions & 5 deletions frontend/viewer/src/lib/in-memory-api-service.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import {entries, projectName, writingSystems} from './entry-data';
/* eslint-disable @typescript-eslint/naming-convention */

import {entries, projectName, writingSystems} from './entry-data';
import type {
IEntry,
IExampleSentence,
Expand Down Expand Up @@ -123,10 +125,9 @@ export class InMemoryApiService implements LexboxApiClient {
return Promise.resolve(entry);
}

UpdateEntry(guid: string, update: JsonPatch): Promise<IEntry> {
const entry = entries.find(e => e.id === guid)!;
applyPatch(entry, update);
return Promise.resolve(entry);
UpdateEntry(_before: IEntry, after: IEntry): Promise<IEntry> {
entries.splice(entries.findIndex(e => e.id === after.id), 1, after);
return Promise.resolve(after);
}

CreateSense(entryGuid: string, sense: ISense): Promise<ISense> {
Expand Down
2 changes: 1 addition & 1 deletion frontend/viewer/src/lib/services/lexbox-api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ export interface LexboxApi {
GetEntry(guid: string): Promise<IEntry>;

CreateEntry(entry: IEntry): Promise<IEntry>;
UpdateEntry(guid: string, update: JsonPatch): Promise<IEntry>;
UpdateEntry(before: IEntry, after: IEntry): Promise<IEntry>;
DeleteEntry(guid: string): Promise<void>;

CreateSense(entryGuid: string, sense: ISense): Promise<ISense>;
Expand Down
Loading