YQL Sample App
The YQL Sample App:
- Demonstrates OAuth Support in Mail Applications.
- Calls an OAuth-protected endpoint using the AuthService APIs.
- Uses YQL.
- Shows how to use AuthService to orchestrate user authorization for private data access.
- Shows how to make use of Yahoo! endpoint refinements if they're enabled for your app.
Try It Out
See Quick Start to learn how to get the full source for this example, install it, and run it in Yahoo! Mail.
Introduction
The app opens in a tab when you click on its icon in the app pane. Its view has a text box where the user may enter YQL queries. When the user presses 'Go!', it passes the query to the YQL service using AuthService APIs, requesting results in JSON format. Those results are rendered in a YUI Tree View.
We will only show and talk about the AuthService parts of the sample's
source here. Complete source is available
from ymdt
create -S 'YQL'
The OAuth Configuration File
You can obtain an OAuth key and secret by following these instructions and requesting scopes for "Contacts", "Profiles", "Mail" and any other Yahoo! services you'd like the user to authorize your app to query using YQL.
This is the auth.xml you
would wind up with if you created an app from the YQL sample (i.e. if
you did ymdt
create -S 'YQL'). To make it work, you will need to
replace insert key here with your OAuth consumer key
and insert secret here with your OAuth consumer secret. Be
sure to upload your changed auth.xml
with ymdt!
<?xml version="1.0"?> <Application> <Service name="yahoo" type="OAuth"> <Consumer key = "insert key here" secret = "insert secret here"/> <Request url="https://api.login.yahoo.com/oauth/v2/get_request_token" method="GET"/> <Access url="https://api.login.yahoo.com/oauth/v2/get_token" method="GET"/> <Authorization url="https://api.login.yahoo.com/oauth/v2/request_auth"/> <Signature method="HMAC-SHA1"/> </Service> </Application>
<?xml version="1.0"?> <Application> <Service name="yahoo" type="OAuth"> <Consumer key = "insert key here" secret = "insert secret here"/> <Request url="https://api.login.yahoo.com/oauth/v2/get_request_token" method="GET"/> <Access url="https://api.login.yahoo.com/oauth/v2/get_token" method="GET"/> <Authorization url="https://api.login.yahoo.com/oauth/v2/request_auth"/> <Signature method="HMAC-SHA1"/> </Service> </Application>
Orchestrating User Authorizaton
assets/yql.js shows how to make a web service call to execute a YQL
query for private user data after orchestrating user authorization using
the AuthService
APIs.
Our
getAuthService API can be used to interact with web services that require authorization. In
the code example below, we illustrate how getAuthService() is used,
to get an object to profile "oauth" (which was created together with application configuration
in development tool).
callWebService API is then used on the auth object to make a signed call to the Webservice.
Other AuthService APIs are used to continue the flow of authorization during a signed call. getStatus API is used to check whether logging in is required. getUserAuthorizationURL API is used to redirect the user to complete authorization process. Once the authorization is complete, notifyAuthorizationReceived API is used to initiate the retrieval of authorization token from the third-party service. removeAuthorization API removes all the keys and reverts back to intial state.
YAHOO.namespace('openmail.samples.yql'); //Webservice client / user authorization wrapper // * instantiates AuthService, w/ Yahoo endpoint refinement if available // * controls UI interaction with user authorizaton call sequence YAHOO.openmail.samples.yql.ws = (function(){ //User interface abstraction from assets/ui.js var ui = YAHOO.openmail.samples.yql.ui; //This is how you get an AuthService instance that assumes we're running in //an app with Yahoo! endpoint refinements enabled. We'll fall back to an //'unrefined' AuthService instance that uses the keys from auth.xml if this //assumption turns out to be wrong. var auth = openmail.Application.getAuthService({}); //arguments for once we get around to AuthService::callWebService var service, handler; //AuthService.getStatus callback function onGotStatus(args){ if(args.error){ ui.onError(args.error); return; } //User hasn't yet authorized us to access their private data on the //endpoint. Fetch URL for the site where they can do that. if(args.data.status == 'authorization required'){ auth.getUserAuthorizationURL({ request : {}, authorization : { permissions : 'write' }}, onGotAuthURL); return; } //Status indicates we're in good shape so call YQL. auth.callWebService(service, handler); } //AuthService.getUserAuthorizationURL callback function onGotAuthURL(args){ if(args.error){ ui.onError(args.error); return; } //Open user authorization site in a new window, our UI abstraction will //call us back once the user presses a button indicating that they've //wrapped things up on the external site. ui.showAuthSite(args.data.url, onAuthorizationDone); } //ui callback after user has finished up with external site function onAuthorizationDone(){ //notify the auth service that we think we're good to go auth.notifyAuthorizationReceived(onNotificationAck); } //AuthService.notifyAuthorizationReceived callback function onNotificationAck(args){ ui.hideAuthSite(); if(args.error){ ui.onError(args.error); return; } //AuthService is happy now, try the ws call auth.callWebService(service, handler); } //AuthService.onRemoveAuthorization callback function onRemoveAuthorization(args){ if(args.error){ ui.onError(args.error); return; } } //AuthService.callWebService callback function onFirstCalledWebService(args){ //If a Yahoo! Mail App admin hasn't enabled the Yahoo endpoint //refinement for our app, fallback to orchestrating ourselves. if(args.error && auth.profile == '_yahoo_internal'){ auth = YAHOO.openmail.Application.getAuthService({ profile : 'yahoo' }); } if(args.error){ //see if user authorization needed, error doesn't say auth.getStatus(onGotStatus); return; } handler(args); } //fire off initial status check. If all goes well, we'll eventually //auth.callWebService. If we were certain we had the Yahoo! endpoint //refinement, we could just invoke callWebService, but we want this //app to be useable even w/o that refinement. return { call : function (_service, _handler){ service = _service; handler = _handler; auth.callWebService(service, onFirstCalledWebService); }, logout : function(){ ui.hide('logout'); ui.hideAuthSite(); auth.removeAuthorization(onRemoveAuthorization); } }; })(); //Invoked when user presses 'Go!' button query YQL with user input and dumps YQL //response to UI. YAHOO.openmail.samples.yql.query = (function(){ var ui = YAHOO.openmail.samples.yql.ui; var callWS = YAHOO.openmail.samples.yql.ws.call; function onYQL_Response(response){ var data; if(response.error){ data = '{ Result : "error querying YQL" }'; ui.onError(response.error); } else{ data = YAHOO.lang.JSON.parse(response.data); ui.show('logout'); ui.renderYQL(data); } } return function(){ ui.hide('results'); ui.hide('error'); callWS({ url : 'http://query.yahooapis.com/v1/yql', method: 'GET', parameters : {format : 'json', q : ui.getQuery()}}, onYQL_Response); }; })(); YAHOO.openmail.samples.yql.init = function(){ YAHOO.openmail.samples.yql.ui.init(); }; YAHOO.util.Event.onDOMReady(YAHOO.openmail.samples.yql.init);
YAHOO.namespace('openmail.samples.yql'); //Webservice client / user authorization wrapper // * instantiates AuthService, w/ Yahoo endpoint refinement if available // * controls UI interaction with user authorizaton call sequence YAHOO.openmail.samples.yql.ws = (function(){ //User interface abstraction from assets/ui.js var ui = YAHOO.openmail.samples.yql.ui; //This is how you get an AuthService instance that assumes we're running in //an app with Yahoo! endpoint refinements enabled. We'll fall back to an //'unrefined' AuthService instance that uses the keys from auth.xml if this //assumption turns out to be wrong. var auth = openmail.Application.getAuthService({}); //arguments for once we get around to AuthService::callWebService var service, handler; //AuthService.getStatus callback function onGotStatus(args){ if(args.error){ ui.onError(args.error); return; } //User hasn't yet authorized us to access their private data on the //endpoint. Fetch URL for the site where they can do that. if(args.data.status == 'authorization required'){ auth.getUserAuthorizationURL({ request : {}, authorization : { permissions : 'write' }}, onGotAuthURL); return; } //Status indicates we're in good shape so call YQL. auth.callWebService(service, handler); } //AuthService.getUserAuthorizationURL callback function onGotAuthURL(args){ if(args.error){ ui.onError(args.error); return; } //Open user authorization site in a new window, our UI abstraction will //call us back once the user presses a button indicating that they've //wrapped things up on the external site. ui.showAuthSite(args.data.url, onAuthorizationDone); } //ui callback after user has finished up with external site function onAuthorizationDone(){ //notify the auth service that we think we're good to go auth.notifyAuthorizationReceived(onNotificationAck); } //AuthService.notifyAuthorizationReceived callback function onNotificationAck(args){ ui.hideAuthSite(); if(args.error){ ui.onError(args.error); return; } //AuthService is happy now, try the ws call auth.callWebService(service, handler); } //AuthService.onRemoveAuthorization callback function onRemoveAuthorization(args){ if(args.error){ ui.onError(args.error); return; } } //AuthService.callWebService callback function onFirstCalledWebService(args){ //If a Yahoo! Mail App admin hasn't enabled the Yahoo endpoint //refinement for our app, fallback to orchestrating ourselves. if(args.error && auth.profile == '_yahoo_internal'){ auth = YAHOO.openmail.Application.getAuthService({ profile : 'yahoo' }); } if(args.error){ //see if user authorization needed, error doesn't say auth.getStatus(onGotStatus); return; } handler(args); } //fire off initial status check. If all goes well, we'll eventually //auth.callWebService. If we were certain we had the Yahoo! endpoint //refinement, we could just invoke callWebService, but we want this //app to be useable even w/o that refinement. return { call : function (_service, _handler){ service = _service; handler = _handler; auth.callWebService(service, onFirstCalledWebService); }, logout : function(){ ui.hide('logout'); ui.hideAuthSite(); auth.removeAuthorization(onRemoveAuthorization); } }; })(); //Invoked when user presses 'Go!' button query YQL with user input and dumps YQL //response to UI. YAHOO.openmail.samples.yql.query = (function(){ var ui = YAHOO.openmail.samples.yql.ui; var callWS = YAHOO.openmail.samples.yql.ws.call; function onYQL_Response(response){ var data; if(response.error){ data = '{ Result : "error querying YQL" }'; ui.onError(response.error); } else{ data = YAHOO.lang.JSON.parse(response.data); ui.show('logout'); ui.renderYQL(data); } } return function(){ ui.hide('results'); ui.hide('error'); callWS({ url : 'http://query.yahooapis.com/v1/yql', method: 'GET', parameters : {format : 'json', q : ui.getQuery()}}, onYQL_Response); }; })(); YAHOO.openmail.samples.yql.init = function(){ YAHOO.openmail.samples.yql.ui.init(); }; YAHOO.util.Event.onDOMReady(YAHOO.openmail.samples.yql.init);