Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Practical APIs

Practical APIs

Let's face it: with emerging publish channels like Google AMP or Facebook Instant Articles, it's not enough to just deliver a HTML representation of your content anymore. In this workshop you'll see why you need an API, what the benefits are, and at what you should aim when designing an API. We will extend the eZ Platforms REST API, look at its advantages and disadvantages and discuss other options.

Urban Etter

August 31, 2016
Tweet

More Decks by Urban Etter

Other Decks in Programming

Transcript

  1. INDIAN PALE ALE > High hop content > Tends to

    have more volume percent > Said to be brewed for long ship journeys to India
  2. IPA

  3. EZ PLATFORM REST API { "Content": { "_media-type": "application/vnd.ez.api.ContentInfo+json", "_href":

    "/api/ezp/v2/content/objects/86", "_remoteId": "73be2a5122ecd6a4a7ad1cef6b0393f1", "_id": 86, "ContentType": { "_media-type": "application/vnd.ez.api.ContentType+json", "_href": "/api/ezp/v2/content/types/18" }, "Name": "Build a better performing site with continuous optimization", "Versions": { "_media-type": "application/vnd.ez.api.VersionList+json", "_href": "/api/ezp/v2/content/objects/86/versions" }, "CurrentVersion": { "_media-type": "application/vnd.ez.api.Version+json", "_href": "/api/ezp/v2/content/objects/86/currentversion" } } }
  4. BUILDING BLOCKS A good API makes it easier to develop

    a program by providing all the building blocks, which are then put together by the programmer. — Wikipedia
  5. REMEMBERING BUILDING BLOCKS A good API makes it easier to

    develop a program by providing all the building blocks, which are then put together by the programmer. — Wikipedia
  6. HIGH LEVEL API/ DOMAIN API [ { "name": "Punk IPA",

    "review": 5, "brewery": "Brew Dog", "image": "http://www.ipaapi.io/images/punk-ipa.jpg", }, { "name": "Dead Pony Club", "review": 4.5, "brewery": "Brew Dog", "image": "http://www.ipaapi.io/images/dead-pony-club.jpg" } ]
  7. HIGH LEVEL API > Specified for a certain use case

    > No need to know internals > Not flexible > Needed info with one request > Only one format needs to be supported
  8. LOW LEVEL API { "Content": { "_media-type": "application/vnd.ez.api.ContentInfo+json", "_href": "/api/ezp/v2/content/objects/86",

    "_remoteId": "73be2a5122ecd6a4a7ad1cef6b0393f1", "_id": 86, "ContentType": { "_media-type": "application/vnd.ez.api.ContentType+json", "_href": "/api/ezp/v2/content/types/18" }, "Name": "Build a better performing site with continuous optimization", "Versions": { "_media-type": "application/vnd.ez.api.VersionList+json", "_href": "/api/ezp/v2/content/objects/86/versions" }, "CurrentVersion": { "_media-type": "application/vnd.ez.api.Version+json", "_href": "/api/ezp/v2/content/objects/86/currentversion" } } }
  9. LOW LEVEL API > One API for loads of use

    cases > Knowledge of some internals may be required > Very flexible > Different formats (XML or JSON)
  10. ENTITY > Value objects > Service which "loads" value objects

    > Public attributes > eZ Publish Public API has the same architecture
  11. CREATE PHP ENTITIES AS A SERVICE class IpaService { public

    function loadIpa($contentId) { $content = $contentService->loadContent($contentId); $ipa = new IPA(); $ipa->name = $content->getFieldValue('title')->text; // ... } }
  12. IMAGES [ { "name": "Punk IPA", "review": 5, "image": "http://www.ipaapi.io/images/punk-ipa.jpg"

    }, { "name": "Dead Pony Club", "review": 4.5, "image": "http://www.ipaapi.io/images/dead-pony-club.jpg" } ]
  13. VISITORS > Main target: Translate object > Visits objects of

    a graph, therefore a Visitor > Register as visitor with Symfony DIC tag
  14. EZ REST VISITOR - BASIC public function visit(Visitor $visitor, Generator

    $generator, $data) { $generator->startObjectElement('ipa'); $generator->startValueElement('name', $data->name); $generator->endValueElement('name'); $generator->startValueElement('rating', $data->review); $generator->endValueElement('rating'); $generator->endObjectElement('ipa'); }
  15. EZ REST VISITOR - MIME && HREF public function visit(Visitor

    $visitor, Generator $generator, $data) { $mediaType = 'ipa'; $generator->startObjectElement('ipa', $mediaType); $visitor->setHeader('Content-Type', $generator->getMediaType($mediaType)); $generator->startAttribute( 'href', $this->router->generate('ezpublish_rest_ipa', array('contentId' => $data->id)) ); $generator->endAttribute('href'); // ... $generator->endObjectElement('ipa'); }
  16. EZ REST CONTROLLER: CACHEDVALUE public function ipaAction($contentId) { $service =

    $this->container->get('app.ipa_service'); $ipa = $service->loadIpa($contentId); $contentService = $this->container->get('ezpublish.api.service.content'); $locationId = $contentService->loadContentInfo($contentId)->mainLocationId; $cached = new CachedValue($ipa, ['locationId' => $locationId]); return $cached; }
  17. ADVANTAGES OF EZ REST API > Support of Mime Types

    > XML or JSON format > Caching > Visitor pattern
  18. DISADVANTAGES OF EZ REST API > Some HTTP knowlege (MIME

    types, headers) needed > Given URL prefix (at least when using legacy) > Verbose visitors
  19. EZ RICH TEXT INTERNAL FORMAT <?xml version="1.0" encoding="UTF-8"?> <section xmlns="namespace

    stuff"> <para> Probably my favorite IPA. Some citrus and grapefruit notes. And just.... awsome. </para> <ezembed xlink:href="ezcontent://64" view="embed" ezxhtml:class="ez-embed-type-image"> <ezconfig><ezvalue key="size">small</ezvalue></ezconfig> </ezembed> </section>
  20. EZEMBED > means there can be "subviews" which get rendered

    independently > => override of templates !
  21. PROVIDE LINKS TO REPRESENTATIONS [ { "name": "Punk IPA", "review":

    5, "brewery": "Brew Dog", "image": "http://www.ipaapi.io/images/punk-ipa.jpg", "representations": { "html": "http://www.ipaapi.io/ipa/57", "google_amp": "http://www.ipaapi.io/amp/57" } } ]
  22. CUSTOM MATCHER: CONTROLLER public function ipaAmpAction(Request $request, $contentId) { $ipa

    = $this->get('app.ipa_service')->getIpa($contentId); $this->get('app.matcher.representation')->setRepresentation('amp') $response = $this->render( 'AppBundle:Amp:ipa.html.twig', [ 'content' => $ipa, ] ); return $response; }
  23. CUSTOM MATCHER: MATCHER class Representation extends MultipleValued { private $representationName;

    public function setRepresentationName($name) { $this->representationName = $name; } public function match(View $view) { return isset($this->values[$this->representationName]); } }
  24. UNSOLVED PROBLEM > New embeddable content type means new override

    rule for every representation > Lot of empty templates for a new representation
  25. RICHTEXT RENDERER > Priority 0: Link Renderer > Priority 10:

    Embed Renderer > New Priority 5: Embed Reducer
  26. RICHTEXTREDUCER class RichTextReducer extends Render implements Converter { /** *

    Converts given $xmlDoc into another \DOMDocument object. */ public function convert(DOMDocument $xmlDoc) { $embeds = $xmlDoc->getElementsByTagName('ezembed'); foreach ($embeds as $embed) { $href = $embed->getAttribute('xlink:href'); if (!$this->representation->supportsEmbed($href)) { $embed->parentNode->removeChild($embed); } } return $xmlDoc; } }