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. PRACTICAL
    API

    View Slide

  2. WELCOME

    View Slide

  3. API4EZ

    View Slide

  4. URBAN ETTER
    @URBMC ON TWITTER
    @URBAN ON EZ
    COMMUNITY SLACK

    View Slide

  5. View Slide

  6. 4 PARTS

    View Slide

  7. WHAT
    IS AN API

    View Slide

  8. WHY
    DO I NEED AN
    API

    View Slide

  9. HOW
    DO I CREATE AN
    API

    View Slide

  10. EZ RICH TEXT

    View Slide

  11. EXERCISES

    View Slide

  12. EXERCISES
    > README.md
    > What to do
    > Bonus
    > Hints
    > Code

    View Slide

  13. ABOUT
    INDIAN PALE ALE

    View Slide

  14. INDIAN PALE ALE
    > High hop content
    > Tends to have more volume percent
    > Said to be brewed for long ship journeys to India

    View Slide

  15. IPA

    View Slide

  16. IPA-API

    View Slide

  17. ABOUT
    BREW DOG

    View Slide

  18. PUNK IPA

    View Slide

  19. DEAD PONY CLUB (3.8%)

    View Slide

  20. SIGGIBRÄU

    View Slide

  21. WHAT
    IS AN API

    View Slide

  22. TWITTER API
    GET https://api.twitter.com/1.1/statuses/show.json?
    id=21046285714025267

    View Slide

  23. 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"
    }
    }
    }

    View Slide

  24. PHP API
    $contentInfo = $contentService->loadContentInfo($contentId);
    echo $contentInfo->mainLocationId;

    View Slide

  25. PHP API
    $myKitten = new Cat('Kitty');
    $myKitten->lookCute();

    View Slide

  26. SQL
    SELECT id, name, review
    FROM ipa
    WHERE id = 57;

    View Slide

  27. BASH API
    ln -s my/source my/target

    View Slide

  28. APPLICATION
    PROGRAMMING
    INTERFACE

    View Slide

  29. INTERFACE
    TO A PROGRAMM

    View Slide

  30. PROGRAMMABLE

    View Slide

  31. 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

    View Slide

  32. SEPARATES
    INTERFACE AND
    IMPLEMENTATION

    View Slide

  33. SPECIFICATION
    VS
    IMPLEMENTATION

    View Slide

  34. WHAT
    VS
    HOW

    View Slide

  35. AN API ONLY
    SPECIFIES
    WHAT

    View Slide

  36. PHP REPRESENTATIONS
    > interface: specification
    > trait: implementation
    > class: specification + implementation

    View Slide

  37. WHO WROTE A
    CLASS?

    View Slide

  38. WE'RE DONE,
    RIGHT?

    View Slide

  39. INTRODUCING
    DEMO CONTENT

    View Slide

  40. View Slide

  41. View Slide

  42. CALL TO ACTION
    EXERCISE 1

    View Slide

  43. WHY
    DO YOU NEED
    AN API

    View Slide

  44. IF YOU'RE BUILDING A
    SLACK BOT

    View Slide

  45. WHY YOU NEED AN API AS
    A
    PUBLISHER

    View Slide

  46. View Slide

  47. View Slide

  48. View Slide

  49. View Slide

  50. View Slide

  51. View Slide

  52. DIFFERENT
    REPRESENTATIONS
    OF YOUR
    CONTENT

    View Slide

  53. PROGRAMMABLE
    ACCESS TO
    YOUR CONTENT

    View Slide

  54. CONTENT
    DELIVERY

    View Slide

  55. 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

    View Slide

  56. BUZZWORD
    ALARM!

    View Slide

  57. API
    FIRST

    View Slide

  58. API
    AS A
    PRODUCT

    View Slide

  59. HEADLESS
    CMS

    View Slide

  60. BASICALLY
    THE SAME

    View Slide

  61. PROGRAMMABLE
    ACCESS TO
    YOUR CONTENT

    View Slide

  62. EZ REST API
    TO DELIVER CONTENT

    View Slide

  63. WE'RE DONE,
    RIGHT?

    View Slide

  64. REMEMBER
    > API specifies What not How
    > Programmable content delivery

    View Slide

  65. My content is an
    ARTICLE
    not an
    EZ CONTENT OBJECT

    View Slide

  66. LAYERED APIS

    View Slide

  67. MOBILE APP USECASE
    > Request count matters
    > Verbosity of response matters

    View Slide

  68. 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"
    }
    ]

    View Slide

  69. 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

    View Slide

  70. 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"
    }
    }
    }

    View Slide

  71. LOW LEVEL API
    > One API for loads of use cases
    > Knowledge of some internals may be required
    > Very flexible
    > Different formats (XML or JSON)

    View Slide

  72. LOW LEVEL API

    View Slide

  73. LET'S BUILD A
    DOMAIN API

    View Slide

  74. HOW
    TO BUILD AN
    API

    View Slide

  75. MY CONTENT IS
    AN ARTICLE
    AN IPA

    View Slide

  76. PHP ENTITY (VALUE OBJECT)
    class IPA
    {
    public $id;
    public $name;
    public $review;
    }

    View Slide

  77. ENTITY
    > Value objects
    > Service which "loads" value objects
    > Public attributes
    > eZ Publish Public API has the same architecture

    View Slide

  78. 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;
    // ...
    }
    }

    View Slide

  79. CALL TO ACTION
    EXERCISE 2

    View Slide

  80. NOW WE HAVE A
    NORMALIZED
    ENTITY

    View Slide

  81. CONVERT ENTITY
    TO NEEDED
    REPRESENTATION

    View Slide

  82. WE'RE DONE,
    RIGHT?

    View Slide

  83. WHAT ABOUT
    IMAGES

    View Slide

  84. NOT POSSIBLE TO SEND
    ALL DATA IN JSON

    View Slide

  85. => GIVE URL WHERE
    ADDITIONAL CONTENT
    CAN BE LOADED

    View Slide

  86. 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"
    }
    ]

    View Slide

  87. CALL TO ACTION
    EXERCISE 3

    View Slide

  88. REQUIREMENT
    > XML and JSON
    > Relationship between IPA and Brewery not clear

    View Slide

  89. EZ REST API COMPONENT
    > Rest Controller
    > Rest Routing
    > Visitor pattern

    View Slide

  90. VISITORS
    > Main target: Translate object
    > Visits objects of a graph, therefore a Visitor
    > Register as visitor with Symfony DIC tag

    View Slide

  91. 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');
    }

    View Slide

  92. 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');
    }

    View Slide

  93. EZ REST CONTROLLER
    public function ipaAction($contentId)
    {
    $service = $this->container->get('app.ipa_service');
    $ipa = $service->loadIpa($contentId);
    return $ipa;
    }

    View Slide

  94. 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;
    }

    View Slide

  95. CALL TO ACTION
    EXERCISE 4

    View Slide

  96. ADVANTAGES OF EZ REST API
    > Support of Mime Types
    > XML or JSON format
    > Caching
    > Visitor pattern

    View Slide

  97. DISADVANTAGES OF EZ REST API
    > Some HTTP knowlege (MIME types, headers) needed
    > Given URL prefix (at least when using legacy)
    > Verbose visitors

    View Slide

  98. EZ RICH TEXT
    EZ XML TEXT

    View Slide

  99. POWERFUL FEATURE
    OF EZ PLATFORM

    View Slide

  100. EZ PUBLISH: EZXMLTEXT
    EZ PLATFORM:
    EZRICHTEXT

    View Slide

  101. EZ RICH TEXT INTERNAL FORMAT



    Probably my favorite IPA. Some citrus and grapefruit notes. And just.... awsome.


    small


    View Slide

  102. EZEMBED
    > means there can be "subviews" which get rendered
    independently
    > => override of templates !

    View Slide

  103. EZEMBED
    HARD TO SERIALIZE
    => XML EVEN IN JSON
    REPRESENTATION

    View Slide

  104. PROBLEM
    > A JSON representation contains XML tags
    > => How, not What

    View Slide

  105. REPRESENTATIONS
    RENDERED BY
    EZ PLATFORM/ EZ
    PUBLISH

    View Slide

  106. 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"
    }
    }
    ]

    View Slide

  107. PROBLEMS TO SOLVE
    > Different representations, different templates
    > Representations which do not support all content
    type embeds

    View Slide

  108. EZ PLATFORM OVERRIDES
    CUSTOM REPRESENTATION
    MATCHER

    View Slide

  109. CUSTOM MATCHER
    content_view:
    embed:
    amp_image:
    template: "AppBundle:Amp:image.html.twig"
    match:
    Identifier\ContentType: "image"
    app.matcher.representation: "amp"

    View Slide

  110. 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;
    }

    View Slide

  111. 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]);
    }
    }

    View Slide

  112. RICHTEXT WITH NORMAL IMAGES

    View Slide

  113. RICHTEXT WITH OVERRIDDEN IMAGES

    View Slide

  114. CALL TO ACTION
    EXERCISE 5

    View Slide

  115. UNSOLVED PROBLEM
    > New embeddable content type means new override rule
    for every representation
    > Lot of empty templates for a new representation

    View Slide

  116. EZRICHTEXT OUTPUTS
    > Different output: xhtml5, xhtml5_edit
    > Renderer used for different outputs

    View Slide

  117. USE CUSTOM RENDERER
    TO
    CLEAN OUT UNSUPPORTED
    TYPES

    View Slide

  118. RICHTEXT RENDERER
    > Priority 0: Link Renderer
    > Priority 10: Embed Renderer
    > New Priority 5: Embed Reducer

    View Slide

  119. 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;
    }
    }

    View Slide

  120. REGISTER RICHTEXTREDUCER
    app.richtext.converter.reducer:
    class: "AppBundle\Service\RichTextReducer"
    arguments:
    - "@ezpublish.fieldType.ezrichtext.renderer"
    tags:
    - {name: ezpublish.ezrichtext.converter.output.xhtml5, priority: 5}

    View Slide

  121. CALL TO ACTION
    EXERCISE 6

    View Slide

  122. TANK YOU
    @URBMC ON TWITTER
    @URBAN ON EZ
    COMMUNITY SLACK

    View Slide