Skip to content
This repository has been archived by the owner on Jul 15, 2021. It is now read-only.

Commit

Permalink
Issue #30
Browse files Browse the repository at this point in the history
  • Loading branch information
Alexander Janssen authored and csarrazi committed Jul 10, 2015
1 parent 079fc43 commit 8dd5028
Show file tree
Hide file tree
Showing 5 changed files with 403 additions and 1 deletion.
12 changes: 12 additions & 0 deletions src/DependencyInjection/CsaGuzzleExtension.php
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,19 @@ private function processClientsConfiguration(array $config, ContainerBuilder $co
'service("csa_guzzle.description_factory").getDescription("%s")',
$name
)));
$serviceDefinition->addArgument(['process' => false]);
$serviceDefinition->addMethodCall('setSerializer', [new Reference('serializer')]);
$serviceDefinition->addMethodCall('addProcess');

$container->setDefinition(sprintf('csa_guzzle.service.%s', $name), $serviceDefinition);
$container->getDefinition('csa_guzzle.paramconverter.guzzle')->addMethodCall(
'addService',
[
sprintf('csa_guzzle.service.%s', $name),
new Reference(sprintf('csa_guzzle.service.%s', $name))
]
);

}
}
}
Expand Down
45 changes: 45 additions & 0 deletions src/GuzzleHttp/Command/Guzzle/GuzzleClient.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
<?php
/**
* Created by PhpStorm.
* User: AJanssen
* Date: 03-06-15
* Time: 14:35
*/

namespace Csa\Bundle\GuzzleBundle\GuzzleHttp\Command\Guzzle;

use Csa\Bundle\GuzzleBundle\GuzzleHttp\Command\Guzzle\Subscriber\ProcessResponse;
use GuzzleHttp\Command\Guzzle\GuzzleClient as AbstractGuzzleClient;
use JMS\Serializer\SerializerInterface;

class GuzzleClient extends AbstractGuzzleClient
{
/**
* @var SerializerInterface $serializer;
*/
protected $serializer;

/**
* @param SerializerInterface $serializer
*/
public function setSerializer(SerializerInterface $serializer)
{
$this->serializer = $serializer;
}


public function addProcess()
{
$this->getEmitter()->attach(
new ProcessResponse(
$this->getDescription(),
$this->serializer,
isset($config['response_locations'])
? $config['response_locations']
: []
)
);
}


}
229 changes: 229 additions & 0 deletions src/GuzzleHttp/Command/Guzzle/Subscriber/ProcessResponse.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,229 @@
<?php
/**
* Created by PhpStorm.
* User: AJanssen
* Date: 03-06-15
* Time: 14:37
*/

namespace Csa\Bundle\GuzzleBundle\GuzzleHttp\Command\Guzzle\Subscriber;

use GuzzleHttp\Command\Guzzle\DescriptionInterface;
use GuzzleHttp\Event\SubscriberInterface;
use GuzzleHttp\Message\ResponseInterface;
use GuzzleHttp\Command\Guzzle\Parameter;
use GuzzleHttp\Command\CommandInterface;
use GuzzleHttp\Command\Guzzle\ResponseLocation\JsonLocation;
use GuzzleHttp\Command\Event\ProcessEvent;
use GuzzleHttp\Command\Guzzle\ResponseLocation\ResponseLocationInterface;
use GuzzleHttp\Command\Guzzle\ResponseLocation\BodyLocation;
use GuzzleHttp\Command\Guzzle\ResponseLocation\StatusCodeLocation;
use GuzzleHttp\Command\Guzzle\ResponseLocation\ReasonPhraseLocation;
use GuzzleHttp\Command\Guzzle\ResponseLocation\HeaderLocation;
use GuzzleHttp\Command\Guzzle\ResponseLocation\XmlLocation;
use JMS\Serializer\SerializerInterface;

