Функция выглядела почти элементарной. Загружаем Markdown-файлы, система читает текст и метаданные, создает статьи в CMS. Один файл - одна публикация. Именно так был описан массовый импорт True Creator.
Пока мы не попробовали загрузить настоящую статью.
Вместо одной записи предпросмотр показал семь. После изменения файла - четыре. Только третий тест, из которого были убраны все Markdown-заголовки, создал ровно один материал.
Так небольшая задача на импорт превратилась в последовательное исследование того, как разные части системы понимают один и тот же файл.
Семь статей из одного файла
В первом тесте было семь заголовков: один первого уровня и шесть второго. CMS показала семь отдельных записей.
Во втором файле мы добавили блок YAML frontmatter с названием, адресом и статусом. В тексте остались четыре заголовка - и в предпросмотре снова появились четыре материала.
Третья версия содержала те же метаданные и обычные абзацы, списки, жирный текст и цитату, но без строк, начинающихся с символа решетки. Предпросмотр наконец показал одну запись.
Причина ошибки стала понятной без доступа к коду: обработчик воспринимал заголовки Markdown как границы материалов. Он делал ровно противоположное тому, что обещал интерфейс.
Проблема была не в синтаксисе статьи. Проблема была в отсутствии согласованного контракта между форматом входного файла и импортом.
Что на самом деле означает «один файл - одна статья»
Эта короткая фраза скрывает несколько отдельных обязательств.
В начале файла может находиться YAML frontmatter: название, короткое описание, адрес, статус публикации и другие поля. После закрывающего разделителя начинается тело статьи. Заголовки второго и третьего уровней внутри тела - обычная разметка разделов. Они не должны участвовать в подсчете материалов.
Предпросмотр должен показывать именно те записи, которые будут созданы. И результат импорта должен совпадать с предпросмотром.
Когда мы стали формулировать эти правила явно, у задачи появились проверяемые условия. Один файл с десятью подзаголовками обязан давать одну запись. Десять файлов - десять записей. Ни количество разделов, ни длина текста не меняют эту арифметику.
Такая спецификация значительно полезнее расплывчатого требования «поддержать Markdown».
Импорт - это еще и проверка данных
После исправления разбиения проявился другой класс проблем.
В одном из запусков импорт сообщил, что создал проект, указанный в метаданных статьи. Система при этом поместила сообщение о создании проекта в красный блок ошибок.
Но проект для нас - не случайная метка. Он должен существовать в заранее утвержденном справочнике. Автоматическое создание нового значения из непроверенного текста опасно: достаточно опечатки в названии, и навигация начинает расползаться.
Позже мы уточнили итоговую модель: каждая статья связана с одной существующей темой и одним существующим проектом. Обе связи обязательны. Импорт должен проверять их до сохранения записи, а не додумывать за редактора недостающие сущности.
Это хороший пример того, почему массовая загрузка не может быть отдельным миром со своими правилами. Ручная форма, импортер и публичный сайт должны понимать классификацию одинаково.
Статья создана. Значит, все хорошо?
Нет. Следующим тестом мы открыли созданный материал в редакторе и получили техническую ошибку Prisma: код пытался загрузить связь tags, которой уже не было в актуальной модели ContentNode.
Основные текстовые поля при этом присутствовали. Название, адрес и краткое описание сохранились. Значит, работающий импорт еще не гарантировал работающий редактор.
Отдельно обнаружился опасный симптом: файл имел статус DRAFT, но в редакторе статья отображалась как опубликованная. Без проверки базы нельзя было уверенно сказать, на каком именно этапе произошло расхождение. Но для редакционного процесса сам факт такого несоответствия недопустим.
Черновик нельзя считать безопасным, пока его состояние не проверено от чтения файла до публичного отображения.
Повторная загрузка тоже требует правил
Мы снова загрузили тот же материал и получили отказ из-за уникальности slug. База не позволила создать вторую статью с тем же адресом.
На уровне данных это правильная защита. На уровне интерфейса пользователь увидел длинный текст внутренней ошибки вместо простой фразы: «Материал с таким адресом уже существует».
Здесь возникло еще одно правило: конфликт должен обнаруживаться до создания записи и показываться понятным языком. Повторная загрузка не должна незаметно перезаписывать уже существующую публикацию.
Особенно важно это при пакетном импорте, когда редактор работает не с одним документом, а с десятками.
Почему мы не стали обходить проблему шаблоном
Можно было сделать статьи без подзаголовков, запретить определенные поля или каждый раз менять адрес вручную. Тогда тесты, возможно, выглядели бы успешнее.
Но такой подход означал бы, что содержимое подчиняется ошибкам инструмента.
Мы пошли другим путем: использовали реальные документы как диагностические сценарии, фиксировали наблюдаемое поведение и постепенно уточняли условия приемки.
В итоге массовый импорт перестал быть просто кнопкой «Загрузить». Он стал проверкой целостности всего контентного процесса: разбор файла, валидация метаданных, создание связей, сохранение статуса, открытие в редакторе и вывод на сайте.
Именно поэтому даже небольшую функцию полезно проверять не только на демонстрационном файле. Настоящая статья с несколькими разделами гораздо быстрее показывает, насколько система готова к работе.