pieces of the api puzzle: leveraging typed data...

44
Pieces of the API Puzzle: Leveraging Typed Data, Serializer, and Guzzle in Drupal 8 Matthew Radcliffe mradcliffe @matt-kineme

Upload: others

Post on 30-Aug-2019

3 views

Category:

Documents


0 download

TRANSCRIPT

Page 1: Pieces of the API Puzzle: Leveraging Typed Data ...softpixel.com/~mradcliffe/files/dco-2014-guzzle-serializer-typeddata.pdfPieces of the API Puzzle: Leveraging Typed Data, Serializer,

Pieces of the API Puzzle: Leveraging Typed Data, Serializer, and Guzzle in

Drupal 8 Matthew Radcliffe

mradcliffe @matt-kineme

Page 2: Pieces of the API Puzzle: Leveraging Typed Data ...softpixel.com/~mradcliffe/files/dco-2014-guzzle-serializer-typeddata.pdfPieces of the API Puzzle: Leveraging Typed Data, Serializer,

Summary

• Guzzle

• Typed Data

• Serializer

Page 3: Pieces of the API Puzzle: Leveraging Typed Data ...softpixel.com/~mradcliffe/files/dco-2014-guzzle-serializer-typeddata.pdfPieces of the API Puzzle: Leveraging Typed Data, Serializer,

Guzzle

“Guzzle is a PHP HTTP client that makes it easy to send HTTP requests and trivial to integrate with web services.”

Page 4: Pieces of the API Puzzle: Leveraging Typed Data ...softpixel.com/~mradcliffe/files/dco-2014-guzzle-serializer-typeddata.pdfPieces of the API Puzzle: Leveraging Typed Data, Serializer,

Curl

• Procedural

• Not extendable

• Simple error handling

Page 5: Pieces of the API Puzzle: Leveraging Typed Data ...softpixel.com/~mradcliffe/files/dco-2014-guzzle-serializer-typeddata.pdfPieces of the API Puzzle: Leveraging Typed Data, Serializer,

// Make sure our URL is safe. $url = 'http://api.example.com?key=' . urlencode('value'); !// Init curl to get a curl handle. $ch = curl_init($url); !// Set some boiler plate options. curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE); !// Execute curl. $result = curl_exec($ch); !// Check for errors. if (curl_errorno($ch)) { $error = curl_error($ch); throw new Exception('Error: ' . htmlentities($error)); } !// Do something with the result.

Page 6: Pieces of the API Puzzle: Leveraging Typed Data ...softpixel.com/~mradcliffe/files/dco-2014-guzzle-serializer-typeddata.pdfPieces of the API Puzzle: Leveraging Typed Data, Serializer,

