Skip to content

Feat: import files from disk#9

Merged
oshankkkk merged 7 commits into
mainfrom
feat/import-files
Apr 12, 2026
Merged

Feat: import files from disk#9
oshankkkk merged 7 commits into
mainfrom
feat/import-files

Conversation

@oshankkkk
Copy link
Copy Markdown
Owner

@oshankkkk oshankkkk commented Apr 12, 2026

Summary by CodeRabbit

  • New Features

    • Import markdown files to create notes via a new "Import notes" UI button and server endpoint.
  • Improvements

    • Editor now normalizes and parses Markdown for consistent content display and editing.
    • Import flow refreshes notes and selects the newly imported note.
  • Bug Fixes

    • Improved request validation and error handling for note updates and imports.
  • Tests

    • Added tests covering import, update validation, and error scenarios.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 12, 2026

📝 Walkthrough

Walkthrough

Adds a notes import feature: new authenticated POST /notes/imports route, backend handler and validators to create-and-update notes from uploaded Markdown, frontend file-picker UI and hook to send markdown to the new endpoint, plus tests covering import and validation flows.

Changes

Cohort / File(s) Summary
Backend Route Registration
backend/cmd/server/main.go
Register POST /notes/imports to ImportNotesHandler; other methods on that path return 405.
Backend Handlers & Tests
backend/internal/api/handler/handler.go, backend/internal/api/handler/handler_test.go
Added ImportNotesHandler; updated UpdateNoteHandler error handling; tests added for import, update failure cleanup, invalid JSON, and expanded UpdateNote tests.
Backend Validation & Tests
backend/internal/api/validator/validator.go, backend/internal/api/validator/validator_test.go
Added ImportNotesValidator and shared validateJSONContentType; updated UpdateNote validator to accept media types with charset; tests added for content-type and import path validation.
Frontend Editor infra
frontend/src/editor.ts, frontend/src/components/Editor.tsx
Centralized TipTap extensions and markdown parsing (editorExtensions, parseMarkdownContent, normalizeEditorContent); Editor now uses these and avoids stale closures via ref.
Frontend UI & Hook
frontend/src/components/Options.tsx, frontend/src/Puffnote.tsx, frontend/src/hooks/useNotes.ts
Added Options component with hidden file input and “Import notes” button; integrated into main UI; useNotes gained handleImportNotes which reads .md files, posts {title, content} to /notes/imports, refreshes notes, and sets active index.

Sequence Diagram

sequenceDiagram
    actor User
    participant Options as "Options Component"
    participant Hook as "useNotes Hook"
    participant API as "POST /notes/imports (Fetch)"
    participant Handler as "ImportNotesHandler"
    participant Service as "Note Service"
    participant DB as "Database"

    User->>Options: selects markdown file
    Options->>Hook: onImportNotes(file)
    Hook->>API: POST {title, content} (credentials included)
    API->>Handler: route to ImportNotesHandler
    Handler->>Handler: validate method, content-type, path, auth
    Handler->>Service: CreateNote(userID, title)
    Service->>DB: insert note -> noteID
    Handler->>Handler: parse markdown to editor content
    Handler->>Service: UpdateNoteById(noteID, content)
    Service->>DB: update content
    Handler-->>API: respond (202 / error)
    Hook->>Hook: refresh notes list
    Hook-->>User: update UI (activate imported note)
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~40 minutes

Poem

🐰 I found a file beneath the log,

I nudged it in and beat the bog,
Validator pranced, the parser cheered,
A note appeared — my hops were spared! ✨

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 12.82% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately reflects the main change: adding functionality to import notes from files on disk, which spans both frontend UI components and backend API endpoints.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/import-files

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (2)
frontend/src/hooks/useNotes.ts (1)

108-113: Add lightweight file validation before reading content.

A small type/size guard will prevent accidental large/binary imports from freezing the UI path.

