- колонка `owner` связью с `users` в обеих коллекциях новым шагом схемы `202608140001`; чтение задачи сужено владельцем, и чужая, ничья и несуществующая дают один ответ; правило просмотра файлов сужено им же - приём по HTTP берёт владельца из сессии, а предъявителя без учётной записи пользователя отвергает до чтения тела: позже пришлось бы убирать уложенный файл, а уборки файлов сервис не умеет. Выборка воркера владельцем не сужается - удаление учётной записи с записями отвергается стражем, и вешает его сама сборка хранилища: сборка, забывшая его позвать, теряла защиту молча
11 KiB
11 KiB
Критерии приёмки задачи
Дословно из tasks/items/record-ownership.md. Файл задачи закрытие удалит —
критерии обязаны его пережить.
- Запрос чужой записи по её идентификатору возвращает «не найдено», а не содержимое и не «доступ запрещён». Оракул — тест: две сессии, задача первой запрашивается второй, ответ 404 и пустое тело. Расхождение: «пустое тело» разошлось с нетронутым сценарием спеки, где ответ по неизвестному идентификатору несёт сообщение о ненайденной задаче. Читается как «то же тело, что и у неизвестного идентификатора, и без состояния и текста расшифровки» — иначе реализация по букве критерия молча снимет действующее требование о сообщении.
- Запись, заведённая из веба, принадлежит вошедшему. Оракул — тест приёма по HTTP со сверкой колонки владельца.
- Выборка воркера владельцем не сужается: конвейер обрабатывает записи всех. Оракул — тест: задачи двух владельцев проходят конвейер одним воркером.
- База заводится с чистого листа, колонка владельца обязательна и без умолчания. Оракул — прогон миграций на пустой базе и попытка вставки без владельца.
Четвёртый критерий разошёлся с рамками той же задачи и правится решением
человека на чекпоинте: рамки запрещают назначать владельца записям из Telegram, а
обязательная колонка такую запись вставить не даст. Разобрано в design.md,
«Open Questions»; ниже план написан по рекомендованному способу — колонка без
умолчания, допускающая пустое значение, обязательность приёма по HTTP держит код.
Рубрика ревью дизайна — приёмочные критерии
Порождена проходом rubric до чтения артефактов. Приёмка судится по одному
списку: этот блок и блок выше.
- Чужая запись неотличима от несуществующей на всех наблюдаемых осях: тот же код, то же тело, та же форма ответа.
- Сужение живёт в одном месте пути чтения и обязательно к употреблению: второго читающего входа, у которого сужение можно не позвать, нет.
- Пустой владелец на входе чтения не совпадает ни с одной записью — своей, чужой и ничьей.
- Записи без владельца недостижимы сужённым путём никому, и недостижимость выведена из правила, а не из того, что таких записей мало.
- Владелец назначается сервером из сессии: поле владельца, пришедшее запросом, на результат не влияет.
- Владелец появляется в той же операции, что и запись; отказ по отсутствию владельца не оставляет ни задачи, ни файла, и способ этого назван.
- Неизменность владельца держит механизм, а не обещание: назван каждый путь, которым владельца можно переписать, и что его удерживает.
- Колонка прочитана всеми четырьмя местами правки колонок очереди плюс шагом схемы.
- Воркер владельцем не сужается, и это записано нормой, а не оставлено умолчанию.
- Судьба записи при исчезновении владельца определена.
- Шаг схемы: одно представление «владельца нет», откат не требует переписывания применённого шага.
- Проверка владельца и чтение состояния берутся из одного чтения записи, а не двумя раздельными.
- Ответ отправителю адресуется по источнику записи, а не по владельцу: запись без владельца получает свой ответ в чат.
- Ни ответ, ни журнал не выдают того, что прячет разграничение; различение чужой и несуществующей в журнале допустимо.
1. Схема хранилища
- 1.1 Новый шаг схемы
202608140001_record_owner.go: колонкаownerв таблице задач связью с коллекциейusers, без умолчания, пустое значение допустимо, каскадное удаление выключено. Имя колонки —owner, поле сущности —OwnerID; имена названы здесь, потому что разойтись им есть где — семь мест плюс подпись метода - 1.2 Тем же шагом — колонка
ownerв таблице файлов, теми же свойствами; правило просмотра коллекции файлов сужается владельцем вместо прежнего «всякий вошедший» - 1.3 Тем же шагом — запрет удаления учётной записи, у которой остались задачи: отказ с причиной, а не снятие ссылки
- 1.4 Прежние шаги схемы не тронуты — проверяется шагом гейта
migrations - 1.5
docs/database.md: строки колонок в обеих таблицах, правило выборки по владельцу, новое правило просмотра файлов и запрет удаления учётной записи
2. Сущность и контракт
- 2.1
internal/entity: у задачи расшифровки появляется владелец - 2.2
internal/contract: читающий метод репозитория задач принимает владельца; второго читающего метода не заводится
3. Хранилище задач
- 3.1
applyToRecordкладёт владельца при заведении - 3.2
recordToJobчитает владельца - 3.3
acquireColumnsиacquiredRowчитают колонку владельца - 3.4
applyOwnedByPipelineвладельца не трогает - 3.5 Чтение задачи сужено владельцем: задача другого владельца и задача без владельца отдают ту же ошибку, что и несуществующая
4. Приём и опрос
- 4.1
internal/service: метод заведения задачи из веба принимает владельца и отказывает при пустом; метод заведения из Telegram владельца не назначает. Владелец кладётся и на файл, заводимый при приёме - 4.2
internal/controller/http: приём берёт владельца из предъявленной сессии - 4.3
internal/controller/http: опрос готовности передаёт владельца в хранилище и отвечает404с прежним телом на чужую и на ничью задачу
5. Проверки
- 5.1 Тест: две сессии, задача первой запрашивается второй —
404, тело без состояния и текста - 5.2 Тест: приём по HTTP заводит задачу с владельцем-предъявителем
- 5.3 Тест: приём по HTTP без узнанной учётной записи задачи не заводит
- 5.4 Тест: задачи двух владельцев и задача без владельца проходят конвейер одним воркером
- 5.5 Тест: шаг конвейера, сохраняющий результат, владельца не затирает
- 5.6 Тест: миграции на пустой базе заводят колонку без умолчания
- 5.7 Тест: чтение с пустым владельцем не отдаёт ни своей, ни чужой, ни ничьей задачи
- 5.8 Тест: сессия без учётной записи пользователя получает
403на приёме, файла и задачи не заводится — узнан, но не запись коллекции пользователей - 5.9 Тест: вошедший просит токен чужого файла — отказ; своего — успех
- 5.10 Тест: удаление учётной записи с задачами отвергается, без задач — проходит
- 5.11
task gateзелёный
6. Архивация
- 6.1 На архивации выправить
## Purposeспекиaccess: преамбула переживает слияние дельт дословно и сегодня утверждает, что разграничения по владельцу нет. Валидатор преамбулу не судит — вспомнить об этом больше некому - 6.2 Сверить
docs/security.md,docs/passport.mdиdocs/architecture.md(строка «Разграничения записей по владельцу здесь нет»): все три обещают закрытие разграничением именно этой задачей. Адрес вarchitecture.mdназван отдельно — его нашло ревью, и без него обзор архитектуры отправлял бы следующего читателя чинить уже закрытое