rqr = (http) requester
Список просьб должен сохраняться на сервере, например в map.
Не сказано, должен ли сохраняться список просьб вместе с результатом, и что делать с некорректными просьбами (содержащими неверную структуру запроса или такие, которые нам не удалось выполнять). При реальной работе мы бы обратились к автору задания, а данное задание - тестовое. Было принято решение действовать простейшим образом - сохранять только успешно выполненные просьбы (например, если произошла ошибка при чтении запроса, мы не считаем это успехом), причём сохранять их вместе с нашим ответом.
Также неясно, что делать при повторном поступлении идентичной просьбы. Мы не пытаемся найти подобную просьбу, а выполняем просьбу заново.
Задача предполагает, что кандидат покажет знание перечисленных выше пунктов за исключением, может быть, goroutine/chan/sync.Mutex.
Поскольку http порождает горутины для обработки http запросов, мьютекс необходим для выполнения задания.
Так же мы хотели бы увидеть код приближённый к продакшн версии с понятными наименованиями переменных и http route-ов
Мы назвали просьбу fetchtask. В роутах можно было назвать её fetch-task, но мы выбрали название без чёрточек, чтобы можно было грепом найти все вхождения слова fetchtask, будь то маршрут или имя переменной. По этой же причине не получилось взять fetch_task - линтер ругается на подчёркивания в именах переменных golang. fetchTask мы тоже не стали применять, т.к. смешанный регистр не может применяться в именах файлов и не особо хорош в URL-ах. Поэтому получилось fetchtask, что не слишком хорошо читается.
В записях, подлежащих сериализации в JSON, мы называем поля с использованием только одной заглавной буквы - мы бы назвали их так же, как и маршруты, и по тем же причинам (ведь есть ещё и отображение на поля базы данных, а базы данных обычно нечувствительны к регистру), но неэкспортируемые поля не сериализуются в JSON.
В примерах из официальной документации можно видеть одно-, двух-, трёхбуквенные идентификаторы, например, w, req, t. Мы следуем этому стилю. Подразумевается, что текст будет читаться в IDE. Когда что-то непонятно, можно подвести курсор мыши и IDE (VS Code в нашем случае) показывает тип данных. Тип данных уже назван подробно и тем самым достигается одновременно лаконичность текста и понятность названий.
Принятая в Go идиома if err != nil
слишком многословна. Мы сокращаём её двумя способами:
- если функция f вызывает функцию g и выходит, то мы присваиваем err, который вернула g, именованному возвращаемому значению err в f. Так мы можем сэкономить одну проверку;
- в некоторых случаях мы возвращаем булево значение wasError, что означает "возникла ошибка, она обработана и теперь нужно прервать вычисление". В нашем случае есть два способа обработки ошибок - сообщить клиенту или записать в лог. Мы делаем это максимально близко к месту возникновения. Всё, что нужно сделать вызывающей функции - это понять, что запрос неудачен и прекратить обработку. Этот подход не идеален, например, мы передаём запрос и ответ глубоко в обработку предметной области. Но у него есть и плюсы в данном конкретном применении.
Кроме того, интерфейс error слишком беден. Иногда приходится полагаться на анализ строкового сообщения об ошибки, что является ненадёжным. Мы сделали свой интерфейс, который расширяет error и позволяет возвращать код ошибки, см. jsonerror.go.
Линтер ругается, когда публичная сущность не имеет документации. Но в некоторых случаях там совсем нечего написать. Мы были вынуждены делать пустую документацию, чтобы в IDE не висели предупреждения.
см. testing.md
- ✓ описание структур данных
- ✓ использование интерфейсов
- ± обработка ошибок (кроме panic/recover())
- ✓ работа с json
- ✓ обработка http запросов
- ✓ использование http.Client
- ✓ использование стандартных структур данных map/slice
- ± goroutine/chan/sync.Mutex (только sync.Mutex)