Skip to content

feat(backend): эндпоинты выгрузки/импорта документов + enum DocumentType#22

Open
trialiya wants to merge 6 commits into
mainfrom
claude/backend-export-import-endq
Open

feat(backend): эндпоинты выгрузки/импорта документов + enum DocumentType#22
trialiya wants to merge 6 commits into
mainfrom
claude/backend-export-import-endq

Conversation

@trialiya

@trialiya trialiya commented Jun 14, 2026

Copy link
Copy Markdown
Owner

Что сделано

Три доработки на бэке (отдельная ветка от main).

1. Enum типа документа вместо String

  • Новый DocumentType { DOCUMENT, FOLDER } с @JsonValue/@JsonCreator — на проводе остаётся lowercase ("document"/"folder").
  • DocumentEntity.type теперь DocumentType.
  • DocumentTypeJdbcConverter (Reader/Writer) зарегистрирован в PgVectorJdbcConfig и H2JdbcConfig — в БД хранится lowercase, поэтому существующие строки и колонка не ломаются, миграция не нужна.
  • DTO остались String (минимальный blast radius): конверсия через getType().getValue(). Контракт API/фронта не изменился.

2. REST выгрузки — GET /api/documents/{id}/download?meta=false

  • документ → один .md (text/markdown, attachment);
  • папка.zip поддерева (Markdown + опц. .yaml), структура как у экспорта.
  • Через новый DocumentExportService.renderSubtree(...) — рендер в память, переиспользует существующие helpers; /?doc=ID переписываются в относительные пути внутри архива, внешние ссылки не трогаются.

3. Admin REST синхронизации/импорта — POST /api/documents/admin/import?parentId=

  • Новый DocumentImportService: обходит серверную папку экспорта, восстанавливает дерево, порядок и заголовки берёт из .index.md, тело папок из .content.md, делает обратное переписывание ссылок в /?doc=ID (двухпроходно). Возвращает ImportResult{created, folders, documents}.

Тесты

  • DocumentExportSubtreeTest — раскладка папки, переписывание ссылок, одиночный документ, отсутствующий root.
  • DocumentImportServiceTest — иерархия/порядок/заголовки, реверс ссылок, импорт под parentId, ошибка при отсутствии папки.
  • Существующие DocumentServiceUnitTest (H2 + реальный репозиторий) и PostgresDocumentIT обновлены под enum — подтверждают корректный round-trip конвертера.

Прогон тестов выполнялся локально (окружение содержит только JDK 21, проект требует JDK 25 — toolchain временно переключался для проверки, в коммитах этих правок нет). Полный набор пройдёт в CI на JDK 25.

https://claude.ai/code/session_01QwmrrALvDe85nY9kBakg5c

- DocumentType enum заменяет String-тип в DocumentEntity; JDBC-конвертер
  хранит lowercase ('document'/'folder') для обратной совместимости,
  @jsonvalue сохраняет контракт API
- GET /api/documents/{id}/download — документ как .md, папка как .zip
  (поддерево с переписыванием /?doc=ID ссылок), через renderSubtree
- POST /api/documents/admin/import — синхронизация (импорт) из серверной
  папки экспорта: восстановление дерева, порядок из .index.md, обратное
  переписывание ссылок на /?doc=ID
- тесты: renderSubtree (документ/папка/ссылки) и импорт (иерархия,
  порядок, реверс ссылок); существующие тесты обновлены под enum
@trialiya trialiya changed the title feat(backend): download/import endpoints + DocumentType enum feat(backend): эндпоинты выгрузки/импорта документов + enum DocumentType Jun 14, 2026
claude added 5 commits June 14, 2026 23:55
Render the download subtree lazily as a Stream<ExportEntry> (depth-first
walk using Stream.mapMulti for recursive child expansion) and write
entries straight into the response OutputStream (ZipOutputStream for
folders, raw Markdown for documents) instead of buffering the whole
archive in a byte[]. renderSubtree is kept as a thin eager collector
over streamSubtree for existing callers/tests.

https://claude.ai/code/session_01XejhfdCBDHhk3pqcszieMC
Replace the single repo.findAll() table scan in the download/stream path
with DocumentRepository.findAllByParentIdOrderByPosition(parentId): a
position-sorted stream fetched one level at a time and drained inside
try-with-resources so its JDBC cursor is released. The full documents
table is never held in memory; only the lightweight id→path map (for link
rewriting) and one level of entities live at a time. exportAll (disk
export) is left unchanged.

https://claude.ai/code/session_01XejhfdCBDHhk3pqcszieMC
exportAll now loads roots via findRoots and each level via the shared
childrenOf(parentId) helper (findAllByParentIdOrderByPosition, stream
closed immediately) instead of a full-table findAll() + in-memory
byParent index — consistent with the subtree streaming path. Adds
DocumentExportAllTest covering the on-disk layout, link rewriting, and
meta toggle; applies spotless formatting.

https://claude.ai/code/session_01XejhfdCBDHhk3pqcszieMC
…ding

Merge main into the branch (resolving the merge-only CI failure) and
update DocumentExportServiceTest — added on main after this branch
diverged — to the DocumentType enum and the per-level repository loader
(findAllByParentIdOrderByPosition) instead of the removed findAll().
Drops the now-redundant DocumentExportAllTest, since the merged
DocumentExportServiceTest covers exportAll comprehensively.

https://claude.ai/code/session_01XejhfdCBDHhk3pqcszieMC
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.

2 participants