Чего ожидать
Цель – показать разработчикам, с какими проблемами сталкиваются пользователи их API на примере работы с различными CRM-системами.
В целях защиты своего лица, я не буду афишировать названия участников данной статьи.
Так же, я — не являюсь программистом маминой подруги, поэтому не стоит принимать мои умозаключения за единственно верный вариант.
Подводка
Жил да был, один преисполненный надеждами мальчик. Не было у этого мальчика в жизни ничего, кроме работы в технической поддержке программ по бухгалтерии. Долго он варился в этом котле, пока в один день, не сорвало крышу у него, да послал он главного черта-хозяина и ушел в поисках перспектив.
Мальчик всегда грезил о том, как он станет крутым разработчиком, будет зарабатывать деньги несусветные, да желательно в валюте басурманской. И потому, параллельно с работой, занимался созданием своего сайта, на технологиях невиданных, да с фичами неслыханными для тех лет. Благодаря этому, он смог отыскать себе нового хозяина, который дал ему работу в любимом деле, с зарплатой добротной, да перспективами несусветными.
С тех пор, жил мальчик, поживал, да добра наживал. И не знал он бед и горестей… или все-таки знал? Давайте разбираться!
Я устроился в компанию, занимающуюся бизнес-аналитикой. А какая может быть аналитика без данных? Поэтому в компании была отдельная команда, а затем и отдел, который занимался сбором данных с сайтов клиентов, для построения графиков выручки с рекламных каналов и прочих источников. Если говорить о конкретике, то моя работа состояла в установке на сайты нашего счетчика, для сбора данных о посетителе, и создании заявок с форм, онлайн чатов, обратных звонков и т.д. в их CRM-системах. Из которых, в дальнейшем, наша аналитика подтягивала информацию о статусе и сумме продажи.
И вроде бы ничего сложного тут нет, если учесть, что создание заявок в CRM было упрощено посредством обертки нашей системы, которая с помощью 1 запроса создавала заявку и клиента, в том числе и брала на себя авторизацию в сторонних сервисах. Но как вы должны понимать, подобная логика гибкостью не отличается. И если клиенту нужно было что-то нестандартное, то уже необходимо было «отключать комфорт-режим» и засучив рукава, напрямую работать с API нужной системы. И порой, работая с очередной проблемной API,
Проблема №1: недостаток методов/данных
Пожалуй, самая частая проблема, которую я встречал на своем веку – это отсутствие возможности получить нужные мне данные, которые есть в интерфейсе. Постоянно возникали и продолжают возникать конфликты с клиентами, из-за того, что мы не можем получить что-то, что они каждый день видят на экранах своих мониторов.
К примеру, пару лет назад, к нам пришел довольно крупный клиент, которому потребовалось сохранять в CRM файлы которые ему отправляют клиенты с форм на сайте. Рядовая задача, а возможности реализации не было, и нет по сей день!
Отказ для клиента был неприемлем, и в конце-концов нам пришлось загружать файлы, эмулируя JS-запросы из интерфейса.
Должен заметить, что у этой CRM, авторизация в API, авторизирует в том числе и в интерфейсе, что, как по мне, довольно странное решение. Впрочем, благодаря этому, нам не пришлось эмулировать обычную авторизацию. Или, так и было задумано…?
Другой случай с этой же CRM, случился не так давно. Необходимо было ставить ответственным за заявку менеджера который активен в данный момент. И как вы уже поняли, API нам не возвращает информацию об активности менеджеров. Но парадокс в том, что их JS API — возвращает. В итоге пришлось писать JS-приложение, своеобразного посредника, которой сообщал серверу об активных менеджерах в данный момент.
Решение:
У нас в команде, принято создавать для интерфейса публичный API-метод, и взаимодействовать с сервером уже через него. Это позволяет устранить проблему несоответствия технических возможностей интерфейса и публичного API.
Проблема №2: отсутствие единого стиля запросов/ответов
Очень часто встречаемая проблема даже у крупных западных CRM. Допустим нам нужно получить список заказов за месяц, мы узнаем, что у системы присутствует ограничение на кол-во элементов в выгрузке. И чтобы перемещаться по страницам нужно использовать параметр offset. Окей, мы реализовали метод загрузки заказов, и отдельно, вспомогательный метод постраничной загрузки.
Теперь, нам необходимо выгрузить список клиентов, реализовываем новый метод вместе с методом постраничной загрузки написанным ранее, и…. получаем ошибку. Потому что PM с разработчиками решили, что offset звучит слишком просто, пусть теперь это будет vid-offset.
Я конечно же утрирую, я не могу знать что происходило когда писали это апи. Но это ошибка как минимум PM-а, потому что он не уточнил как это уже реализовано в других методах. А так-же ошибка разработчиков, тех что писали новый метод, и тех что проверяли. Ибо теперь, все кто пользуются их API — вынуждены строить костыли в своих приложениях.
Решение:
Любой новый апи-метод, по возможности, должен соответствовать структуре уже существующих. Тех.лид(или тим.лид за неимением первого) могут составить регламент, своеобразный кодстайл, по которому необходимо разрабатывать апи, и в обязательном порядке ознакомить с ним всех разработчиков. Если проблема уже имеет место быть, то при возможности, лучше поставить костыль на своей стороне, чем вынуждать это делать тысячи своих клиентов на своих сторонах.
Проблема №3: плохая документация или её отсутствие
Документация — это справочник разработчика. Мы часто сталкиваемся с ситуациями, когда у нас спрашивают о возможности реализации той или иной фичи. И если мы не помним или не знаем ответа, то сразу открываем документацию. Найти информацию в хорошем справочнике — работы на пару минут, тогда как в перегруженном можно и полчаса копаться, и все равно однозначного ответа не получишь.
В некоторых запущенных случаях, её просто невозможно найти в открытом доступе. И если нужно получить актуальный список методов — необходимо обращаться в тех.поддержку. А потом еще нужно проверить, не появилось ли там чего новенького, что у нас пару клиентов просило…
Решение:
Я приведу несколько тезисов, которые как по мне, характеризуют качественную документацию:
- поиск по названию, описанию и параметрам методов
- корректное описание предназначения метода
- список принимаемых параметров с типом и описанием
- список возвращаемых параметров с типом и описанием
- пример запроса
- пример ответа
- возможные коды ошибок с описанием
- песочница, в которой можно в режиме конструктора «поиграться» с запросами
- историю изменений всех апи-методов для быстрого ознакомления
Проблема №4: авторизационные велосипеды
Вы знаете это прекрасное чувство, когда ты открываешь документацию новой системы, и там в разделе авторизации, описан порядок из 3 методов для получения ключа действительного только на 1 запрос...? Так вот, у меня его нет.
Для меня остается загадкой, почему разработчики отказываются от Oauth 2.0 в пользу своих велосипедов? И ладно, если авторизация ограничивается одним неизменным токеном, но вот целая авторизационная цепочка…
Решение:
Не стоит изобретать свой велосипед, когда уже есть готовые стандарты. Ведь наверняка, под эти стандарты, у разработчиков уже есть свои компоненты для простой авторизации.
Эпилог
Я рассказал о 4-х проблемах, с которыми сталкивался в начале своего пути и по сей день. Я попытался предоставить их решения, но хорошие они или нет — решать вам. В конце-концов у каждого из нас, есть своя архитектура мысли.
Так-же, благодарю всех тех, кто дочитал до сего момента! Это моя первая статья, и если она имела для вас информационную ценность, то я буду рад продолжить цикл статей, о моих страданиях с API.
И разумеется, буду рад услышать мнения экспертов в комментариях.
Всех с наступившим 2020 годом!
Комментарии (13)
fougasse
07.01.2020 00:11Проблема #4 может возникнуть там, где нет необходимости/возможности в OAuth и подобном, а а AAA нужен.
Вот и появляются API с множеством шагов.
Ну и конечно не всё ограничивается взаимодействием с пользователем, когда в ход идут готовые компоненты.
RouR
07.01.2020 00:23Часто просто есть два разных API — первое публичное, и второе внутреннее, для сайта. Второе меняют как и когда хотят, и в нем есть больше данных.
Dangetsu-PK Автор
07.01.2020 01:19Внутреннее апи — гибко, не спорю. Но, зачем поддерживать одновременно две апи, когда можно сделать все в рамках одной? Как правило, добавить данные в публичную апишку — проблем не составляет. А ситуаций когда нужно что-то кардинально изменить в ней, я даже как-то не встречал.
qrKot
07.01.2020 09:41+6Вот тут позвольте с вами не согласиться.
А ситуаций когда нужно что-то кардинально изменить в ней, я даже как-то не встречал
Это просто «недостаток опыта»))) (прошу не воспринимать, как критику)
Два API (внутренний и внешний) — очень и очень удобная штука, которая стала уже чуть ли не стандартом де-факто в случаях, когда есть 3rd-party (сторонний) потребитель этого самого API. Подход, собственно, такой: есть внутренний API, поверх которого строится вся кухня. Это вообще логично — максимально унифицировать подход к разработке и используемый инструментарий. И есть API внешний, доступный 3-й стороне, клиентам, потребителям и т.д. и т.п., который выглядит как своеобразная обертка над внутренним API.
Такой подход позволяет решить целую уйму проблем, таких как:
1. Релизный цикл. Внутренний АПИ имеет склонность к частым изменениям, на то он и внутренний, чтобы максимально быстро меняться в угоду скорости разработки зависящих от него компонент системы и частоты релизов. Логично предположить, что он изменяется часто, итеративно, временами даже спорадически, и не требует поддержки легаси-методов (при условии, что потребители внутреннего АПИ контролируются целиком вами). Т.е. наиболее подходящая модель релизов — роллинг. При этом роллинг-релиз модель во внешнем АПИ, поставляемом третьим лицам… Ну, такое себе. Надо иметь железобетонную уверенность, что эти третьи лица — люди уравновешенные, спокойные, флегматичные, не склонные к насилию, и, что важно, быть твердо уверенным, что у них нет вашего домашнего адреса. Внешнее АПИ должно а) никогда не ломаться, б) релизиться строго версионно с заявленным уровнем поддержки обратной совместимости, в) релизиться в предсказуемые сроки, г) должно быть строго аккуратно документировано в соответствии с версией (с поддержкой архива документации на поддерживаемые версии), д) иметь поддержку нескольких параллельных версий.
2. Декомпозиция тестирования. При наличии 2 АПИ вы имеете очевидный business-critical слой логики, который необходимо покрыть спецификациями и тестировать в соответствии с оными. Т.е. во внутреннем АПИ вы можете запилить метод, который при сложении двух чисел 2+2 будет возвращать 5 — это ваше право. Но ровно до тех пор, пока метод-обертка во внешнем АПИ возвращает 4. Суть разницы — во внутреннем АПИ костыли иметь можно, и они неизбежно появятся. Во внешнем — нельзя.
3. Безопасность. Безопасность, она, как говорится, превыше всего. Честно говоря, сама идея отдать наружу целиком АПИ, возвращающий, допустим, список банковских проводок клиента по переданному в запросе айдишнику и рулить доступом на уровне авторизации и ACL-ей всяких — мне лично идея очень не нравится. Внешний АПИ, имхо, должен иметь метод «покажи мне мой список проводок», который будет гарантированно фильтровать выдачу внутреннего по айдишнику/логину/ключу/whatever запросившего пользователя. Т.е. внешняя обертка позволяет программно отрезать возможность получения одним клиентом данных другого и исключает случаи, когда «криворукий манагер натыкал что-то в настройках прав в длинной менюшке, в которой он ничего не понимает и вообще на листочке так написано было».
4. Простота внешнего АПИ. Вот это тоже важно. Внутри вы можете изгаляться как угодно, пилить методы на 50 входных параметров и прочие важные вещи. Внешний АПИ должен быть простым и очевидным (если вы не твердо уверены, что у клиентов нет вашего домашнего адреса). Если вы поставляете АПИ третьим лицам — не поленитесь, запилите им удобный АПИ. Чтобы в методе получения списка своих операций из параметров были какие-то осмысленные фильтры по дате, например, или типу операции. Не надо в этот запрос требовать добавить логин, пароль, текущую фазу луны и номер кредитной карты — все эти данные уже получены вами при авторизации.Dangetsu-PK Автор
07.01.2020 14:53+1Т.е. я правильно понимаю, что в публичный апи, нельзя добавлять новые данные, даже если они никак не задевают уже существующие? А чем это обусловлено?
qrKot
08.01.2020 08:12Ну, не совсем правильно вы поняли. 2 вещи:
1. В публичный АПИ нельзя добавлять новые данные, даже если они никак не задевают уже существующие, не сменив при этом номер версии.
2. Как раз для таких вещей есть вещи вроде семантического версионирования. Версия номер x.y.z (мажорныйРелиз.минорныйРелиз.патч). Добавить новые данные, не сломав старые — это минорный релиз. В случае с АПИ, вероятно, патч можно убрать. Т.е. версия будет из 2 цифр: x.y
Допустим, у вас молодая, бурно развивающаяся апишка, версия которой на данный момент — пусть будет 3.15. Вы добавляете новые данные, не ломая старые — выпускаете версию 3.16. Всем хорошо.
До тех пор, пока потребитель, написавший свой клиент еще на версии 3.02, может работать с АПИ, не внося изменений в свой код — плюсуете вторую цифру. Как только с обновлением что-то сломалось — плюсуйте первую, и будьте добры поддерживать предыдущую версию до тех пор, пока клиенты (в разумные сроки) не смигрируют.
А обусловлено это тем, что АПИ — публичное. Им пользуется N потребителей. Нет, N мало, лучше K. И у каждого из них свой продукт, который пилится, и в который уже вложено некоторое количество человеко-часов, и от которого у владельца продукта есть определенные ожидания.
Перепилить продукт-потребитель стоит, допустим, X человеко-часов. Очевидно, миграция всех клиентов обойдется в X * K человеко-часов. Нехорошо вот так труд людей тратить.
Даже если вы ничего не сломали, в дальнейшем клиент, допустим, скажет: а вот у вас в документации написано «можно получить цвет глаз котенка вот этим методом АПИ, передав ему в качестве параметра АйДи необходимого котенка», а у меня не работает. А вы ему четко, взвешено и аргументировано: метод получения цвета глаз реализован в АПИ версии 3.17 и поддерживается с этого момента, а у вас клиентская библиотека версии 3.14. Т.е., заметьте, не «ну попробуйте обновиться, яхз», а «у вас версия библиотеки 3.14, которая не поддерживает фичу, появившуюся в версии 3.17, обновитесь до версии не ниже 3.17».
Или, допустим, я накопал библиотечку, которая над вашим АПИ обертка. И мне очень прямо важно уметь получать цвет глаз котят. Я смотрю в вашу доку, а там есть такой метод, интегрирую библиотечку — а там хрен вместо глаза.
А будь у вас номер версии, и будь в документации описано «поддерживается с версии 3.17, а параметр rgba|rgb|cmyk позволяет выбрать формат представления цвета с версии 3.19» я бы просто посмотрел, что библиотечка версии 3.14 и прошел бы мимо.
Короче, ВЕРСИОНИРУЙТЕ. Любой чих версионируйте — это экономит очень много сил как для вас, так и для вашего клиента.
fougasse
08.01.2020 00:20Потому, что один для клиентов, а другой для внутреннего пользования, ваш Кэп.
И клиентам совершенно не нужно знать то, что знает свой условный фронт.
trolley813
07.01.2020 00:33Кстати, авторизационные велосипеды лучше не делать еще и из-за потенциальных дыр в безопасности — потому же, почему не стоит делать кастомные алгоритмы шифрования/хеширования вместо стандартных.
DionizasA
07.01.2020 14:16Проблемы №1-3 лекго решаются исползуя peekdata.io или cube.dev компоненты. Кстати,- такой же подход используетсья Гууглом и Яндехом.
RaFaeL-NN
08.01.2020 14:17+1Подпись составляется по следующему алгоритму:
массив из передаваемых параметров (GET, POST, PUT, DELETE) сортируется по названию ключа по алфавиту;
из полученного массива формируется строка запроса (например, функция http_build_query в PHP), пример «from=DATEFROM&to=DATETO…»;
и далее — соединяется по формуле: строка = имя_метода строка_запроса md5( строка_запроса ), где «имя_метода» — строка запроса, начиная от домена (с указанием версии АПИ), до начала перечисления параметров, например — '/v1/sip/'
полученная строка хешируется по алгоритму sha1 с секретным ключом пользователя: хеш = hash( строка, секретный_ключ )
и далее хеш кодируется в base64 подпись = base64_encode( хеш )
Вот кто это придумал? Особенно поражает последний пункт, который из 40-символьной регистронезависимой строки вдруг делает 56-символьную регистрозависимую. API внешнее и для широкого пользованияsmarthomeblog
08.01.2020 17:33Есть еще поэротичнее варианты:
$message = $x_invoice .'V' . $x_amount .'I' . $x_iduser .'2' . $x_bank .'1' . $x_cpf .'H' . $x_bdate .'G' . $x_email .'Y' . $x_state . 'P'; $control = strtoupper(hash_hmac('sha256', pack('A*', $message), pack('A*', $secretKey)));
Причем в ряде методов добавление символов к полю отсутствует вообще. В каких-то есть только префикс. Короче мрак, если честно. Основное назначение API — ИМХО легкая интеграция с продуктом. А на деле все превращается в камасутру и пляски с бубном.
Кстати, зачем тут нужен pack? Чтобы не латинские символы поддерживать?
justhabrauser
Dangetsu-PK Автор
Возможно, я где-то переборщил со своим негодованием, но в целом я не ставил цели пожаловаться на кого-то. Даже названий CRM, на основе которых готовил статью, я не указывал…