mirror of
https://github.com/alchemy-fr/Phraseanet.git
synced 2025-10-16 14:33:14 +00:00
Add delivery results to application settings page
This commit is contained in:
@@ -19,6 +19,7 @@ use Alchemy\Phrasea\Model\Manipulator\ApiOauthTokenManipulator;
|
||||
use Alchemy\Phrasea\Model\Repositories\ApiAccountRepository;
|
||||
use Alchemy\Phrasea\Model\Repositories\ApiApplicationRepository;
|
||||
use Alchemy\Phrasea\Model\Repositories\ApiOauthTokenRepository;
|
||||
use Alchemy\Phrasea\Model\Repositories\WebhookEventDeliveryRepository;
|
||||
use Symfony\Component\HttpFoundation\JsonResponse;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
@@ -244,8 +245,12 @@ class DeveloperController extends Controller
|
||||
throw new AccessDeniedHttpException();
|
||||
}
|
||||
|
||||
$deliveries = $this->getWebhookDeliveryRepository()
|
||||
->findLastDeliveries($account->getApplication(), 10);
|
||||
|
||||
return $this->render('developers/application.html.twig', [
|
||||
"application" => $application,
|
||||
"deliveries" => $deliveries,
|
||||
"user" => $user,
|
||||
"token" => $token,
|
||||
]);
|
||||
@@ -298,4 +303,12 @@ class DeveloperController extends Controller
|
||||
{
|
||||
return $this->app['repo.api-applications'];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return WebhookEventDeliveryRepository
|
||||
*/
|
||||
private function getWebhookDeliveryRepository()
|
||||
{
|
||||
return $this->app['webhook.delivery_repository'];
|
||||
}
|
||||
}
|
||||
|
@@ -21,6 +21,10 @@ class WebhookServiceProvider implements ServiceProviderInterface
|
||||
$this->createAlias($app, 'webhook.delivery_repository', 'repo.webhook-delivery');
|
||||
$this->createAlias($app, 'webhook.delivery_manipulator', 'manipulator.webhook-delivery');
|
||||
|
||||
$app['webhook.delivery_payload_repository'] = $app->share(function ($app) {
|
||||
return $app['orm.em']->getRepository('Phraseanet:WebhookEventPayload');
|
||||
});
|
||||
|
||||
$app['webhook.processor_factory'] = $app->share(function ($app) {
|
||||
return new EventProcessorFactory($app);
|
||||
});
|
||||
@@ -32,7 +36,8 @@ class WebhookServiceProvider implements ServiceProviderInterface
|
||||
$app['webhook.event_repository'],
|
||||
$app['webhook.event_manipulator'],
|
||||
$app['webhook.delivery_repository'],
|
||||
$app['webhook.delivery_manipulator']
|
||||
$app['webhook.delivery_manipulator'],
|
||||
$app['webhook.delivery_payload_repository']
|
||||
);
|
||||
});
|
||||
|
||||
|
@@ -56,6 +56,11 @@ class WebhookEventDelivery
|
||||
*/
|
||||
private $created;
|
||||
|
||||
/**
|
||||
* @ORM\OneToOne(targetEntity="WebhookEventPayload", mappedBy="delivery")
|
||||
*/
|
||||
private $payload;
|
||||
|
||||
/**
|
||||
* @param \DateTime $created
|
||||
*
|
||||
@@ -163,4 +168,12 @@ class WebhookEventDelivery
|
||||
{
|
||||
return $this->event;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return WebhookEventPayload
|
||||
*/
|
||||
public function getPayload()
|
||||
{
|
||||
return $this->payload;
|
||||
}
|
||||
}
|
||||
|
127
lib/Alchemy/Phrasea/Model/Entities/WebhookEventPayload.php
Normal file
127
lib/Alchemy/Phrasea/Model/Entities/WebhookEventPayload.php
Normal file
@@ -0,0 +1,127 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of phrasea-4.1.
|
||||
*
|
||||
* (c) Alchemy <info@alchemy.fr>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Alchemy\Phrasea\Model\Entities;
|
||||
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
use Ramsey\Uuid\Uuid;
|
||||
|
||||
/**
|
||||
* @ORM\Table(name="WebhookEventPayloads")
|
||||
* @ORM\Entity(repositoryClass="Alchemy\Phrasea\Model\Repositories\WebhookEventPayloadRepository")
|
||||
*/
|
||||
class WebhookEventPayload
|
||||
{
|
||||
/**
|
||||
* @ORM\Column(type="guid")
|
||||
* @ORM\Id
|
||||
* @ORM\GeneratedValue(strategy="NONE")
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $id;
|
||||
|
||||
/**
|
||||
* @ORM\OneToOne(targetEntity="WebhookEventDelivery")
|
||||
* @ORM\JoinColumn(name="delivery_id", referencedColumnName="id")
|
||||
*/
|
||||
private $delivery;
|
||||
|
||||
/**
|
||||
* @ORM\Column(type="text", name="request")
|
||||
* @var string
|
||||
*/
|
||||
private $requestPayload;
|
||||
|
||||
/**
|
||||
* @ORM\Column(type="text", name="response")
|
||||
* @var string
|
||||
*/
|
||||
private $responsePayload;
|
||||
|
||||
/**
|
||||
* @ORM\Column(type="integer", name="status")
|
||||
* @var int
|
||||
*/
|
||||
private $statusCode;
|
||||
|
||||
/**
|
||||
* @ORM\Column(type="text")
|
||||
* @var string
|
||||
*/
|
||||
private $headers;
|
||||
|
||||
/**
|
||||
* @param WebhookEventDelivery $eventDelivery
|
||||
* @param string $requestPayload
|
||||
* @param string $responsePayload
|
||||
* @param int $statusCode
|
||||
* @param string $headers
|
||||
*/
|
||||
public function __construct(WebhookEventDelivery $eventDelivery, $requestPayload, $responsePayload, $statusCode, $headers)
|
||||
{
|
||||
$this->id = Uuid::uuid4()->toString();
|
||||
|
||||
$this->delivery = $eventDelivery;
|
||||
$this->requestPayload = $requestPayload;
|
||||
$this->responsePayload = $responsePayload;
|
||||
$this->statusCode = $statusCode;
|
||||
$this->headers = $headers;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getId()
|
||||
{
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return WebhookEventDelivery
|
||||
*/
|
||||
public function getDelivery()
|
||||
{
|
||||
return $this->delivery;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getRequestPayload()
|
||||
{
|
||||
return $this->requestPayload;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getResponsePayload()
|
||||
{
|
||||
return $this->responsePayload;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function getStatusCode()
|
||||
{
|
||||
return $this->statusCode;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getResponseHeaders()
|
||||
{
|
||||
return $this->headers;
|
||||
}
|
||||
}
|
@@ -11,6 +11,7 @@
|
||||
|
||||
namespace Alchemy\Phrasea\Model\Repositories;
|
||||
|
||||
use Alchemy\Phrasea\Model\Entities\ApiApplication;
|
||||
use Alchemy\Phrasea\Model\Entities\WebhookEventDelivery;
|
||||
use Doctrine\ORM\EntityRepository;
|
||||
|
||||
@@ -22,6 +23,10 @@ use Doctrine\ORM\EntityRepository;
|
||||
*/
|
||||
class WebhookEventDeliveryRepository extends EntityRepository
|
||||
{
|
||||
|
||||
/**
|
||||
* @return WebhookEventDelivery[]
|
||||
*/
|
||||
public function findUndeliveredEvents()
|
||||
{
|
||||
$qb = $this->createQueryBuilder('e');
|
||||
@@ -34,4 +39,22 @@ class WebhookEventDeliveryRepository extends EntityRepository
|
||||
|
||||
return $qb->getQuery()->getResult();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ApiApplication $apiApplication
|
||||
* @param int $count
|
||||
* @return WebhookEventDelivery[]
|
||||
*/
|
||||
public function findLastDeliveries(ApiApplication $apiApplication, $count = 10)
|
||||
{
|
||||
$qb = $this->createQueryBuilder('e');
|
||||
|
||||
$qb
|
||||
->where('e.application = :app')
|
||||
->setMaxResults(max(0, (int) $count))
|
||||
->orderBy('e.created', 'DESC')
|
||||
->setParameters([ 'app' => $apiApplication ]);
|
||||
|
||||
return $qb->getQuery()->getResult();
|
||||
}
|
||||
}
|
||||
|
@@ -0,0 +1,27 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of phrasea-4.1.
|
||||
*
|
||||
* (c) Alchemy <info@alchemy.fr>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Alchemy\Phrasea\Model\Repositories;
|
||||
|
||||
use Alchemy\Phrasea\Model\Entities\WebhookEventPayload;
|
||||
use Doctrine\ORM\EntityRepository;
|
||||
|
||||
class WebhookEventPayloadRepository extends EntityRepository
|
||||
{
|
||||
|
||||
public function save(WebhookEventPayload $payload)
|
||||
{
|
||||
$this->_em->persist($payload);
|
||||
$this->_em->persist($payload->getDelivery());
|
||||
|
||||
$this->_em->flush([ $payload, $payload->getDelivery() ]);
|
||||
}
|
||||
}
|
@@ -0,0 +1,40 @@
|
||||
<?php
|
||||
|
||||
namespace Alchemy\Phrasea\Setup\DoctrineMigrations;
|
||||
|
||||
use Doctrine\DBAL\Schema\Schema;
|
||||
|
||||
/**
|
||||
* Auto-generated Migration: Please modify to your needs!
|
||||
*/
|
||||
class Version20161013115559 extends AbstractMigration
|
||||
{
|
||||
|
||||
public function isAlreadyApplied()
|
||||
{
|
||||
return $this->tableExists('Orders');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Schema $schema
|
||||
*/
|
||||
public function doUpSql(Schema $schema)
|
||||
{
|
||||
// this up() migration is auto-generated, please modify it to your needs
|
||||
$this->abortIf($this->connection->getDatabasePlatform()->getName() != 'mysql', 'Migration can only be executed safely on \'mysql\'.');
|
||||
|
||||
$this->addSql("CREATE TABLE WebhookEventPayloads (id CHAR(36) NOT NULL COMMENT '(DC2Type:guid)', delivery_id INT DEFAULT NULL, request LONGTEXT NOT NULL, response LONGTEXT NOT NULL, status INT NOT NULL, headers LONGTEXT NOT NULL, UNIQUE INDEX UNIQ_B949629612136921 (delivery_id), PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8 COLLATE utf8_unicode_ci ENGINE = InnoDB;");
|
||||
$this->addSql("ALTER TABLE WebhookEventPayloads ADD CONSTRAINT FK_B949629612136921 FOREIGN KEY (delivery_id) REFERENCES WebhookEventDeliveries (id);");
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Schema $schema
|
||||
*/
|
||||
public function doDownSql(Schema $schema)
|
||||
{
|
||||
// this down() migration is auto-generated, please modify it to your needs
|
||||
$this->abortIf($this->connection->getDatabasePlatform()->getName() != 'mysql', 'Migration can only be executed safely on \'mysql\'.');
|
||||
|
||||
$this->addSql('DROP TABLE WebhookEventPayloads');
|
||||
}
|
||||
}
|
@@ -14,15 +14,18 @@ namespace Alchemy\Phrasea\Webhook;
|
||||
use Alchemy\Phrasea\Model\Entities\ApiApplication;
|
||||
use Alchemy\Phrasea\Model\Entities\WebhookEvent;
|
||||
use Alchemy\Phrasea\Model\Entities\WebhookEventDelivery;
|
||||
use Alchemy\Phrasea\Model\Entities\WebhookEventPayload;
|
||||
use Alchemy\Phrasea\Model\Manipulator\WebhookEventDeliveryManipulator;
|
||||
use Alchemy\Phrasea\Model\Manipulator\WebhookEventManipulator;
|
||||
use Alchemy\Phrasea\Model\Repositories\ApiApplicationRepository;
|
||||
use Alchemy\Phrasea\Model\Repositories\WebhookEventDeliveryRepository;
|
||||
use Alchemy\Phrasea\Model\Repositories\WebhookEventPayloadRepository;
|
||||
use Alchemy\Phrasea\Model\Repositories\WebhookEventRepository;
|
||||
use Guzzle\Batch\BatchBuilder;
|
||||
use Guzzle\Common\Event;
|
||||
use Guzzle\Http\Client;
|
||||
use Guzzle\Http\Message\EntityEnclosingRequestInterface;
|
||||
use Guzzle\Http\Message\Request;
|
||||
use Guzzle\Http\Message\Response;
|
||||
use Guzzle\Plugin\Backoff\BackoffPlugin;
|
||||
use Guzzle\Plugin\Backoff\CallbackBackoffStrategy;
|
||||
use Guzzle\Plugin\Backoff\CurlBackoffStrategy;
|
||||
@@ -62,26 +65,35 @@ class WebhookInvoker implements LoggerAwareInterface
|
||||
* @var LoggerInterface
|
||||
*/
|
||||
private $logger;
|
||||
|
||||
/**
|
||||
* @var EventProcessorFactory
|
||||
*/
|
||||
private $processorFactory;
|
||||
|
||||
/**
|
||||
* @var WebhookEventRepository
|
||||
*/
|
||||
private $eventRepository;
|
||||
|
||||
/**
|
||||
* @var WebhookEventManipulator
|
||||
*/
|
||||
private $eventManipulator;
|
||||
|
||||
/**
|
||||
* @var WebhookEventPayloadRepository
|
||||
*/
|
||||
private $eventPayloadRepository;
|
||||
|
||||
/**
|
||||
* @param ApiApplicationRepository $applicationRepository
|
||||
* @param EventProcessorFactory $processorFactory
|
||||
* @param WebhookEventRepository $eventRepository
|
||||
* @param WebhookEventManipulator $eventManipulator
|
||||
* @param WebhookEventDeliveryManipulator $eventDeliveryManipulator
|
||||
* @param WebhookEventDeliveryRepository $eventDeliveryRepository
|
||||
* @param WebhookEventDeliveryManipulator $eventDeliveryManipulator
|
||||
* @param WebhookEventPayloadRepository $eventPayloadRepository
|
||||
* @param Client $client
|
||||
*
|
||||
* @todo Extract classes to reduce number of required dependencies
|
||||
@@ -93,6 +105,7 @@ class WebhookInvoker implements LoggerAwareInterface
|
||||
WebhookEventManipulator $eventManipulator,
|
||||
WebhookEventDeliveryRepository $eventDeliveryRepository,
|
||||
WebhookEventDeliveryManipulator $eventDeliveryManipulator,
|
||||
WebhookEventPayloadRepository $eventPayloadRepository,
|
||||
Client $client = null
|
||||
) {
|
||||
$this->applicationRepository = $applicationRepository;
|
||||
@@ -101,6 +114,8 @@ class WebhookInvoker implements LoggerAwareInterface
|
||||
$this->eventManipulator = $eventManipulator;
|
||||
$this->eventDeliveryManipulator = $eventDeliveryManipulator;
|
||||
$this->eventDeliveryRepository = $eventDeliveryRepository;
|
||||
$this->eventPayloadRepository = $eventPayloadRepository;
|
||||
|
||||
$this->client = $client ?: new Client();
|
||||
$this->logger = new NullLogger();
|
||||
|
||||
@@ -216,24 +231,41 @@ class WebhookInvoker implements LoggerAwareInterface
|
||||
$eventProcessor = $this->processorFactory->getProcessor($event);
|
||||
$data = $eventProcessor->process($event);
|
||||
|
||||
// Batch requests
|
||||
$batch = BatchBuilder::factory()
|
||||
->transferRequests(10)
|
||||
->build();
|
||||
|
||||
foreach ($targets as $thirdPartyApplication) {
|
||||
$delivery = $this->eventDeliveryManipulator->create($thirdPartyApplication, $event);
|
||||
|
||||
// append delivery id as url anchor
|
||||
// Append delivery id as url anchor
|
||||
$uniqueUrl = $this->buildUrl($thirdPartyApplication, $delivery);
|
||||
|
||||
// create http request with data as request body
|
||||
$batch->add($this->client->createRequest('POST', $uniqueUrl, [
|
||||
// Create http request with data as request body
|
||||
$request = $this->client->createRequest('POST', $uniqueUrl, [
|
||||
'Content-Type' => 'application/vnd.phraseanet.event+json'
|
||||
], json_encode($data)));
|
||||
], json_encode($data));
|
||||
|
||||
$requestBody = $request instanceof EntityEnclosingRequestInterface ? $request->getBody() : '';
|
||||
|
||||
try {
|
||||
$response = $request->send();
|
||||
|
||||
$responseBody = $response->getBody(true);
|
||||
$statusCode = $response->getStatusCode();
|
||||
$headers = $this->extractResponseHeaders($response);
|
||||
}
|
||||
catch (\Exception $exception) {
|
||||
$responseBody = $exception->getMessage();
|
||||
$statusCode = -1;
|
||||
$headers = '';
|
||||
}
|
||||
|
||||
$batch->flush();
|
||||
$deliveryPayload = new WebhookEventPayload(
|
||||
$delivery,
|
||||
$requestBody,
|
||||
$responseBody,
|
||||
$statusCode,
|
||||
$headers
|
||||
);
|
||||
|
||||
$this->eventPayloadRepository->save($deliveryPayload);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -245,4 +277,16 @@ class WebhookInvoker implements LoggerAwareInterface
|
||||
{
|
||||
return sprintf('%s#%s', $application->getWebhookUrl(), $delivery->getId());
|
||||
}
|
||||
|
||||
private function extractResponseHeaders(Response $response)
|
||||
{
|
||||
$headerCollection = $response->getHeaders()->toArray();
|
||||
$headers = '';
|
||||
|
||||
foreach ($headerCollection as $name => $value) {
|
||||
$headers .= sprintf('%s: %s', $name, $value) . PHP_EOL;
|
||||
}
|
||||
|
||||
return trim($headers);
|
||||
}
|
||||
}
|
||||
|
@@ -19,16 +19,16 @@
|
||||
{% block content_account %}
|
||||
<div class="row-fluid">
|
||||
<div class="span12">
|
||||
<h1>{{ "Application" | trans }}</h1>
|
||||
<h1>{{ "Application" | trans }} - <strong><a class="link" href="{{ path("developers_application", {"application" : application.getId()}) }}">{{ application.getName() }}</a></strong></h1>
|
||||
<input type="hidden" value="{{ application.getId() }}" name="app_id"/>
|
||||
|
||||
<div>
|
||||
<div><strong><a class="link" href="{{ path("developers_application", {"application" : application.getId()}) }}">{{ application.getName() }}</a></strong></div>
|
||||
<div>{{ application.getDescription() }}</div>
|
||||
</div>
|
||||
|
||||
<br />
|
||||
|
||||
<h1>{{ "settings OAuth" | trans }}</h1>
|
||||
<p>{{ "Les parametres oauth de votre application." | trans }}</p>
|
||||
|
||||
<table id="app-oauth-setting" class="table table-condensed table-bordered">
|
||||
<tbody>
|
||||
@@ -121,9 +121,39 @@
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
{% if deliveries | length > 0 %}
|
||||
<h1>{{ "Derniers envois" | trans }}</h1>
|
||||
<p> {{ "Résultats des derniers envois effectués pour cette application" | trans }}</p>
|
||||
|
||||
<table id="app-access-delivery-log" class="table table-condensed table-bordered">
|
||||
<thead>
|
||||
<tr>
|
||||
<th> </th>
|
||||
<th>ID</th>
|
||||
<th>Type</th>
|
||||
<th>Date</th>
|
||||
<th>Status code</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for delivery in deliveries %}
|
||||
<tr>
|
||||
<td>{{ delivery.isDelivered ? 'OK' : 'KO' }}</td>
|
||||
<td style="font-family: monospace">{{ delivery.payload ? delivery.payload.id : '-' }}</td>
|
||||
<td>{{ delivery.webhookEvent.type }}</td>
|
||||
<td>{{ delivery.created | date }}</td>
|
||||
<td>{{ delivery.payload ? delivery.payload.statusCode : '-' }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
{% endif %}
|
||||
|
||||
<div class="form-actions">
|
||||
<a class="btn btn-primary" href="{{ path("developers_applications") }}">{{ "boutton::retour" | trans }}</a>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
Reference in New Issue
Block a user