if ( (count($arguments) == 0) || ( is_string($arguments[0]) ) || ( is_numeric($arguments[0]) ) || ( $arguments[0] === false ) ) { $where = false; //it's a GET request if ( !in_array($name, $valid_get_methods) ) { return false; } $filterid = ( count($arguments) > 0 ) ? strip_tags(strval($arguments[0])) : false; if(isset($arguments[1])) $modified_after = ( count($arguments) > 1 ) ? str_replace( 'X','T', date( 'Y-m-dXH:i:s', strtotime($arguments[1])) ) : false; if(isset($arguments[2])) $where = ( count($arguments) > 2 ) ? $arguments[2] : false; if ( is_array($where) && (count($where) > 0) ) { $temp_where = ''; foreach ( $where as $wf => $wv ) { if ( is_bool($wv) ) { $wv = ( $wv ) ? "%3d%3dtrue" : "%3d%3dfalse"; } else if ( is_array($wv) ) { if ( is_bool($wv[1]) ) { $wv = ($wv[1]) ? rawurlencode($wv[0]) . "true" : rawurlencode($wv[0]) . "false" ; } else { $wv = rawurlencode($wv[0]) . "%22{$wv[1]}%22" ; } } else { $wv = "%3d%3d%22$wv%22"; } $temp_where .= "%26%26$wf$wv"; } $where = strip_tags(substr($temp_where, 6)); } else { $where = strip_tags(strval($where)); }

Page 7: Pieces of the API Puzzle: Leveraging Typed Data ...softpixel.com/~mradcliffe/files/dco-2014-guzzle-serializer-typeddata.pdfPieces of the API Puzzle: Leveraging Typed Data, Serializer,

drupal_http_request()

• Procedural

• Provides an options argument

• Not extendable

• Simple error handling

Page 8: Pieces of the API Puzzle: Leveraging Typed Data ...softpixel.com/~mradcliffe/files/dco-2014-guzzle-serializer-typeddata.pdfPieces of the API Puzzle: Leveraging Typed Data, Serializer,

$options = array( 'headers' => array( 'Accept' => 'application/xml', ), 'method' => 'GET', 'data' => 'key=' . urlencode('value'), ); !$result = drupal_http_request('http://example.com', $options); !// Handle error if ($result['code'] < 200 || $result['code'] > 300) { throw new Exception('Error: ' . htmlentities($result['error'])); } !// Do something with $result['data'].

Page 9: Pieces of the API Puzzle: Leveraging Typed Data ...softpixel.com/~mradcliffe/files/dco-2014-guzzle-serializer-typeddata.pdfPieces of the API Puzzle: Leveraging Typed Data, Serializer,

Guzzle• Class-based, Namespace

• OAuth support, JSON/XML parsing.

• Extendable

• Throws catchable exceptions

• Guzzle 4+:

• Use different HTTP adapter than Curl.

• Asynchronous requestsGuzzle 3 Documentation. http://guzzle3.readthedocs.org/docs.html

Page 10: Pieces of the API Puzzle: Leveraging Typed Data ...softpixel.com/~mradcliffe/files/dco-2014-guzzle-serializer-typeddata.pdfPieces of the API Puzzle: Leveraging Typed Data, Serializer,

Guzzle 3$options = array( 'headers' => array( 'Accept' => 'application/xml', ), 'query' => array( 'key' => 'value', ), ); !$client = new Guzzle\Http\Client($options); !try { $response = $client->get('http://example.com'); // Do something with the response. $xml = $response->xml(); } catch (Guzzle\Http\Exception\BadResponseException $e) { // Handle 400 and 500 level errors }

Page 11: Pieces of the API Puzzle: Leveraging Typed Data ...softpixel.com/~mradcliffe/files/dco-2014-guzzle-serializer-typeddata.pdfPieces of the API Puzzle: Leveraging Typed Data, Serializer,

Guzzle 4$options = array( 'headers' => array( 'Accept' => 'application/xml', ), 'query' => array( 'key' => 'value', ), ); !$client = new GuzzleHttp\Client('http://example.com', $options); !try { $response = $client->get('something'); // Do something with the response. $xml = $response->xml(); } catch (GuzzleHttp\Exception\RequestException $e) { // Handle errors in making request. } catch (GuzzleHttp\Exception\ResponseException $e) { // Handle errors from the response. }

Page 12: Pieces of the API Puzzle: Leveraging Typed Data ...softpixel.com/~mradcliffe/files/dco-2014-guzzle-serializer-typeddata.pdfPieces of the API Puzzle: Leveraging Typed Data, Serializer,

Typed Data• Plugin

• Data type

• Data definition

• Entities are typed data.

• Needlessly complicated?

Page 13: Pieces of the API Puzzle: Leveraging Typed Data ...softpixel.com/~mradcliffe/files/dco-2014-guzzle-serializer-typeddata.pdfPieces of the API Puzzle: Leveraging Typed Data, Serializer,

<?php !$node = \Drupal\node\Entity\Node::load($nid); !$author_name = $node->uid->referencedentities()[0]->get('name')[0]->getValue();

Page 14: Pieces of the API Puzzle: Leveraging Typed Data ...softpixel.com/~mradcliffe/files/dco-2014-guzzle-serializer-typeddata.pdfPieces of the API Puzzle: Leveraging Typed Data, Serializer,

Plugin• Annotation-based

• A plugin instance is loaded via a plugin manager i.e. Block Manager or Entity Manager. Field types, widgets, and formatters.

• Drupal\module\Plugin\(Plugin type) namespace.

• Common operations that all of those things inherit.

Page 15: Pieces of the API Puzzle: Leveraging Typed Data ...softpixel.com/~mradcliffe/files/dco-2014-guzzle-serializer-typeddata.pdfPieces of the API Puzzle: Leveraging Typed Data, Serializer,

/** * * @FieldType( * id = "uri", * label = @Translation("URI"), * description = @Translation("An entity field containing a URI."), * no_ui = TRUE, * default_formatter = "uri_link", * ) */ class UriItem extends StringItem {}

plugin type

plugin id plugin label

Page 16: Pieces of the API Puzzle: Leveraging Typed Data ...softpixel.com/~mradcliffe/files/dco-2014-guzzle-serializer-typeddata.pdfPieces of the API Puzzle: Leveraging Typed Data, Serializer,

Data Type Plugin• @DataType

• Description

• (optional) Definition class

• (optional) List class, List definition class

• (optional) Primitive

• (optional) Constraints

Page 17: Pieces of the API Puzzle: Leveraging Typed Data ...softpixel.com/~mradcliffe/files/dco-2014-guzzle-serializer-typeddata.pdfPieces of the API Puzzle: Leveraging Typed Data, Serializer,

Primitive Data Type• string

• integer, float

• boolean

• binary

• uri

Page 18: Pieces of the API Puzzle: Leveraging Typed Data ...softpixel.com/~mradcliffe/files/dco-2014-guzzle-serializer-typeddata.pdfPieces of the API Puzzle: Leveraging Typed Data, Serializer,

namespace Drupal\Core\TypedData\Plugin\DataType; !use Drupal\Core\TypedData\PrimitiveBase; use Drupal\Core\TypedData\Type\BooleanInterface; !/** * The boolean data type. * * @DataType( * id = "boolean", * label = @Translation("Boolean") * ) */ class Boolean extends PrimitiveBase implements BooleanInterface { ! /** * {@inheritdoc} */ public function getCastedValue() { return (bool) $this->value; } }

Page 19: Pieces of the API Puzzle: Leveraging Typed Data ...softpixel.com/~mradcliffe/files/dco-2014-guzzle-serializer-typeddata.pdfPieces of the API Puzzle: Leveraging Typed Data, Serializer,

Complex Data Type• Lists

• Datetime, timestamp, etc…

• E-mail, URI

• Map

• Nodes, Users, Taxonomy terms, etc…

Page 20: Pieces of the API Puzzle: Leveraging Typed Data ...softpixel.com/~mradcliffe/files/dco-2014-guzzle-serializer-typeddata.pdfPieces of the API Puzzle: Leveraging Typed Data, Serializer,

Map• The only complex data type that is not an entity

in core.

• Basically an associative array in object form.

• No storage implications like entities.

• Requires data definition to describe properties much like entities i.e. getFieldDefinitions() or getPropertyDefinitions().

Page 21: Pieces of the API Puzzle: Leveraging Typed Data ...softpixel.com/~mradcliffe/files/dco-2014-guzzle-serializer-typeddata.pdfPieces of the API Puzzle: Leveraging Typed Data, Serializer,

<?php namespace Drupal\xero\Plugin\DataType; !/** * Xero Payment type. * * @DataType( * id = "xero_payment", * label = @Translation("Xero Payment"), * definition_class = "\Drupal\xero\TypedData\Definition\PaymentDefinition" * ) */ class Payment extends XeroTypeBase { protected function getPropertyDefinitions() { return $this->definition->getPropertyDefinitions(); } }

Page 22: Pieces of the API Puzzle: Leveraging Typed Data ...softpixel.com/~mradcliffe/files/dco-2014-guzzle-serializer-typeddata.pdfPieces of the API Puzzle: Leveraging Typed Data, Serializer,

Data Definition• How to handle data types

• create

• constraints

• property definitions

• Typed Data Manager creates data type instances based on data definition for complex typed data.

Page 23: Pieces of the API Puzzle: Leveraging Typed Data ...softpixel.com/~mradcliffe/files/dco-2014-guzzle-serializer-typeddata.pdfPieces of the API Puzzle: Leveraging Typed Data, Serializer,

class PaymentDefinition extends ComplexDataDefinitionBase { public function getPropertyDefinitions() { if (!isset($this->propertyDefinitions)) { $info = &$this->propertyDefinitions; ! $info['Invoice'] = DataDefinition::create(‘xero_invoice')->setRequired(TRUE)->setLabel('Invoice'); ! $info['Account'] = DataDefinition::create(‘xero_account')->setRequired(TRUE)->setLabel('Account'); ! $info['Date'] = DataDefinition::create(‘string')->setRequired(TRUE)->setLabel('Date'); ! $info['Amount'] = DataDefinition::create(‘float')->setRequired(TRUE)->setLabel('Amount'); ! $info['CurrencyRate'] = DataDefinition::create('string')->setLabel('Currency rate’); ! $info['Reference'] = DataDefinition::create(‘string')->setLabel('Reference'); ! $info['IsReconciled'] = DataDefinition::create('boolean')->setLabel('Is reconciled?'); } return $this->propertyDefinitions; } }

Page 24: Pieces of the API Puzzle: Leveraging Typed Data ...softpixel.com/~mradcliffe/files/dco-2014-guzzle-serializer-typeddata.pdfPieces of the API Puzzle: Leveraging Typed Data, Serializer,

<?php !use Drupal\xero\TypedData\Definition\PaymentDefinition; !$typedDataManager = \Drupal::typedDataManager(); $payment_definition = PaymentDefinition::create('xero_payment'); !// You may also set values when creating data type plugin instances. Map // complex data type allows for sending in associative arrays. $payment = $typeDataManager::createInstance($payment_definition); !$payment->set('Amount', '10.00');

Page 25: Pieces of the API Puzzle: Leveraging Typed Data ...softpixel.com/~mradcliffe/files/dco-2014-guzzle-serializer-typeddata.pdfPieces of the API Puzzle: Leveraging Typed Data, Serializer,

Constraints

• Symfony 2 constraints

• Allowed Values is a constraint that extends the Choice constraint.

Page 26: Pieces of the API Puzzle: Leveraging Typed Data ...softpixel.com/~mradcliffe/files/dco-2014-guzzle-serializer-typeddata.pdfPieces of the API Puzzle: Leveraging Typed Data, Serializer,

Serializer• Symfony 2 component

• Abstracts the idea of serialization into normalizing and encoding.

• Supports JSON and XML out of the box.

• Extendable

• Easy to use despite complexity.

Page 27: Pieces of the API Puzzle: Leveraging Typed Data ...softpixel.com/~mradcliffe/files/dco-2014-guzzle-serializer-typeddata.pdfPieces of the API Puzzle: Leveraging Typed Data, Serializer,

Normalizer• Supports normalize and denormalize methods.

• Any additional data processing before encoding (hal+json, jsonld, etc…)

• Drupal provides ComplexTypeNormalizer, ContentEntityNormalizer, and a primitive normalizer amongst a few others.

Page 28: Pieces of the API Puzzle: Leveraging Typed Data ...softpixel.com/~mradcliffe/files/dco-2014-guzzle-serializer-typeddata.pdfPieces of the API Puzzle: Leveraging Typed Data, Serializer,

namespace Drupal\serialization\Normalizer; !use Symfony\Component\Serializer\Exception\RuntimeException; !class ComplexDataNormalizer extends NormalizerBase { ! protected $supportedInterfaceOrClass = 'Drupal\Core\TypedData\ComplexDataInterface'; ! public function normalize($object, $format = NULL, array $context = array()) { $attributes = array(); foreach ($object as $name => $field) { $attributes[$name] = $this->serializer->normalize($field, $format); } return $attributes; } }

Page 29: Pieces of the API Puzzle: Leveraging Typed Data ...softpixel.com/~mradcliffe/files/dco-2014-guzzle-serializer-typeddata.pdfPieces of the API Puzzle: Leveraging Typed Data, Serializer,

Normalized User

<?php !array( 'uid' => array(array('value' => 1)), 'name' => array(array('value' => 'admin')), 'pass' => array(array('value' => 'hashedpasswordsecurityvulnerability')), // and so on... );

Page 30: Pieces of the API Puzzle: Leveraging Typed Data ...softpixel.com/~mradcliffe/files/dco-2014-guzzle-serializer-typeddata.pdfPieces of the API Puzzle: Leveraging Typed Data, Serializer,

Encoder

• Take normalized data and encode in a given format.

• Take data in given format, and return normalized data.

Page 31: Pieces of the API Puzzle: Leveraging Typed Data ...softpixel.com/~mradcliffe/files/dco-2014-guzzle-serializer-typeddata.pdfPieces of the API Puzzle: Leveraging Typed Data, Serializer,

namespace Symfony\Component\Serializer\Encoder; !class JsonEncoder implements EncoderInterface, DecoderInterface { const FORMAT = 'json'; protected $encodingImpl; protected $decodingImpl; ! public function encode($data, $format, array $context = array()) { return $this->encodingImpl->encode($data, self::FORMAT, $context); } ! public function decode($data, $format, array $context = array()) { return $this->decodingImpl->decode($data, self::FORMAT, $context); } }

if } ! return }

Page 32: Pieces of the API Puzzle: Leveraging Typed Data ...softpixel.com/~mradcliffe/files/dco-2014-guzzle-serializer-typeddata.pdfPieces of the API Puzzle: Leveraging Typed Data, Serializer,

namespace Symfony\Component\Serializer\Encoder; !class JsonEncoder implements EncoderInterface, DecoderInterface { const FORMAT = 'json'; protected $encodingImpl; protected $decodingImpl; ! public function encode($data, $format, array $context = array()) { return $this->encodingImpl->encode($data, self::FORMAT, $context); } ! public function decode($data, $format, array $context = array()) { return $this->decodingImpl->decode($data, self::FORMAT, $context); } }

! public function encode($data, $format, array $context = array()) { $context = $this->resolveContext($context); ! $encodedJson = json_encode($data, $context['json_encode_options']); ! if (JSON_ERROR_NONE !== $this->lastError = json_last_error()) { throw new UnexpectedValueException(JsonEncoder::getLastErrorMessage()); } ! return $encodedJson; }

Page 33: Pieces of the API Puzzle: Leveraging Typed Data ...softpixel.com/~mradcliffe/files/dco-2014-guzzle-serializer-typeddata.pdfPieces of the API Puzzle: Leveraging Typed Data, Serializer,

Using Serializer

<?php !$node = \Drupal\node\Entity\Node::load($nid); !$context = array('plugin_id' => 'node'); $my_json = $serializer->serialize($node, 'json', $context);

Page 34: Pieces of the API Puzzle: Leveraging Typed Data ...softpixel.com/~mradcliffe/files/dco-2014-guzzle-serializer-typeddata.pdfPieces of the API Puzzle: Leveraging Typed Data, Serializer,

Putting it all together• Typed Data describes entities, primitives, etc…

with getter/setter, constraints, etc…

• Serializer normalizes typed data and encodes it into a format.

• Guzzle makes a request with the serialized data as a payload.

• Or vice versa.

Page 35: Pieces of the API Puzzle: Leveraging Typed Data ...softpixel.com/~mradcliffe/files/dco-2014-guzzle-serializer-typeddata.pdfPieces of the API Puzzle: Leveraging Typed Data, Serializer,

services: xero.client.factory: class: Drupal\xero\XeroClientFactory xero.client: class: BlackOptic\Bundle\XeroBundle\XeroClient factory_method: get factory_service: xero.client.factory arguments: ['@config.factory', '@logger.factory'] xero.normalizer: class: Drupal\xero\Normalizer\XeroNormalizer arguments: ['@typed_data_manager'] tags: - { name: normalizer } xero.query: class: Drupal\xero\XeroQuery arguments: ['@xero.client', '@serializer', '@typed_data_manager', '@logger.factory']

Page 36: Pieces of the API Puzzle: Leveraging Typed Data ...softpixel.com/~mradcliffe/files/dco-2014-guzzle-serializer-typeddata.pdfPieces of the API Puzzle: Leveraging Typed Data, Serializer,

<?php /** * @file * Contains \Drupal\xero\Form\SettingsForm. */ !namespace Drupal\xero\Form; !use Drupal\Core\DependencyInjection\ContainerInjectionInterface; use Drupal\Core\Config\ConfigFactoryInterface; use Drupal\Core\Form\ConfigFormBase; use Drupal\Core\Form\FormStateInterface; use Symfony\Component\DependencyInjection\ContainerInterface; use Symfony\Component\Serializer\Serializer; use Drupal\xero\XeroQuery; use Guzzle\Http\Exception\RequestException; use Guzzle\Http\Exception\ClientErrorResponseException; use Drupal\Core\Logger\LoggerChannelFactoryInterface; !/** * Provide configuration form for user to provide Xero API information for a * private application. */ class SettingsForm extends ConfigFormBase implements ContainerInjectionInterface { ! protected $query = FALSE; ! /** * Inject dependencies into the form except for XeroClient because we want to * handle errors properly instead of failing and exploding spectacularly. *

Page 37: Pieces of the API Puzzle: Leveraging Typed Data ...softpixel.com/~mradcliffe/files/dco-2014-guzzle-serializer-typeddata.pdfPieces of the API Puzzle: Leveraging Typed Data, Serializer,

/** * Inject dependencies into the form except for XeroClient because we want to * handle errors properly instead of failing and exploding spectacularly. * * @param $config_factory * Configuration factory interface. * @param $query * An instance of XeroClient or NULL if it fails, which is most likely the * case on first load. * @param $serializer * Serializer object. */ public function __construct(ConfigFactoryInterface $config_factory, $query, Serializer $serializer, LoggerChannelFactoryInterface $logger_factory) { $this->setConfigFactory($config_factory); $this->query = $query; $this->serializer = $serializer; $this->logger = $logger_factory->get('xero'); } ! /** * {@inheritdoc} */ public function getFormId() { return 'xero_configuration_form'; } ! /** * {@inheritdoc} */ public function buildForm(array $form, FormStateInterface $form_state) {

Page 38: Pieces of the API Puzzle: Leveraging Typed Data ...softpixel.com/~mradcliffe/files/dco-2014-guzzle-serializer-typeddata.pdfPieces of the API Puzzle: Leveraging Typed Data, Serializer,

public function buildForm(array $form, FormStateInterface $form_state) { ! // Get the configuration from ConfigFormBase::config(). $config = self::config('xero.settings'); ! // Open the fieldset if there was a problem loading the library. $form['oauth'] = array( '#type' => 'fieldset', '#title' => t('Xero Configuration'), '#collapsible' => TRUE, '#collapsed' => $this->query->client !== FALSE, '#tree' => TRUE, ); ! $form['oauth']['consumer_key'] = array( '#type' => 'textfield', '#title' => t('Xero Consumer Key'), '#description' => t('Provide the consumer key for your private application on xero.'), '#default_value' => $config->get('oauth.consumer_key'), '#required' => TRUE, ); ! $form['oauth']['consumer_secret'] = array( '#type' => 'textfield', '#title' => t('Xero Consumer Secret'), '#description' => t('Provide the consumer secret for your private application on xero.'), '#default_value' => $config->get('oauth.consumer_secret'), '#required' => TRUE, );

Page 39: Pieces of the API Puzzle: Leveraging Typed Data ...softpixel.com/~mradcliffe/files/dco-2014-guzzle-serializer-typeddata.pdfPieces of the API Puzzle: Leveraging Typed Data, Serializer,

if ($this->query->client !== FALSE) { $account_options = array(); ! try { $context = array('plugin_id' => 'xero_account'); $accounts = $this->query ->setType($context['plugin_id']) ->setMethod('get') ->setFormat('xml') ->execute(); ! foreach ($accounts as $account) { // Bank accounts do not have a code, exclude them. if ($account->get('Code')->getValue()) { $account_options[$account->get('Code')->getValue()] = $account->get('Name')->getValue(); } } } catch (RequestException $e) { $this->logger->error('%message: %response', array('%message' => $e->getMessage(), '%response' => $e->getResponse()->getBody(TRUE))); return parent::buildForm($form, $form_state); } catch (\Exception $e) { $this->logger->error('%message', array('%message' => $e->getMessage())); return parent::buildForm($form, $form_state); } ! $form['defaults']['account'] = array( '#type' => 'select',

Page 40: Pieces of the API Puzzle: Leveraging Typed Data ...softpixel.com/~mradcliffe/files/dco-2014-guzzle-serializer-typeddata.pdfPieces of the API Puzzle: Leveraging Typed Data, Serializer,

<?php /** * @file * Provides \Drupal\xero\XeroQuery. */ !namespace Drupal\xero; !use Symfony\Component\Serializer\Serializer; use BlackOptic\Bundle\XeroBundle\XeroClient; use \Drupal\xero\TypedData\XeroTypeInterface; use Drupal\Core\TypedData\TypedDataManager; use Drupal\Component\Uuid; use Drupal\Core\Logger\LoggerChannelFactoryInterface; use Drupal\Component\Plugin\Exception\PluginNotFoundException; use Guzzle\Http\Exception\RequestException; use Guzzle\Http\Exception\ClientErrorResponseException; // use Drupal\xero\XeroQueryInterface; !/** * Provides a query builder service for HTTP requests to Xero. * * This matches functionality provided by Drupal 7 xero module and the old * PHP-Xero library. */ class XeroQuery /*implements XeroQueryInterface */ { ! static protected $operators = array('==', '!=', 'StartsWith', 'EndsWith', 'Contains', 'Guid'); ! /**

Page 41: Pieces of the API Puzzle: Leveraging Typed Data ...softpixel.com/~mradcliffe/files/dco-2014-guzzle-serializer-typeddata.pdfPieces of the API Puzzle: Leveraging Typed Data, Serializer,

public function __construct(XeroClient $client, Serializer $serializer, TypedDataManager $typed_data, LoggerChannelFactoryInterface $logger_factory) { $this->client = $client; $this->serializer = $serializer; $this->typed_data = $typed_data; $this->logger = $logger_factory->get('xero'); }

Page 42: Pieces of the API Puzzle: Leveraging Typed Data ...softpixel.com/~mradcliffe/files/dco-2014-guzzle-serializer-typeddata.pdfPieces of the API Puzzle: Leveraging Typed Data, Serializer,

public function execute() { try { $this->validate(); $this->explodeConditions(); ! $data_class = $this->type_definition['class']; $endpoint = $data_class::$plural_name; $context = array('plugin_id' => $this->type); ! if ($this->data !== NULL) { $this->options['body'] = $this->serializer->serialize($this->data, $this->format, $context); } ! $request = $this->client->{$this->method}($endpoint, $this->options['headers']); ! // Add query parameters via getQuery() instead :-( if (!empty($this->options['query'])) { $q = $request->getQuery(); foreach ($this->options['query'] as $key => $value) { $q->set($key, $value); } } ! $response = $request->send(); $data = $this->serializer->deserialize($response->getBody(TRUE), $data_class, $this->format, $context); ! return $data; }

Page 43: Pieces of the API Puzzle: Leveraging Typed Data ...softpixel.com/~mradcliffe/files/dco-2014-guzzle-serializer-typeddata.pdfPieces of the API Puzzle: Leveraging Typed Data, Serializer,

Views Rest Export

• You can also use Views and Rest modules.

• But if you need to serialize for third party APIs, then you have the tools and knowledge to do so.

Page 44: Pieces of the API Puzzle: Leveraging Typed Data ...softpixel.com/~mradcliffe/files/dco-2014-guzzle-serializer-typeddata.pdfPieces of the API Puzzle: Leveraging Typed Data, Serializer,

Questions?