Ostatnimi czasy miałem okazję integrować rozwijaną aplikację z zewnętrznym systemem za pomocą mechanizmu webhooks. W tym artykule przedstawię Ci, jak pracowałem nad rozwojem tej integracji. Pokażę Ci, jak możesz sprawdzić jakie dane wysyła do Ciebie zewnętrzny system oraz jak na lokalnej maszynie możesz przetestować integrację ze swoją aplikacją.
Na samym początku jednak warto napisać kilka słów o tym, czym są webhooki. Mechanizm webhooks pozwala na komunikację między aplikacją implementującą go a wskazanym adresem. Webhooki można skonfigurować tak, by były wywoływane w momencie zaistnienia konkretnych zdarzeń w systemie. W momencie zaistnienia w systemie wskazanego zdarzenia, pod wskazany adres wysłane jest zapytanie HTTP z danymi dotyczącymi tego zdarzenia. Ponieważ webhooki służą do przesyłania danych, często będzie to zapytanie typu POST, gdzie body
zapytania będzie zawierało dane. Mechanizm ten będzie oczekiwał zwrócenia odpowiedniego statusu odpowiedzi, by mieć pewność, że webhook został poprawnie odebrany. Warto dopilnować, by zapytania były poprawnie odbierane, ponieważ często webhooki implementują mechanizm powtarzania zapytań na wypadek chwilowej niedostępności odbiorcy zapytania. Lista dostępnych zdarzeń zwykle jest ściśle powiązana z biznesowym zastosowaniem aplikacji implementującej ten mechanizm.
Webhooki można wykorzystywać do synchronizacji danych w dwóch aplikacjach. Przykładowo, mając aplikację, która korzysta z zewnętrznego systemu sprzedażowego, można wykorzystać webhooki do aktywacji zakupionej usługi, gdy płatność zostanie zaakceptowana. Mechanizm ten można również wykorzystać w automatyzacji pracy i procesów, np. CI/CD. W projekcie, w którym obecnie pracuję, każdy wysłany commit na branch z powiązanym otwartym pull requestem to zdarzenie obsługiwane przez webhooki, które wysyłają informację do bota. Następnie bot uruchamia listę checków niezbędnych, by pull request mógł zostać zmergeowany. Do checków należy sprawdzenie poprawności zmian w kodzie infrastruktury (Terraform) czy poprawność wersji zmienionych paczek. W podobny sposób można przygotować np. systemy powiadomień zintegrowane ze Slackiem czy Discordem. Lista możliwości jest ogromna, a ogranicza nas jedynie wyobraźnia.
Testowanie webhooków w praktyce — analiza zawartości otrzymywanych danych
Pierwszym i najważniejszym źródłem informacji o danych wysyłanych przez mechanizm webhooks powinna być dokumentacja. Jednak teoria teorią a praktyka praktyką. Dokumentacja może być niekompletna, nieaktualna, nieczytelna i wszelkie inne „nie”, które przyjdą Ci do głowy. Dlatego oprócz dokumentacji, warto w praktyce sprawdzić, jakie dane i w jakim formacie wysyłane są w momencie występowania zdarzeń i wysyłania zapytań.
Do sprawdzenia zawartości wysyłanych zapytań można wykorzystać narzędzia online. Ja w swojej pracy wykorzystuję zwykle RequestBin lub Webhook.site.
W artykule do wysyłania webhooków będę wykorzystywał GitHuba i webhooki związane ze zmianami w jednym z moich prywatnych repozytoriów. Aby otrzymać adres do testów, wystarczy odwiedzić RequestBin i skopiować wygenerowany endpoint. GitHub umożliwia wysyłanie danych w dwóch formatach — JSON i x-www-form-encoded. Oprócz tego możliwe jest ustawienie wartości secret
. Wartość secret posłuży do podpisania wysyłanego webhooka. Dzięki podpisowi serwer docelowy będzie w stanie zweryfikować czy otrzymane zapytanie HTTP pochodzi od zweryfikowanego nadawcy i może zostać przetworzone. Bez tego mechanizmu ktoś łatwo mógłby podszyć się i wysłać dowolne zapytanie pod wskazany adres. Podpis webhooka przesyłany jest w formie hasha SHA-1 pod nagłówkiem x-hub-signature
i hasha SHA-256 pod nagłówkiem x-hub-signature-256
. GitHub w dokumentacji webhooków przedstawia przykłady kodów umożliwiających wygenerowanie sygnatury i porównaniu jej z tą wysłaną w zapytaniu.
Przy tworzeniu webhooka możliwe jest również zdefiniowanie jakie zdarzenia powinny triggerować webhook. Aby zoptymalizować liczbę wysyłanych zapytań, warto wybrać tylko te zdarzenia, które nas interesują.
Po stworzeniu webhooka jedyne co pozostaje to wykonać akcję, która striggeruje webhooka. W moim przypadku było to dodanie nowego issue w repozytorium. Po wykonaniu akcji w RequestBin pojawi się nowe zapytanie HTTP. Aplikacja umożliwia sprawdzenie wartości body i nagłówków zapytania.
Testowanie webhooków w praktyce — lokalna instancja aplikacji
RequestBin jaki jest każdy widzi i nie jest to szczególnie skomplikowane narzędzie. Zdecydowanie bardziej wolałbym się skupić na praktycznym testowaniu integracji z lokalną instancją aplikacji. Gdy aplikacja uruchomiona jest na lokalnej maszynie, problemem do rozwiązania jest brak dostępu do niej z zewnątrz. Aby rozwiązać ten problem, można skorzystać z tunelowania ruchu. Rozwiązaniem, z którego skorzystałem do postawienia tunelu jest ngrok. Zasada działania tego narzędzia sprowadza się do instalacji i konfiguracji klienta ngroka, który po uruchomieniu łączy się z chmurą ngrok. Chmura wystawia publicznie dostępny adres, którego odpytanie spowoduje przekierowanie ruchu do aplikacji działającej na lokalnej maszynie. Alternatywą dla ngroka jest localtunnel, jednak nie miałem zbyt wielu okazji, by skorzystać z niego w praktyce. Wzmiankę o nim pozostawię tu w ramach ciekawostki.
Do rozpoczęcia pracy z ngrokiem konieczne będzie założenie konta w serwisie. Po założeniu konta w dashboardzie należy wybrać platformę, z której korzystamy i zainstalować klienta ngroka. Następnie należy przekazać authtoken do konfiguracji klienta ngroka poleceniem ngrok config add-authtoken [token]
lub ręcznie zmodyfikować plik konfiguracyjny ngroka. Po tych krokach można przetestować, czy ngrok działa poprawnie wywołując komendę ngrok http 80
w wierszu poleceń. Jeśli wszystko przebiegło pomyślnie, powinien zostać wyświetlony adres publiczny tunelu (Forwarding
), Session Status online
oraz dodatkowe informacje takie jak powiązane konto czy wersja klienta.
Jako aplikację, która będzie integrowana z mechanizmem webhooków GitHuba, wykorzystałem boileplate dla Express.js, który przygotowałem i omówiłem w jednym z artykułów na blogu. Do aplikacji dodałem dodatkową ścieżkę /webhook
wykorzystującą metodę POST
w definicji routera w pliku server.ts
oraz dodałem pliki z handlerem i kontrolerem. Po zbudowaniu i uruchomieniu aplikacji na porcie 8000
jedyne co pozostaje, to ponownie uruchomić tunel poleceniem ngrok http localhost:8000
. Aby sprawdzić, czy aplikacja działa poprawnie, można udać się w przeglądarce pod adres [Forwarding]/hello/my-name
lub wykonać zapytanie GET dowolnym klientem HTTP. Oczekiwaną odpowiedzią będzie JSON zawierający listę gości. Wiedząc, że tunel działa wystarczy ustawić wskazany adres jako Payload URL ze ścieżką do zasobu /webhook
w panelu konfiguracji webhooków Githuba. Jeśli operacja przebiegła pomyślnie, po dodaniu nowego issue w testowym repozytorium, na liście zapytań powinno pojawić się nowe zapytanie typu POST
.
Mogłeś(aś) już pewnie zauważyć, że każda inicjalizacja tunelu powoduje wystawienie go pod innym adresem. Jest to uciążliwe, ponieważ wymaga częstego aktualizowania URL-a w konfiguracji webhooków. Na szczęście łatwo można rozwiązać ten problem. W panelu ngroka w zakładce Cloud Edge w sekcji Domains wystarczy wygenerować domenę i przekazać ją przy inicjalizcji tunelu poleceniem ngrok http --domain=my-fancy-domain.ngrok-free.app localhost:8000
. Dzięki temu, przy każdym uruchomieniu ngrok wykorzysta tę samą domenę.
Podsumowanie
Mechanizm webhooks to bardzo użyteczne rozwiązanie, który pozwala na znaczne zautomatyzowanie wielu procesów. Jak widzisz, testowanie go lokalnie nie jest szczególnie trudne. Mam nadzieję, że ten artykuł pomoże Ci, gdy będziesz chciał(a) z niego skorzystać i je przetestować. Daj znać w komentarzu, jakie integracje z webhookami w swojej pracy miałeś(aś) okazję stworzyć.
Swego czasu wpadłem na jeszcze jedno narzędzie, które może się przydać przy testowaniu hooków lokalnie – https://smee.io/
Fajne, szczególnie z uwagi na brak konieczności zakładania konta i dodatkowej konfiguracji.