Mobile API
===============
### Документация
При разработке API половина дела - это документация. На проекте ИГС мы пришли к следующему формату.
В конфлюенсе для каждой фичи заводим таблицу:
**Экран** | **Описание** | **API** | **АИС**
Одна строка - один экран.
**Экран** - скриншоты дизайна экрана приложения
**Описание** - словесное описание логики экрана + опционально тест-лист от QA
**API** - пример запроса-ответа, назначение каждого поля с указанием типа данных, обязательности передаваемых и возвращаемых полей
**АИС** - входная документация от заказчика. Может понадобиться QA
Я стремлюсь к тому, чтобы для одного экрана было не более одного запроса API. К примеру, один из запросов API может вызывать 6 методов SOAP-сервиса.
Также разработчики МП говорят спасибо, если структура и порядок данных максимально соответствуют дизайну.
### Свагер + postman
Для API автоматически генерируется описание swagger и настроен swagger-ui. В основном это нужно для того, чтобы понимать актуальное состояние API - выкатили метод/поле или нет и на какое оркужение.
> В iOS разработчики используют порочную практику генерации кода сетевого клиента из swagger.json. Если кратко, то проблема в том, что сгенеренный код - черный ящик. Также в swagger.json протекают названия DTO, что при рефакторинге API выливается в неожиданные доработки на стороне iOS.
>
> Если у вас будет тоже самое, то нужно подумать, как зафиксировать названия DTO при генерации swagger.json
Swagger для .NET имеет недоработки, например, не помечает поля deprecated, возможно, в java таких проблем нет. Это решается при помощи описания в конфлюенсе актуального состояния.
Для работы с запросами мы с QA используем единую учетку Postman и шарим коллекции и историю запросов. Postman гораздо удобнее свагера, например, из-за окружений и переменных.
### Соглашения
Прописано в шапке swagger-ui, дословно:
```
Даты:
Поля с суффиксом _date передаются в формате dd.mm.YYYY. Такие даты нельзя пересчитывать по часовому поясу
Поля с суффиксом _date_time передаются в формате DateTime ISO
Стоимость:
Поля с суффиксом _amount - это денежная сумма, нужно парсить без потери точности (вероятно в decimal)
Возврат ошибок:
Все ответы с кодом кроме 200 OK нельзя парсить, поскольку в теле ответа содержится только диагностическая информация, не предназначенная для использования на клиенте
Идентификаторы _id и id - тип string
```
### Формат
В случае ИГС SOAP-сервис присылает различное безобразие: Y, N, 1, 0 для булева типа, М, Ж, муж - для пола, тип double для идентификаторов, даты разных сортов и прочее. Я все по-максимуму привожу к унифицированным enum'ам и нормальным типам данных - string для id, boolean для булов, даты в iso формате.
Если в SOAP нужно передавать ФИО целой строкой, то в API есть отдельно FirstName SecondName LastName, которые склеиваются в API и передаются дальше. Для массивов приходит пустой массив место null. И так далее. Все эту косметику Саша П называет "чтобы все было по фэншую".
Ошибки наш SOAP-сервис отдает в кастомном формате и по ним нет толковой документации, но скорее всего можно узнать, какие коды ошибок соответствуют:
* Status400BadRequest
* Status401Unauthorized
* Status403Forbidden
* Status503ServiceUnavailable
* Как отличать бизнес-ошибку
Для таймаутов SOAP-сервиса у нас отдается 504 ошибка для понимания ситуации.
Формат респонза API:
{
"header": {
"error_code": 0,
"error_message": "string",
},
"data": {
...
}
}
Для МП получается прозрачная логика работа с ошибками
Если Http Status Code != 200 - то ошибка сервера, приложение показывает примерно следующий алерт "что то пошло не так, сообщите на саппорт". Если ошибка 503, то "пожалуйста, подождите, сервис скоро продолжит свою работу"
Если Http Status Code == 200 и error_code != 0 - то это бизнес-ошибка, показываем диалог с содержимым error_message.
Если Http Status Code == 200 и error_code == 0 - то все ок, можно смотреть, что пришло в data.
### Changelog
На каждую сборку настроен CI, который после деплоя API получает его swagger.json и заливает этот файл в другой репозиторий. С этого репозитория падает нотификашка на каждый коммит в отдельный канал в слаке. Таким образом можно видеть изменения методов и полей. Это помощь прежде всего самому себе, так как для всех изменений лучше заводить задачу или стикер на доску, иначе мобильщики что-нибудь пропустят.
Помимо этого при мерже правок на демо окружение я создаю в конфлюенсе страницу changelog с таблицей следующего формата:
**Method** | **Request** | **Response** | **iOS** | **Android**
**Method** - uri метода
**Request** - словесное описание изменений в реквесте метода. Иногда сожержит подсказку для QA как проверять.
**Response** - словесное описание изменений в респонзе метода. Иногда сожержит подсказку для QA как проверять.
**iOS** | **Android** - заполняет QA при проверке.
### Моки API
Моки нужны по нескольким причинам:
* они работают быстрее, это экономия времени для разработчиков приложения
* внешний сервис крайне нестабилен
* заказчик долго предоставляет тестовые данные
Для моков у нас используется mock-server.js. Его код и стабы хранятся в gitlab, в gitlab есть удобный web ide, так что любой участник проекта в теории может быстро добавить нужный мок на общий мок-сервер либо запустить свой локальный мок-сервер. На практике только единицы отважились этим воспользоваться. Возможно, подход с apiary более жизнеспособный.
На эту тему у нас висят в бэклоге еще несколько идей
* использовать replay.js для того, чтобы кэшировать все успешные респонзы. В случае, когда упал внешний сервис, переключаемся на кэш. Только для тестового окружения.
* моки для внешнего сервиса для прогона автотестов
### Кэширование + gzip
Несмотря на ограниченность канала сотовых сетей, сжатие траффика достаточно для того, чтобы оставлять json-ы максимально читаемыми, не оптимизировать и не урезать данные.
Но экономить трафик мобильного приложения - это благородно, особенно, для всяких справочников. Нужно реализовывать Cache-Control, ETag, Expires. iOS и Android поддерживают все из коробки. Соответственно со стороны SOAP понадобятся доработки для возврата дат последнего изменения данных либо информация, насколько часто данные меняются.
### Логирование
У нас логируются все xml и json'ы, которые проходят через мобильное API. На проде набегает примерно 5гб в сутки до сжатия, храним неделю. За редким исключением недели вполне достаточно.
Приложение на старте генерирует GUID на 20 минут (как бы сессия) и отправляет его в каждый запрос в специальном хедере. Это нужно, чтобы отслеживать поведение пользователя, даже пока он не авторизован или меняет учетку.
API в каждом запросе возвращает ResponseTag. Разработчики мобильного приложения могут сообщить его при обнаружении проблем с запросом, чтобы затем было удобнее искать конкретный запрос по логам.
### Версионирование
iOS во всех запросах стандартно отдает User-Agent c версией приложения. В Android для этих целей добавили хедер AppVersion. Как вариант, попробовать также пихать в User-Agent.
По классике API имеет версию, а клиент указывает, с какой версией хочет работать. У нас, поскольку все потребители заранее известны, проверка версии происходит на стороне API в хедерах User-Agent и AppVersion. Таким образом, если возникает необходимость что-то поправить для старых версий, мы делаем проверку в API с указанием версий приложения. В классическом способе придется записывать в таблице, какая версия приложения на какую версию API смотрит.
Поля и методы, которые становятся depreceted, желательно помечать в комментах датой, когда появилась метка, чтобы подчищать API по мере ухода старых версий из маркета.
Также иногда возникает необходиость обрезать доступ к некоему методу для старых версий приложения. Мы просто кидаем бизнес-ошибку вида "ваша версия приложения устарела". Как более мягкий вариант, можно было бы в header добавить поле warning_message для того, чтобы выводить данный алерт, но при этом продолжать работу как обычно.
### Отмена операций
Наш SOAP-сервис не поддерживает cancellation. Для этой проблемы два решения:
* блокировать отмену операции на ui до полного выполнения запроса
* обрабатывать отмену в API и дергать второй SOAP-метод, аннигилирующий действие первого метода - мы так сделали
### Окружения
API живет в 4х окружениях:
* тест - для разработки приложения, здесь самая актуалные изменения
* стейдж (демо) - для тестирования и демонстрации заказчику
* препрод - это в среде заказчика, смотрит на боевоей сервер
* прод
На тесте http-кэш полностью выключен, чтобы разработка шла на актуальной версии респонзов.
Android сборки:
QA - смотрит на API Test, Stage, Preprod - в меню можно менять сервера. В идеальном мире адрес сервера можно вводить вручную, тогда можно тестировать работу со своим локальным инстансом API.
Release - смотрит на API Prod
iOS:
DT - смотрит на API Test
DS - смотрит на API Stage
DPP - смотрит на API PreProd
Release - смотрит на API Prod
для iOS проблематично делать переключение серверов, ибо для пушей нужны разные сертификаты