Suggested fix
+const MAX_IMPORT_BYTES = 2 * 1024 * 1024;
+
 async function importNotes(file: File, getToken: GetToken): Promise<void> {
+  const isMarkdown = /\.md$/i.test(file.name) || file.type === "text/markdown";
+  if (!isMarkdown) {
+    throw new Error("only markdown files are supported");
+  }
+  if (file.size > MAX_IMPORT_BYTES) {
+    throw new Error("file too large to import");
+  }
+
   const url = BASEURL + "/notes/imports";
   const headers = await authHeaders(getToken);
   const markdown = await file.text();
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@frontend/src/hooks/useNotes.ts` around lines 108 - 113, The importNotes
function should perform a lightweight validation on the incoming File before
calling file.text() to avoid large/binary reads; add checks at the top of
importNotes (before const markdown = await file.text()) to verify file.size is
under a reasonable limit (e.g. 1MB) and that file.type is a text/ type or the
filename endsWith .md (case-insensitive); if validation fails, throw or return a
clear error so callers can handle it; keep references to importNotes, file.text,
parseMarkdownContent, GetToken and BASEURL when updating the function.
frontend/src/components/Options.tsx (1)

26-31: Restrict file picker to markdown files.

Adding accept improves UX and reduces invalid selections before onImportNotes runs.

Suggested fix
       <input
         ref={fileInputRef}
         type="file"
+        accept=".md,text/markdown"
         className="hidden"
         onChange={handleFileChange}
       />
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@frontend/src/components/Options.tsx` around lines 26 - 31, The file input in
the Options component should restrict selectable files to Markdown to improve
UX: update the <input> element (the one using fileInputRef and invoking
handleFileChange) to include an accept attribute like
".md,.markdown,text/markdown" so the picker only shows Markdown files; ensure
handleFileChange and any callers such as onImportNotes continue to validate file
type if needed but the primary change is adding the accept prop to the input.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@backend/internal/api/handler/handler.go`:
- Around line 100-111: The current flow calls db.Service.CreateNote before
validating the incoming payload and uses request.UpdateNoteparser which can
panic on parse errors, and then ignores the error returned by
db.Service.UpdateNodeById; change the flow so you first parse/validate the
request payload with request.UpdateNoteparser and return an HTTP error on
parse/validation failure (avoid panics), then call db.Service.CreateNote only
after validation; when calling db.Service.UpdateNodeById check its returned
error and if it fails, delete/rollback the created note (using a delete/remove
method on db.Service or a provided rollback) and return an appropriate HTTP
error instead of success; only write the success response
(response.WriteResponse) after UpdateNodeById succeeds.

In `@backend/internal/api/validator/validator.go`:
- Around line 144-148: The Content-Type check in ImportNotesValidator rejects
valid headers like "application/json; charset=utf-8"; change the strict equality
to parse the header with mime.ParseMediaType(r.Header.Get("Content-Type")) and
compare only the returned mediaType to "application/json", returning the same
http.StatusUnsupportedMediaType on mismatch; mirror the identical fix in
UpdateNoteValidator so both validators accept valid JSON content-type
parameters.

---

Nitpick comments:
In `@frontend/src/components/Options.tsx`:
- Around line 26-31: The file input in the Options component should restrict
selectable files to Markdown to improve UX: update the <input> element (the one
using fileInputRef and invoking handleFileChange) to include an accept attribute
like ".md,.markdown,text/markdown" so the picker only shows Markdown files;
ensure handleFileChange and any callers such as onImportNotes continue to
validate file type if needed but the primary change is adding the accept prop to
the input.

In `@frontend/src/hooks/useNotes.ts`:
- Around line 108-113: The importNotes function should perform a lightweight
validation on the incoming File before calling file.text() to avoid large/binary
reads; add checks at the top of importNotes (before const markdown = await
file.text()) to verify file.size is under a reasonable limit (e.g. 1MB) and that
file.type is a text/ type or the filename endsWith .md (case-insensitive); if
validation fails, throw or return a clear error so callers can handle it; keep
references to importNotes, file.text, parseMarkdownContent, GetToken and BASEURL
when updating the function.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: b5dec886-a299-4d67-96a0-db5aab54c4b4

📥 Commits

Reviewing files that changed from the base of the PR and between 070f683 and 3724bab.

📒 Files selected for processing (8)
  • backend/cmd/server/main.go
  • backend/internal/api/handler/handler.go
  • backend/internal/api/validator/validator.go
  • frontend/src/Puffnote.tsx
  • frontend/src/components/Editor.tsx
  • frontend/src/components/Options.tsx
  • frontend/src/editor.ts
  • frontend/src/hooks/useNotes.ts

Comment thread backend/internal/api/handler/handler.go
Comment thread backend/internal/api/validator/validator.go Outdated
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
backend/internal/api/handler/handler_test.go (1)

202-218: ⚠️ Potential issue | 🟡 Minor

Missing assertion on response status code.

The test verifies mock expectations but does not assert the HTTP status code. Add an assertion to confirm the handler returns the expected status on successful deletion.

🔧 Proposed fix
 	handler.DelNoteHandler(w, req)

+	assert.Equal(t, http.StatusOK, w.Code)
 	mockService.AssertExpectations(t)
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@backend/internal/api/handler/handler_test.go` around lines 202 - 218, The
test TestDelNoteHandler calls Handler.DelNoteHandler and verifies mock
expectations but lacks an assertion on the HTTP response status; update
TestDelNoteHandler to assert the recorder's status code (e.g., check
w.Result().StatusCode or w.Code) equals the expected success code (use
http.StatusNoContent or the status your DelNoteHandler returns) after calling
handler.DelNoteHandler; keep the MockService.DelNoteById expectation and add
this single assertion to validate the handler's HTTP response.
🧹 Nitpick comments (1)
backend/internal/api/validator/validator_test.go (1)

