SAPUI5 and Twitter API
Hi,
This blog will show how to use the Twitter REST API in a SAPUI5 application. In this example I will use the Twitter Search API (search/tweets.json).
All other API services can be found here: API Console Tool | Twitter Developers. (Log on with your twitter account)
Prerequisites:
– create a Twitter application (Twitter Application Management)
– retrieve: Customer Key, Customer Secret, Access Token and Access Token secret.
Creating a signature
This explains how to generate an OAuth 1.0a HMAC-SHA1 signature for a HTTPS request. This signature will be suitable for passing to the Twitter API as part of an authorized request.
first we have to declare some variables:
var customer_key = "xxxxxxxxxxxxxxxxxxxxxxxxxxx";
var customer_secret = "xxxxxxxxxxxxxxxxxxxxxxxxx";
var access_token= "xxxxxxxxx";
var access_token_secret = "xxxxxxxxxxx";
var signing_key = customer_secret + "&" + access_token_secret;
var signature_method = "HMAC-SHA1";
var authorization_version = "1.0";
var method = "GET";
var protocol = "https";
var server = "api.twitter.com";
var version = "1.1";
var service = "search/tweets.json";
STEP 1: Create base url:
The base URL is the URL to which the request is directed, minus any query string or hash parameters.
var BaseURL = protocol + "://" + server + "/" + version + "/" + service;
STEP 2: Collect your parameters:
Next, gather all of the parameters included in the request.
var callback = "callback=twitterCallback";
var count = "count=" + this.count;
var language = "lang=" + this.lang;
var oauth_consumer_key = "oauth_consumer_key=" + customer_key + "&";
var oauth_nonce = "oauth_nonce=" + this.makeid() + "&";
var oauth_signature_method = "oauth_signature_method=" + signature_method + "&";
var oauth_timestamp = "oauth_timestamp=" + Math.floor(Date.now() / 1000) + "&";
var oauth_token = "oauth_token=" + access_token + "&";
var oauth_version = "oauth_version=" + authorization_version + "&";
var query = "q=" + encodeURIComponent(oEvent.getParameter("query"));
var result_type = "result_type=" + this.resultType;
oauth_nonce must be a generated string. this.makeid() is a method that generates a string of 10 characters.
STEP 3: create authorization parameter string and search options:
var oauth_parameters = oauth_consumer_key + oauth_nonce + oauth_signature_method + oauth_timestamp + oauth_token + oauth_version;
var searchOption = query + "&" + count + "&" + result_type;
STEP 4: Create parameterstring:
this is a very important step: concatenate all parameters alphabetically:
var parametersString = callback + "&" + count + "&" + language + "&" + oauth_parameters + query + "&" + result_type;
STEP 5: Create signature string:
- encode the complete parameter string
- encode the base url
- append the method. (method in upper case!!!)
var signatureBaseString = method + "&" + encodeURIComponent(BaseURL) + "&" + encodeURIComponent(parametersString);
it should look like this:
GET&https%3A%2F%2Fapi.twitter.com%2F1.1%2Fsearch%2Ftweets.json&callback%3DtwitterCallback%26count%3D100%26lang%3Dnl%26oauth_consumer_key%3Da5IGWlSrrLyI7GEPV5ZMY1MiP%26oauth_nonce%3DoJV2FF2PtC%26oauth_signature_method%3DHMAC-SHA1%26oauth_timestamp%3D1453384662%26oauth_token%3D2744465306-RGKnQQl5l5p1bZBNlUqbYrEROBqvZx6ij5ZIOos%26oauth_version%3D1.0%26q%3Datos%26result_type%3Dmixed
STEP 6: Calculating the signature:
In this step we create the authorization signature. The signature is calculated, based on the encoded signatureBaseString and the signing_key.
You can find a lot of HMAC SHA1 hashing algoritms on the internet, but I used the algoritms from CryptoJS ( crypto-js – JavaScript implementations of standard and secure cryptographic algorithms – Google Project Hosting )
these 2 are needed:
- http://crypto-js.googlecode.com/svn/tags/3.1.2/build/rollups/hmac-sha1.js
- http://crypto-js.googlecode.com/svn/tags/3.1.2/build/components/enc-base64-min.js
I downloaded them to my project folder.
jQuery.sap.require("TwitterSearch_v002.util.HMACsha1");
jQuery.sap.require("TwitterSearch_v002.util.EncodeBase64");
First I used:
jQuery.sap.includeScript("http://crypto-js.googlecode.com/svn/tags/3.1.2/build/components/enc-base64-min.js");
jQuery.sap.includeScript("http://crypto-js.googlecode.com/svn/tags/3.1.2/build/rollups/hmac-sha1.js");
but I get the message:
Mixed Content: The page at ‘https://webidetesting1208672-s0009219687trial.dispatcher.hanatrial.ondemand…onentPreload=off&origional-url=index.html&sap-ui-appCacheBuster=..%2F..%2F‘ was loaded over HTTPS, but requested an insecure script ‘http://crypto-js.googlecode.com/svn/tags/3.1.2/build/components/enc-base64-min.js‘. This request has been blocked; the content must be served over HTTPS.t @ sap-ui-core.js:88
sap-ui-core.js:88 Mixed Content: The page at ‘https://webidetesting1208672-s0009219687trial.dispatcher.hanatrial.ondemand…onentPreload=off&origional-url=index.html&sap-ui-appCacheBuster=..%2F..%2F‘ was loaded over HTTPS, but requested an insecure script ‘http://crypto-js.googlecode.com/svn/tags/3.1.2/build/rollups/hmac-sha1.js‘. This request has been blocked; the content must be served over HTTPS.
getting the signature:
var hash = CryptoJS.HmacSHA1(signatureBaseString, signing_key);
var base64String = hash.toString(CryptoJS.enc.Base64);
var oauth_signature = encodeURIComponent(base64String);
STEP 7: Build the url
ok, so now we have everything to build our url, to call the API:
concatenate as follow:
var URL = BaseURL + "?" + searchOption + "&" + oauth_parameters + "oauth_signature=" + oauth_signature + "&" + language + "&" + callback;
So now we have the url to retrieve the data.
Getting the data
I use JSONP to retrieve the data via a callback function. You can also use an xmlhttprequest (in JQuery or Ajax) but I got an access-control-allow-origin error due to CORS problems…(I am running my app on the HANA Cloud Platform) But luckily Twitter supports the use of JSONP. In step 4 ,the parameter “callback” was added to the parameter string. The name of the function can be chosen. In this example I use “twitterCallback”. Don’t forget to add this parameter in the parameter string!!
So, JSONP: first you have to inject the script in the header of the page:
var socialGetter = (function() {
/* just a utility to do the script injection */
function addScript(url) {
var script = document.createElement('script');
script.async = true;
script.src = url;
document.body.appendChild(script);
}
return {
getTwitterTweets: function(url) {
addScript(url);
}
};
})();
then, you have to define the callback function:
window.twitterCallback = function(data) {
if (data) {
var twitterResult = new sap.ui.model.json.JSONModel();
twitterResult.setData(data);
sap.ui.getCore().byId("__xmlview0").setModel(twitterResult, "twitterResult");
}
};
The output format is JSON, so I use sap.ui.model.json.JSONModel.
notice that I use “window.twitterCallback”. this is because a callback method is always a global function, so you have to declare a global function.
with this code:
sap.ui.getCore().byId("__xmlview0").setModel(twitterResult, "twitterResult")
;
I retrieve the search view and set the model “twitterResult”.
ok, so now we can call the url:
socialGetter.getTwitterTweets(URL);
View
In the view I use a list of objectListItems:
<List busyIndicatorDelay="{masterView>/delay}" growing="true" growingScrollToLoad="true" id="list"
items="{ path: 'twitterResult>/statuses', sorter: { path: 'accountID', descending: false }, groupHeaderFactory: '.createGroupHeader' }"
mode="{= ${device>/system/phone} ? 'None' : 'SingleSelectMaster'}" noDataText="{masterView>/noDataText}" selectionChange="onSelectionChange"
updateFinished="onUpdateFinished">
<infoToolbar>
<Toolbar active="true" id="filterBar" press="onOpenViewSettings" visible="{masterView>/isFilterBarVisible}">
<Title id="filterBarLabel" text="{masterView>/filterBarLabel}"/>
</Toolbar>
</infoToolbar>
<items>
<ObjectListItem icon="{twitterResult>user/profile_image_url}"
intro="{twitterResult>user/name} - {twitterResult>created_at} - {twitterResult>user/location}" press="onSelectionChange"
title="{twitterResult>text}" type="{= ${device>/system/phone} ? 'Active' : 'Inactive'}"></ObjectListItem>
</items>
</List>
an example of a tweet looks like this (in JSON):
"statuses": [
{
"metadata": {
"iso_language_code": "en",
"result_type": "recent"
},
"created_at": "Thu Jan 21 14:47:27 +0000 2016",
"id": 690183933741310000,
"id_str": "690183933741309954",
"text": "Throwback Thursday // NYC shoot with elite_e46 for @pbmwmagazine // #meatyflush #bmw #bmwusa… https://t.co/7w1x7293Sf",
"source": "<a href="http://instagram.com" rel="nofollow">Instagram</a>",
"truncated": false,
"in_reply_to_status_id": null,
"in_reply_to_status_id_str": null,
"in_reply_to_user_id": null,
"in_reply_to_user_id_str": null,
"in_reply_to_screen_name": null,
"user": {
"id": 2418867279,
"id_str": "2418867279",
"name": "MeatyFlush",
"screen_name": "MeatyFlush",
"location": "DMV",
"description": "We are a photography collective who's sole purpose is to capture the car scene featuring some of the hottest and greatest automobiles in the DMV.",
"url": "http://t.co/lk0XSiqxNR",
"entities": {
"url": {
"urls": [
{
"url": "http://t.co/lk0XSiqxNR",
"expanded_url": "http://meatyflush.com",
"display_url": "meatyflush.com",
"indices": [
0,
22
]
}
]
},
"description": {
"urls": []
}
},
"protected": false,
"followers_count": 57,
"friends_count": 14,
"listed_count": 14,
"created_at": "Tue Mar 18 01:02:57 +0000 2014",
"favourites_count": 19,
"utc_offset": null,
"time_zone": null,
"geo_enabled": false,
"verified": false,
"statuses_count": 1552,
"lang": "en",
"contributors_enabled": false,
"is_translator": false,
"is_translation_enabled": false,
"profile_background_color": "C0DEED",
"profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png",
"profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png",
"profile_background_tile": false,
"profile_image_url": "http://pbs.twimg.com/profile_images/519541485772214272/aKA7PMCO_normal.jpeg",
"profile_image_url_https": "https://pbs.twimg.com/profile_images/519541485772214272/aKA7PMCO_normal.jpeg",
"profile_banner_url": "https://pbs.twimg.com/profile_banners/2418867279/1395120295",
"profile_link_color": "0084B4",
"profile_sidebar_border_color": "C0DEED",
"profile_sidebar_fill_color": "DDEEF6",
"profile_text_color": "333333",
"profile_use_background_image": true,
"has_extended_profile": false,
"default_profile": true,
"default_profile_image": false,
"following": false,
"follow_request_sent": false,
"notifications": false
},
"geo": null,
"coordinates": null,
"place": null,
"contributors": null,
"is_quote_status": false,
"retweet_count": 0,
"favorite_count": 0,
"entities": {
"hashtags": [
{
"text": "meatyflush",
"indices": [
70,
81
]
},
{
"text": "bmw",
"indices": [
82,
86
]
},
{
"text": "bmwusa",
"indices": [
87,
94
]
}
],
"symbols": [],
"user_mentions": [
{
"screen_name": "PBMWmagazine",
"name": "PBMW",
"id": 169887768,
"id_str": "169887768",
"indices": [
53,
66
]
}
],
"urls": [
{
"url": "https://t.co/7w1x7293Sf",
"expanded_url": "https://www.instagram.com/p/BAzhLBmPkIE/",
"display_url": "instagram.com/p/BAzhLBmPkIE/",
"indices": [
96,
119
]
}
]
},
"favorited": false,
"retweeted": false,
"possibly_sensitive": false,
"lang": "en"
}
the output looks like this:
Enjoy!!
bye 😉
hi Andy,
you should never do what you just did, unless you are doing such for the only reason of testing purposes.. it does seem that this is a test account you created thou.. you should do all key calculations server side.
exposing any sort of keys in javascript is the same as publishing them for any one to interact with your account (or the account in question) - worst case scenario someone would bomb it to have the rates down to zero making it unusable.
Cheers,
Dan.
hi,
Yes indeed. it is just a test sample of how to use it in javascript. (and how to encript your API request)
It is usefull for a demo or a proof of concept.
In real casses you should implement the search on the server side. (because the customer_key, secret,... must be private)
this is an interesting blog of how to implement it on server side in abap:
Twibap: the ABAP Twitter API
KR.
Andy,
May be you could have used Twitter Widget to make it simple?
Enhance your Fiori app with a Twitter widget
Hi Andy,
Can you please provide the complete project folder?
Hi Andy,
can you please provide the entire project as a zip file.
Thanks in advance.
Hello,
I have followed your instruction but it is not working. Please provide this project in zip file.
Thanks