Адмінка як юніт тест для HTTP API

Вітаю, колеги. Безліч сервісів в інтернет надають HTTP API для розробників. Є багато статей як це зробити правильно, не менша кількість оповідань як вийшло неправильно, і могутня купка критики що вийшло у інших. Гарне API зробити важко — він постійно намагається випасти з гаманця міллера, обзавестися циклічними залежностями серед сутностей і засунути бідного розробника в прокрустове ложе сценаріїв використання «як їх бачать розробники». Свої п'ять копійок додам і я — під катом забавний, але робочий спосіб, який ми використовуємо для приборкання нашого немаленького HTTP API



Кращі фреймворки і API витягуються з живих проектів

На просторах інтернету я часто зустрічаю твердження, що вдалі фреймворки і API були витягнуті з авторами працюючих, комерційних проектів. Як виник фреймворк Django? Автори робили безліч новинних сайтів. У якийсь момент вони зрозуміли, що частина коду переповзає з проекту в проект. Винесли цей код в окрему бібліотеку — вийшов веб фреймворк. Як виникло Amazon API? Робили інтерфейси взаємодії між внутрішніми сервісами. Через деякий час API стабілізувався, став змінюватися все менше і менше і, нарешті, його зробили доступним для зовнішніх клієнтів.

Витягаючи щось з живого проекту нам немає необхідності намагатися «на папері» розписати варіанти використання, що зазвичай виходить не дуже добре. Незважаючи на милиці «мозкових штурмів», діаграм і wiki, люди не дуже добре пристосовані до утримання в голові нових складних систем, і безліч спроб придумати складний API «на папері» закінчується повною переробкою через кілька місяців після релізу.

Але що робити, якщо проект є платформою, і ніяких «комерційних проектів», з яких можна витягти його частини, в природі поки не існує? Багато менеджери в таких випадках пропонують командам розробників робити «макети» потенційних рішень і розробляти API для макетів. На жаль, придуманий «з голови» макет найчастіше сильно відрізняється від того, що будуть створювати реальні користувачі. Не тому, що погано придумували, а з-за обмежень нашого мозку на утримуваний у фокусі уваги обсяг інформації.

Адмінка як еталонне додаток

Багато років тому, коли ми тільки починали створювати voximplant, у нас була велика стопка хотілок від клієнтів і розуміння того, що з першого разу хороше API зробити не вийде. Але дуже хотілося :). І під час чергового мозкового штурму народилася дивна ідея: зробити адмінку для розробників не на backend, як тоді робили для більшості проектів, а на frontend. І щоб адмінка для всіх своїх функцій використовувала той же HTTP API, що ми даємо нашим клієнтам.

Величезний плюс такого підходу полягає в тому, що дизайн API еволюціонує разом з адмінкою. На початку це чистий аркуш з кнопкою «додати користувача». Щоб кнопка працювала, ми робимо AJAX виклик і метод /AddUser з боку сервера. Додаємо поле введення ідентифікатора користувача — додаємо аргумент парний user_name для методу API — і так далі, поки не буде створена основна функціональність.

Безумовно, такий підхід підійде не для всіх. Розробка «софту» — дуже велика область. Є розробка по специфікації замовника, коли всі API цілком дизайнится спеціально навченої командою архітекторів. Є ситуації, коли API відображає лише невелику частину функціональності платформи, доступну для зовнішніх розробників «пісочницю». Є розробка за контрактом, коли все потрібно продумати і розрахувати заздалегідь, щоб спрогнозувати терміни і бюджет.

Тим не менш, нам цей спосіб дуже добре допоміг. І, наскільки я бачу в інтернеті, все більше і більше сервісів з розвиненим API дотримуються принципу «eat your own dog food», використовуючи ці API для розробки власних админок і сервісів. Поки у стартапу немає вичерпної документації та навчальних матеріалів на всі випадки життя, на питання клієнта «а як зробити так-то» завжди можна відповісти — «зробіть дії в адмінці і подивіться chrome dev tools які терміни і з якими аргументами вона виконує». Такий підхід працює і працює добре.

Подальша еволюція: як використовувати AttachPhoneNumber

Адмінка — штука хороша, але для багатьох сервісів вона дозволяє робити тільки найбільш популярні операції, тоді як «складні» кейси залишаються під капотом або доступні за API. В якості прикладу я покажу нашу функцію API/AttachPhoneNumber яка дозволяє орендувати номер телефону, щоб в подальшому за допомогою javascript сценаріїв управляти приходять на цей номер дзвінками. В адмінці при купівлі номери можна відфільтрувати доступні номери по країнам/типами/містам, вибрати потрібний номер або вказати кількість номерів для оренди, а потім натиснути на кнопку «купити»:



Якщо подивитися на нашу документацію для відповідного методу API, то можна легко знайти відповідність між тим, що ми бачимо в інтерфейсі і тим, що можна передати у функцію API:

  • Різні варіанти авторизації. Вони однакові для більшості методів API і описані документації.
  • phone_count передається кількість номерів, які ми хочемо орендувати за один запит від одного і до… скільки є.
  • country_code при передається код країни, для Росії це буде "UK", повний список можна подивитися… так, в тій же адмінці:


  • phone_category_name передається категорія номера: прямий міський, мобільний, 8-800 — і так далі. Список категорій також можна підглянути в адмінці або отримати для зазначеної країни з допомогою функції API GetPhoneNumberCategories — наприклад, для міських номерів Росії це буде "GEOGRAPHIC".
  • phone_region_id передається ідентифікатор регіону". Це якийсь внутрішній ідентифікатор, який використовують телекоми різних країн, що надають номери. Список традиційно можна підглянути, але краще все-таки отримати за допомогою спеціального API GetPhoneNumberRegions, яка повертає список доступних регіонів для зазначеної країни і категорії номера.
  • У телефонній адресації деяких країн регіону недостатньо, і для таких країн при купівлі декількох номерів у параметрі country_state передається ідентифікатор штату. Список штатів можна отримати з допомогою GetPhoneNumberCountryStates — для тих країн, де вони є.


Як можна бачити, що всі ці значення є відбиттям відповідних полів форми і додавалися по мірі додавання функціональності: спочатку була можливість купувати за номером, потім ми додали вибір кількості номерів, який потягнув за собою необхідність вказувати будь номери, що цікавлять клієнта, потім з'явилися партнери і можливість покупки за номером застаріла — і так далі.

Як отримати номер в Японії і відповісти на дзвінок

Практичний приклад, як творці додатків на нашій платформі можуть використовувати API, керуючись адмінкою як еталонної реалізацією. Припустимо, ми хочемо орендувати собі номер в Токіо і виконати javascript при дзвінку на номер. Виключно в демонстраційних цілях, звичайно ж.

Для початку нам знадобиться з'ясувати кількість доступних номерів — раптом Токіо закінчився? Для авторизації використовуємо ідентифікатор облікового запису і ключ з налаштувань адмінки voximplant, а для запиту кількості номерів — спеціально навчений метод GetPhoneNumbers:

curl "https://api.voximplant.com/platform_api/GetNewPhoneNumbers/?account_id=1&api_key=2&country_code=JP&phone_category_name=GEOGRAPHIC&phone_region_id=8384"


У відповідь отримує помилку авторизації json, в якому поле total_count містить кількість доступних номерів, на момент написання статті — 224 штуки. Номери у Токіо є в кількості. Для оренди номери використовуємо вже описаний метод AttachPhoneNumber точно так само, як це робить адмінка. Від попереднього запиту буде відрізнятися тільки назвою api endpoint і параметром phone_count, що вказує що ми хочемо орендувати рівно один номер:

curl "https://api.voximplant.com/platform_api/AttachPhoneNumber/?account_id=1&api_key=2&country_code=JP&phone_category_name=GEOGRAPHIC&phone_region_id=8384&phone_count=1"


Після кількох секунд роздумів ми отримаємо JSON з параметрами орендованого номери, виглядає він ось так:

{
"result":1,
"phone_numbers":[
{
"phone_number":"81345793488",
"verification_status":"VERIFIED",
"phone_id":9033
}
]
}


У відповіді ми можемо бачити сам номер (мені дістався 81345793488). У цьому номері "+81" — код Японії, «3» — код Токіо (зверніть увагу — він не співпадає з ідентифікатором регіоном для Токіо, яке ми ставили як «8384», тому що ідентифікатор регіону — це внутрішній ідентифікатор телекому) і «45793488» — власне сам номер. Щоб прийняти на нього дзвінок і зробити що-небудь корисне потрібно зв'язати його з одним з додатків voximplant — які теж можна створювати через API. Приклад такої команди, яка зв'яже орендований номер з додатком «foo» (так, у мене є такий додаток. Воно говорить «привіт» і вішає трубку):

curl "https://api.voximplant.com/platform_api/BindPhoneNumberToApplication/?account_id=1api_key=2&phone_number=81345793488&application_name=foo"


Сподіваємося, що цей невеликий кейс додасть у вашу скарбничку прийомів веб розробки невеликий трюк — можливо, коли-небудь в майбутньому такий підхід врятує вас від багатьох безсонних ночей, проведених за дизайном "ідеального api". В коментарях я традиційно готовий відповісти на неконструктивну критику, відповісти на каверзні запитання і просто поспілкуватися на тему дизайну і розробки API.

Джерело: Хабрахабр

0 коментарів

Тільки зареєстровані та авторизовані користувачі можуть залишати коментарі.