Merck Moving Beyond Passwords: FIDO Paris Seminar.pptx
Designing CakePHP plugins for consuming APIs
1. Designing CakePHP plugins for consuming APIs By @neilcrookes for CakeFest2010 www.neilcrookes.com github.com/neilcrookes
2. Contents Foundations CakePHP plugins, APIs, REST, HTTP, CakePHP HttpSocket, OAuth Design approach Traditional approach, issues with that, my solution Examples
3. Types of CakePHP plugins Mini apps Provide full functionality you can include in your app e.g. blog, store locator Extenders Extend your app with more functionality e.g. commentable & taggable Enhancers Enhance your apps existing functionality e.g. filter Wrappers Provide functionality to access 3rd party APIs
4.
5. My work so far has been mainly consuming RESTful APIs so this presentation and examples will focus on REST
8. Simple HTTP GET Request & Response Request GET http://www.example.com/index.html HTTP/1.1 User-Agent: My Web Browser Response HTTP/1.1 200 OK Content-Type: text/html Content-Length: 70 <html> <head> <title>My Web Page</title> </head> <body> <h1>My Web Page</h1> </body> </html>
9. Simple HTTP POST Request & Response Request POST http://www.example.com/login HTTP/1.1 Content-Type: application/x-www-form-urlencoded Content-Length: 38 username=neilcrookes&password=abcd1234 Response HTTP/1.1 301 Moved Permanently Location: http://www.example.com/my_account
10.
11. OAuth In summary, it allows users of a service (e.g. Twitter) to authorize other parties (i.e. your application) access to their accounts on that service, without sharing their password with the other parties. In reality, it means: a little bit of handshaking between your app and the service provider to get various string tokens redirecting the user to the service in order for them to authorize your app to access their account, so the user only signs in to the service, not your app. the service provides you with a token you can persist and use to make authorized requests to their service on behalf of the user In practice it’s just an extra header line (Authorization header) in the HTTP request which contains some arbitrary parameters e.g. timestamp a token that identifies your application to the API provider a signature string that signs the request and is a hash of various request parameters and the secret tokens you retrieved above Used by e.g. Twitter & Google APIs
13. Contents Foundations CakePHP plugins, APIs, REST, HTTP, CakePHP HttpSocket, OAuth Design approach Traditional approach, issues with that, my solution Examples
14. Traditional approach: DataSource Complex DataSource containing all the logic Call methods on the DataSource directly from your models or controllersor as implied by the example Twitter DataSource in the cook book: access DataSource methods through your models but include most of the logic in the DataSourcehttp://book.cakephp.org/view/1077/An-Example Works well for simple stuff This is how I started implementing
15. However... Does not scale well for large APIs Twitter has ~100 API calls available, all with a wide variety of options and parameters. The cook book Twitter DataSource partially implements 2 API calls and is 86 lines Does not exploit built-in CakePHP goodness Callbacks Validation Pagination Does not allow for multiple models (and therefore multiple schemas) to use the same DataSource Didn’t feel right to me
16. So what does feel right? What operations are we actually doing? Reading data Creating and updating data Deleting data i.e. Find, save & delete What type of classes in CakePHP provide these methods?
17. Models Photo by memoflores, available under creative commons http://www.flickr.com/photos/memoflores/ And what should models be?...
18. FAT! Photo by cstreetus, available under creative commons http://www.flickr.com/photos/cstreetus/ Sorry but every other image I found through searching for “fat models” or “fat ladies” was completely inappropriate ;-)
19. So if we move our API calls into Model::find(), Model::save() and Model::delete() methods It feels like the right place We’re more familiar with interacting with these We can have lots of simple models classes to achieve scale, separation of concerns and different models can have different validation rules and schemas and we can collect them together in a plugin But...
20. But what about CakePHP goodness? Triggering callbacks beforeFind(), afterFind(), beforeSave(), afterSave(), beforeValidate(), beforeDelete(), afterDelete() Triggering validation Handling custom find types If we made the API calls directly in these methods and returned the response, to exploit this excellent built-in additional CakePHP functionality we’d have to trigger/code them manually We’d be duplicating loads of code from CakePHP’s core Model class. Not very DRY
21. To understand the solution, we must understand CakePHP Model internals Model methods like find(), save() and delete() accept various params such as conditions, data to save etc Handle custom find types for find() only Handle validation for save() only Trigger the before*() callbacks Call create(), read(), update() or delete() on that model’s DataSource Trigger the after*() callbacks Return the result
22. So what’s my solution for designing CakePHP plugins for consuming APIs? Plugin containing one model for each type of resource in the API e.g. TwitterStatus or YouTubeVideo Models implement find() (or actually more commonly just CakePHP custom find types), save() and delete() methods as appropriate These methods set the details of the request, i.e. The array that represents an HTTP request that HttpSocket::request() methods expects (as we saw earlier in this presentation) in a request property of your model, then calls the same method on the parent object i.e. Model. Cont...
23. Solution continued CakePHP Model class handles validation and custom find types, triggers callbacks etc then calls create(), read(), update() or delete() on the child model’s (your model’s) DataSource, and passes the model object Your model’s useDbConfig property should be set to a custom DataSource that you also include in your plugin Your DataSource implements the appropriate CRUD method(s) and issues the API call described in the model’s requestproperty, and returns the results
24. Hmmm, sounds complicated It’s not I’ve written a REST DataSource you can use (see later) All you have to do is create a model that has find() or save() methods, in which you set a request property to an array expected by HttpSocket::request() and call the same method on the parent.
25. E.g. Creating a tweet <?php class TwitterStatus extends AppModel { public function save($data = null) { $this->request = array( 'uri' => array( 'host' => 'api.twitter.com', 'path' => '1/statuses/update.json'), 'body' => array( 'status' => $data['TwitterStatus']['text'])); return parent::save($data); } } ?>
26. Which you call like this ClassRegistry::init('Twitter.TwitterStatus')->save(array( 'TwitterStatus' => array( 'text' => “Hello world!”) )); ... from anywhere you like in your CakePHP application, e.g. In your Post model afterSave() method, thus automatically creating a tweet every time you create a new post.
28. This diagram illustrates the flow through the methods an classes involved in creating a tweet https://docs.google.com/drawings/edit?id=1Aht7huICl9bhl2hWRdM0VdoaBePpJ0kXkceyQpAR8os&hl=en_GB&authkey=CISSqJkN
29. In summary By designing plugins like this you’re providing Simple (1 line) method calls to API functions That are familiar to all CakePHP bakers And easy to document You also get to exploit CakePHP goodness such as validation and callbacks etc You can have multiple models, one for each resource type on the API, each with it’s own schema (which the FormHelper uses) and validation rules
30. Contents Foundations CakePHP plugins, APIs, REST, HTTP, CakePHP HttpSocket, OAuth Design approach Traditional approach, issues with that, my solution Examples