125-153: Tests cover content-type validation but miss method and path validation.

The ImportNotesValidator implementation (per context snippet) also validates HTTP method and path format (/notes/imports). Consider adding tests for these scenarios to improve coverage:

  • Invalid HTTP method (e.g., GET instead of POST)
  • Invalid path (e.g., /notes/invalid or /wrong/imports)
📝 Suggested additional tests
func TestImportNotesValidator_InvalidMethod(t *testing.T) {
	body := strings.NewReader(`{"title":"test","content":{}}`)
	req := httptest.NewRequest(http.MethodGet, "/notes/imports", body)
	req.Header.Set("Content-Type", "application/json")

	err := ImportNotesValidator(req)

	assert.NotNil(t, err)
}

func TestImportNotesValidator_InvalidPath(t *testing.T) {
	body := strings.NewReader(`{"title":"test","content":{}}`)
	req := httptest.NewRequest(http.MethodPost, "/notes/invalid", body)
	req.Header.Set("Content-Type", "application/json")

	err := ImportNotesValidator(req)

	assert.NotNil(t, err)
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@backend/internal/api/validator/validator_test.go` around lines 125 - 153, Add
unit tests to cover HTTP method and path validation for ImportNotesValidator:
create a test that sends a GET request to "/notes/imports" (using
httptest.NewRequest with http.MethodGet) and asserts the returned error is
non-nil, and another test that sends a POST to an incorrect path such as
"/notes/invalid" and asserts a non-nil error; use the same JSON body and
Content-Type header setup as existing tests so the failures come from
ImportNotesValidator's method/path checks rather than content-type parsing.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Outside diff comments:
In `@backend/internal/api/handler/handler_test.go`:
- Around line 202-218: The test TestDelNoteHandler calls Handler.DelNoteHandler
and verifies mock expectations but lacks an assertion on the HTTP response
status; update TestDelNoteHandler to assert the recorder's status code (e.g.,
check w.Result().StatusCode or w.Code) equals the expected success code (use
http.StatusNoContent or the status your DelNoteHandler returns) after calling
handler.DelNoteHandler; keep the MockService.DelNoteById expectation and add
this single assertion to validate the handler's HTTP response.

---

Nitpick comments:
In `@backend/internal/api/validator/validator_test.go`:
- Around line 125-153: Add unit tests to cover HTTP method and path validation
for ImportNotesValidator: create a test that sends a GET request to
"/notes/imports" (using httptest.NewRequest with http.MethodGet) and asserts the
returned error is non-nil, and another test that sends a POST to an incorrect
path such as "/notes/invalid" and asserts a non-nil error; use the same JSON
body and Content-Type header setup as existing tests so the failures come from
ImportNotesValidator's method/path checks rather than content-type parsing.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 5f85d908-2a3e-4c7c-8ec8-a7485b061a95

📥 Commits

Reviewing files that changed from the base of the PR and between 3724bab and e8e44f8.

📒 Files selected for processing (4)
  • backend/internal/api/handler/handler.go
  • backend/internal/api/handler/handler_test.go
  • backend/internal/api/validator/validator.go
  • backend/internal/api/validator/validator_test.go
🚧 Files skipped from review as they are similar to previous changes (2)
  • backend/internal/api/validator/validator.go
  • backend/internal/api/handler/handler.go

@oshankkkk oshankkkk merged commit bf5ac62 into main Apr 12, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant