Official Webhook Documentation:
https://support.atlassian.com/bitbucket-cloud/docs/manage-webhooks/
Event Types:
https://support.atlassian.com/bitbucket-cloud/docs/event-payloads/
Building the Webhook Controller
These concepts are presented in a format native to Pimcore, however the concept is a generalization and can easily be translated into other PHP frameworks with minimal effort.
namespace App\Controller; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\Routing\Annotation\Route; use Pimcore\Controller\FrontendController; class WebhookController extends FrontendController { /** * @Route("/webhook", name="Webhook") */ public function WebhookAction( Request $request ) { // Check for a recognized network... if( !self::GetNetworkValid( $request ) ) { return $this->json( [ 'error' => 'Request made from an unrecognized network.' ] ); } // Process the webhook event... if( self::WebhookPush( $request ) ) { return $this->json( [ 'status' => 'ok' ] ); } return $this->json( [ 'error' => 'Could not process event.' ] ); } }
In this sample block, we define the namespace, library usage, and structure of the Controller class. We also define a public method and bind the /webhook
URI to it using Annotations. There are two calls to static methods in the same class (self::
), GetNetworkValid
and WebhookPush
. Ultimately, this function returns one of three possible states, an failure state, a success state, and an undefined state.
GetNetworkValid
use Symfony\Component\HttpFoundation\IpUtils; ... private static function GetNetworkValid( $request ) { // Retrieve the remote IP making the request... $client_ip = $request->getClientIp(); // Retrieve networks from the network discovery URL... $json = json_decode( file_get_contents( 'https://ip-ranges.atlassian.com/' ), true ); if( !$json || empty( $json[ 'items' ] ) ) return false; // Compile a list cidr ranges... $cidrs = []; foreach( $json[ 'items' ] as $item ) { $cidrs[] = $item[ 'cidr' ]; } return IpUtils::checkIp( $client_ip, $cidrs ); } ...
The GetNetworkValid
method takes a Symfony\Component\HttpFoundation\Request
object and extracts the IP from the remote resource, comparing it to a live-list of IP ranges supplied by Atlassian, using the IpUtils
library. This is a Quality of Life feature that prevents the need for manually managing allowed IP ranges and CIDRs.
WebhookPush
use Pimcore\Model\DataObject\WebhookPush; ... private static function WebhookPush( $request ) { // Decode the JSON payload... if( $json = json_decode( $request->getContent(), true ) ) { // Create a new ORM object... $item = new WebhookPush(); // Set headers... $item->setXEventKey( $request->headers->get( 'X-Event-Key' ) ); $item->setXHookUUID( $request->headers->get( 'X-Hook-UUID' ) ); $item->setXRequestUUID( $request->headers->get( 'X-Request-UUID' ) ); $item->setXAttemptNumber( $request->headers->get( 'X-Attempt-Number' ) ); // Set fields... $item->setActor( json_encode( $json[ 'actor' ] ) ); $item->setRepository( json_encode( $json[ 'repository' ] ) ); $item->setPush( json_encode( $json[ 'push' ] ) ); // Store it! $item->setParentId( 1 ); $item->save(); return true; } return false; } ...
The WebhookPush
static method takes the request and forms a Pimcore DataObject from the request. This can be dumped to a log file instead if logging is all that is needed.
Topics
## PAGE REGION UNDER MAINTENANCE ##
Related Articles
## PAGE REGION UNDER MAINTENANCE ##