/**
* Class ProcessResponse
*
* @package Csa\Bundle\GuzzleBundle\GuzzleHttp\Command\Guzzle\Subscriber
*/
class ProcessResponse implements SubscriberInterface
{
/** @var ResponseLocationInterface[] */
private $responseLocations;

/** @var DescriptionInterface */
private $description;

/** @var SerializerInterface */
private $serializer;

/**
* @param \GuzzleHttp\Command\Guzzle\DescriptionInterface $description
* @param \JMS\Serializer\SerializerInterface $serializer
* @param array $responseLocations
*/
public function __construct(
DescriptionInterface $description,
SerializerInterface $serializer,
array $responseLocations = []
) {
static $defaultResponseLocations;
if (!$defaultResponseLocations) {
$defaultResponseLocations = [
'body' => new BodyLocation('body'),
'header' => new HeaderLocation('header'),
'reasonPhrase' => new ReasonPhraseLocation('reasonPhrase'),
'statusCode' => new StatusCodeLocation('statusCode'),
'xml' => new XmlLocation('xml'),
'json' => new JsonLocation('json')
];
}

$this->responseLocations = $responseLocations + $defaultResponseLocations;
$this->description = $description;
$this->serializer = $serializer;
}

public function getEvents()
{
return ['process' => ['onProcess']];
}

public function onProcess(ProcessEvent $event)
{
// Only add a result object if no exception was encountered.
if ($event->getException()) {
return;
}

$command = $event->getCommand();

// Do not overwrite a previous result
if ($event->getResult()) {
return;
}

$operation = $this->description->getOperation($command->getName());

// Add a default Model as the result if no matching schema was found.
if (!($modelName = $operation->getResponseModel())) {
$event->setResult([]);
return;
}

$model = $operation->getServiceDescription()->getModel($modelName);
if (!$model) {
throw new \RuntimeException("Unknown model: {$modelName}");
}

$event->setResult($this->visit($model, $event));
}

protected function visit(Parameter $model, ProcessEvent $event)
{
$result = [];
$context = ['client' => $event->getClient(), 'visitors' => []];
$command = $event->getCommand();
$response = $event->getResponse();

if ($model->getType() == 'object') {
$this->visitOuterObject($model, $result, $command, $response, $context);
} elseif ($model->getType() == 'array') {
$this->visitOuterArray($model, $result, $command, $response, $context);
} elseif ($model->getType() == 'serializer') {
$this->visitOuterSerializer($model, $result, $command, $response, $context);
} else {
throw new \InvalidArgumentException('Invalid response model: ' . $model->getType());
}

// Call the after() method of each found visitor
foreach ($context['visitors'] as $visitor) {
$visitor->after($command, $response, $model, $result, $context);
}

return $result;
}

private function triggerBeforeVisitor(
$location,
Parameter $model,
array &$result,
CommandInterface $command,
ResponseInterface $response,
array &$context
) {
if (!isset($this->responseLocations[$location])) {
throw new \RuntimeException("Unknown location: $location");
}

$context['visitors'][$location] = $this->responseLocations[$location];

$this->responseLocations[$location]->before(
$command,
$response,
$model,
$result,
$context
);
}

private function visitOuterObject(
Parameter $model,
array &$result,
CommandInterface $command,
ResponseInterface $response,
array &$context
) {
$parentLocation = $model->getLocation();

// If top-level additionalProperties is a schema, then visit it
$additional = $model->getAdditionalProperties();
if ($additional instanceof Parameter) {
// Use the model location if none set on additionalProperties.
$location = $additional->getLocation() ?: $parentLocation;
$this->triggerBeforeVisitor(
$location, $model, $result, $command, $response, $context
);
}

// Use 'location' from all individual defined properties, but fall back
// to the model location if no per-property location is set. Collect
// the properties that need to be visited into an array.
$visitProperties = [];
foreach ($model->getProperties() as $schema) {
$location = $schema->getLocation() ?: $parentLocation;
if ($location) {
$visitProperties[] = [$location, $schema];
// Trigger the before method on each unique visitor location
if (!isset($context['visitors'][$location])) {
$this->triggerBeforeVisitor(
$location, $model, $result, $command, $response, $context
);
}
}
}

// Actually visit each response element
foreach ($visitProperties as $prop) {
$this->responseLocations[$prop[0]]->visit(
$command, $response, $prop[1], $result, $context
);
}
}

private function visitOuterArray(
Parameter $model,
array &$result,
CommandInterface $command,
ResponseInterface $response,
array &$context
) {
// Use 'location' defined on the top of the model
if (!($location = $model->getLocation())) {
return;
}

if (!isset($foundVisitors[$location])) {
$this->triggerBeforeVisitor(
$location, $model, $result, $command, $response, $context
);
}

// Visit each item in the response
$this->responseLocations[$location]->visit(
$command, $response, $model, $result, $context
);
}

private function visitOuterSerializer(
Parameter $model,
array &$result,
CommandInterface $command,
ResponseInterface $response,
array &$context
) {
$result = $this->serializer->deserialize($response->getBody(), $model->class, 'json');
}
}
Loading

0 comments on commit 8dd5028

Please sign in to comment.