- адреса приложения переехали в своё пространство `/app/`, опрос готовности убран целиком: рубеж и причину остановки владелец узнаёт карточкой записи, текст — отдельным адресом названного вида - заведена единая точка отображения доменной ошибки и слой, приводящий к той же форме отказы библиотеки: тело несёт машиночитаемый код рядом с сообщением - у записи появились имя файла отправителя, длительность и размер своими колонками, а у ленты владельца — свой индекс: без него страница сканировала весь архив сервиса
78 lines
4.5 KiB
Go
78 lines
4.5 KiB
Go
package contract
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
)
|
|
|
|
// ErrOwnerRequired — приём по HTTP дошёл до заведения задачи, а владельца ему не
|
|
// назвали. Значение сентинельное: нести отказу нечего, а имя учётной записи в
|
|
// него не кладётся никогда.
|
|
var ErrOwnerRequired = errors.New("owner is required to accept a record")
|
|
|
|
// ErrRecordUnreadable — присланную запись не удалось прочитать: источник
|
|
// метаданных не разобрал её содержимое. Причина отказа — сама запись, а не сбой
|
|
// сервиса, и код ответа обязан называть причину, а не место.
|
|
//
|
|
// Заводится sentinel'ом, а не остаётся голой ошибкой источника метаданных:
|
|
// ветвь по умолчанию отдала бы `500`, и «файл негоден» читалось бы как «сломался
|
|
// сервер». Своих данных отказу нести нечего — имя файла в него не кладётся
|
|
// никогда.
|
|
var ErrRecordUnreadable = errors.New("uploaded record cannot be read")
|
|
|
|
// ErrRecordTooLarge — присланная запись длиннее потолка размера. Самый частый
|
|
// отказ у человека на мобильной сети, и прежде он уходил телом ограничителя тела
|
|
// — мимо единой формы отказа.
|
|
var ErrRecordTooLarge = errors.New("uploaded record exceeds size limit")
|
|
|
|
// ErrTextNotReady — текста запрошенного вида у записи ещё нет. Состояние, а не
|
|
// отсутствие: запись есть и принадлежит спрашивающему, просто конвейер до этого
|
|
// вида не дошёл. Отвечать на это тем же, чем отвечает чужая запись, нельзя —
|
|
// человек увидел бы «не найдено» на своей записи, загруженной минуту назад.
|
|
var ErrTextNotReady = errors.New("requested text view is not ready yet")
|
|
|
|
// ErrBadRequest — во входе запроса негодное значение: неизвестный вид текста,
|
|
// нечитаемый ключ страницы, отрицательный размер. Отличается от ErrRecordUnreadable
|
|
// тем, что негодна **просьба**, а не присланная запись.
|
|
var ErrBadRequest = errors.New("request input is not valid")
|
|
|
|
// ErrUnauthorized — сессии нет вовсе. Первая строка таблицы отображения, и без
|
|
// собственного признака она собиралась бы руками мимо единой точки: правка формы
|
|
// тела не доехала бы до неё, и два места разошлись бы молча.
|
|
var ErrUnauthorized = errors.New("session is required")
|
|
|
|
// ErrNotFound — под корнем приложения такого адреса нет. Отличается от
|
|
// JobNotFoundError тем, что не найдена **просьба**, а не запись: тело у ответа
|
|
// то же, но повод другой, и смешивать их в одном признаке значило бы называть
|
|
// отсутствующий адрес отсутствующей записью.
|
|
var ErrNotFound = errors.New("address not found")
|
|
|
|
type JobNotFoundError struct {
|
|
State string
|
|
Message string
|
|
}
|
|
|
|
func (e *JobNotFoundError) Error() string {
|
|
return fmt.Sprintf("%s - %s", e.State, e.Message)
|
|
}
|
|
|
|
// LostAcquisitionError — захват задачи за время работы шага достался другому.
|
|
// Шаг, получивший его, завершается без записи результата: иначе два воркера
|
|
// пишут в одну запись по очереди, портя её результат, и счётчик отказов
|
|
// сбрасывает тот, кто уже не владелец.
|
|
type LostAcquisitionError struct {
|
|
JobID string
|
|
}
|
|
|
|
func (e *LostAcquisitionError) Error() string {
|
|
return fmt.Sprintf("%s: job acquisition lost", e.JobID)
|
|
}
|
|
|
|
type NoopJobError struct {
|
|
State string
|
|
}
|
|
|
|
func (e *NoopJobError) Error() string {
|
|
return fmt.Sprintf("%s: no op job occur", e.State)
|
|
}
|