Skip to content

Commit

Permalink
fix: allow degrading canonical slug to alias
Browse files Browse the repository at this point in the history
  • Loading branch information
polyrainbow committed Jun 17, 2024
1 parent bd7d689 commit f289162
Show file tree
Hide file tree
Showing 2 changed files with 179 additions and 4 deletions.
166 changes: 166 additions & 0 deletions src/lib/notes/index.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import { Block } from "../subwaytext/types/Block.js";
Object.assign(global, { TextDecoder, TextEncoder });
import { describe, it, expect, vi } from "vitest";
import { getCompareKeyForTimestamp } from "./utils.js";
import { ErrorMessage } from "./types/ErrorMessage.js";

vi.stubGlobal("navigator", {
hardwareConcurrency: 4,
Expand Down Expand Up @@ -2144,4 +2145,169 @@ describe("Notes module", () => {
expect(note2.backlinks.length).toBe(1);
expect(note2.backlinks[0].slug).toBe("note-1");
});

it("should allow degrading canonical slugs to aliases", async () => {
const mockStorageProvider = new MockStorageProvider();
const notesProvider = new NotesProvider(mockStorageProvider);

const noteSaveRequest1: NewNoteSaveRequest = {
note: {
content: "",
meta: {
additionalHeaders: {},
flags: [],
},
},
changeSlugTo: "main-slug",
ignoreDuplicateTitles: false,
aliases: new Set(),
};
await notesProvider.put(noteSaveRequest1);

const noteSaveRequest2: NoteSaveRequest = {
note: {
content: "",
meta: {
slug: "main-slug",
additionalHeaders: {},
flags: [],
},
},
ignoreDuplicateTitles: false,
changeSlugTo: "new-canonical-slug",
aliases: new Set(["main-slug"]), // same as old canonical slug
};
const note = await notesProvider.put(noteSaveRequest2);

expect(note.meta.slug).toBe("new-canonical-slug");
expect(note.aliases.size).toBe(1);
expect(note.aliases.has("main-slug")).toBe(true);
});

it(
"should fail when adding a slug of an existing note as alias to a new note",
async () => {
const mockStorageProvider = new MockStorageProvider();
const notesProvider = new NotesProvider(mockStorageProvider);

const noteSaveRequest1: NewNoteSaveRequest = {
note: {
content: "",
meta: {
additionalHeaders: {},
flags: [],
},
},
changeSlugTo: "s1",
ignoreDuplicateTitles: false,
aliases: new Set(),
};
await notesProvider.put(noteSaveRequest1);

const noteSaveRequest2: NoteSaveRequest = {
note: {
content: "",
meta: {
additionalHeaders: {},
flags: [],
},
},
ignoreDuplicateTitles: false,
changeSlugTo: "s2",
aliases: new Set(["s1"]),
};
expect(notesProvider.put(noteSaveRequest2))
.rejects.toThrowError(ErrorMessage.NOTE_WITH_SAME_SLUG_EXISTS);
},
);

it(
"should fail when adding a slug of an existing note as alias to an "
+ "existing note",
async () => {
const mockStorageProvider = new MockStorageProvider();
const notesProvider = new NotesProvider(mockStorageProvider);

const noteSaveRequest1: NewNoteSaveRequest = {
note: {
content: "",
meta: {
additionalHeaders: {},
flags: [],
},
},
changeSlugTo: "s1",
ignoreDuplicateTitles: false,
aliases: new Set(),
};
await notesProvider.put(noteSaveRequest1);

const noteSaveRequest2: NoteSaveRequest = {
note: {
content: "",
meta: {
additionalHeaders: {},
flags: [],
},
},
ignoreDuplicateTitles: false,
changeSlugTo: "s2",
aliases: new Set(),
};

await notesProvider.put(noteSaveRequest2);

const noteSaveRequest3: NoteSaveRequest = {
note: {
content: "",
meta: {
slug: "s2",
additionalHeaders: {},
flags: [],
},
},
ignoreDuplicateTitles: false,
aliases: new Set(["s1"]),
};
expect(notesProvider.put(noteSaveRequest3))
.rejects.toThrowError(ErrorMessage.NOTE_WITH_SAME_SLUG_EXISTS);
},
);

it(
"should fail when adding an existing alias as alias",
async () => {
const mockStorageProvider = new MockStorageProvider();
const notesProvider = new NotesProvider(mockStorageProvider);

const noteSaveRequest1: NewNoteSaveRequest = {
note: {
content: "",
meta: {
additionalHeaders: {},
flags: [],
},
},
changeSlugTo: "s1",
ignoreDuplicateTitles: false,
aliases: new Set(["alias"]),
};
await notesProvider.put(noteSaveRequest1);

const noteSaveRequest2: NoteSaveRequest = {
note: {
content: "",
meta: {
additionalHeaders: {},
flags: [],
},
},
ignoreDuplicateTitles: false,
changeSlugTo: "s2",
aliases: new Set(["alias"]),
};
expect(notesProvider.put(noteSaveRequest2))
.rejects.toThrowError(ErrorMessage.ALIAS_EXISTS);
},
);
});
17 changes: 13 additions & 4 deletions src/lib/notes/noteUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -611,6 +611,9 @@ const handleExistingNoteUpdate = async (
existingNote.meta.flags = noteFromUser.meta.flags;
existingNote.meta.additionalHeaders = noteFromUser.meta.additionalHeaders;

const canonicalSlugShouldChange = "changeSlugTo" in noteSaveRequest
&& typeof noteSaveRequest.changeSlugTo === "string";

const aliasesToUpdate: Set<Slug> = new Set();

if (noteSaveRequest.aliases) {
Expand All @@ -629,17 +632,23 @@ const handleExistingNoteUpdate = async (
throw new Error(ErrorMessage.INVALID_ALIAS);
}
if (alias === existingNote.meta.slug) {
throw new Error(ErrorMessage.ALIAS_EXISTS);
/*
We allow assigning the canonical slug as an alias here only if
the canonical slug is also about to change to something else.
If the canonical slug should not change, this is an invalid operation.
*/
if (!canonicalSlugShouldChange) {
throw new Error(ErrorMessage.ALIAS_EXISTS);
}
} else if (graph.notes.has(alias)) {
throw new Error(ErrorMessage.NOTE_WITH_SAME_SLUG_EXISTS);
}
if (
graph.aliases.has(alias)
&& graph.aliases.get(alias) !== existingNote.meta.slug
) {
throw new Error(ErrorMessage.ALIAS_EXISTS);
}
if (graph.notes.has(alias)) {
throw new Error(ErrorMessage.NOTE_WITH_SAME_SLUG_EXISTS);
}
if (
graph.aliases.has(alias)
&& graph.aliases.get(alias) === existingNote.meta.slug
Expand Down

0 comments on commit f289162

Please sign in to comment.