Remote observer

If the project went beyond the scope of the local machine, most likely you will have to integrate with some third-party systems.


I want to consider the case when the mentioned external system wants to receive notifications of any changes in our system. For example, updating a product catalog.


Task


There is a trading platform that provides access to its product base through WEB services. The partners of the site want to know about changes in the database as soon as possible.


Solution option


We know all our partners, we can request documentation for their software.


You can implement work with the API of our partners, and when changing the product catalog, notify them directly.


With this approach, each new client will have to be connected individually. If the partner has changed something in the software, resources will be required to restore functionality. In general, additional expenses and an extra zone of responsibility.


I really do not want to establish such tight ties. Therefore, we will do the following.


Let's take the โ€œobserverโ€ pattern as the basis. Let our colleagues subscribe to events and receive the notifications they need.


Implementation


Subscription records must be kept somewhere. The type of storage will remain on the conscience of the developer. Consider what needs to be stored.


  • Event. There can be a lot of types of events and in order not to send alerts to everyone, you need to know who has subscribed to what.
  • URL. . HTTP- URL. , .
  • . , ( , , ). , . - . .

, PHP.


, POST- URL. , b2b.api.my-soft.ru/event-subscription. URL event( ).


( Laravel):


public function subscribe()
{
    $request = $this->getRequest();
    $eventName = $request->input('event');
    $url = $request->input('callback');

    $validator = \Validator::make([
        'url' => $url,
        'event' => $eventName
    ], [
        'url' => 'url|required',
        'event' => 'required'
    ]);

    if ($validator->fails()) {
        throw new BadRequestHttpException($validator->errors()->first());
    }

    $repository = $this->getRepository();
    if (!$repository->eventAvailable($eventName)) {
        throw new BadRequestHttpException(trans('api.error.eventSubscription.wrongEvent'));
    }

    if (!$repository->canAddEvent($eventName)) {
        throw new BadRequestHttpException(trans('api.error.eventSubscription.maxCallbacks'));
    }

    $model = $repository->createModel([
        'client_id' => $request->attributes->get('client')->id,
        'event' => $eventName,
        'url' => $url
    ]);
    if ($repository->save($model)) {
        return $this->response($model);
    }
    throw new \Exception();
}

:


  • ,
  • ,
  • ( canAddEvent)
  • ,

.


. , . , .. . .


, , .


.


, , . , , .


, . .


$subscribersRepository->with(['event' => $event->getEventName()])->getActive()->each(function ($model) use ($event) {
    $this->dispatch(new \Commands\RemoteCallback(
        $model->id,
        $model->url,
        $event->getData()->toArray()
    ));
});

.


RemoteCallback :


public function handle(EventSubscriptionRepository $subscriptionRepository)
{
    $client = new \Guzzle\Http\Client();
    $res = $client->post($this->url, [], $this->data, ['connect_timeout' => 3, 'timeout' => 3]);
    try {
        if ($res->send()->getStatusCode() !== 200) {
            throw new \Exception();
        }
        $subscriptionRepository->dropErrors($this->subscriptionId);
    } catch (\Exception $e) {
        $subscriptionRepository->incrementError($this->subscriptionId);
    }
}

. POST- URL. , , .


. . HTTP != 200, . , 3 3 . , .


During the processing of a subscription request, the possibility of this is checked (canAddEvent method). It can be anything, but in my case the limit on the number of listeners is checked. No more than 3 for each type of event.


Plus, of course, authorization is required to work with such API methods.


That's basically it. The simplest option is described. If necessary, you can add support for SOAP or socket connections or something like that. And you need to re-send the message if the response was not successful.


All Articles