Skip to content
This repository was archived by the owner on Jan 10, 2024. It is now read-only.

Commit 85dcad6

Browse files
author
Pat Patterson
committed
Added persistent storage of refresh token to the PhoneGap sample app, which
necessitated changes in forcetk.js, which rippled into the other apps. Changes are minor - you just need to call setSessionToken() after constructing the client.
1 parent c0c66fe commit 85dcad6

File tree

7 files changed

+242
-152
lines changed

7 files changed

+242
-152
lines changed

README.markdown

Lines changed: 10 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -39,8 +39,9 @@ Your Visualforce page will need to include jQuery and the toolkit, then create a
3939
// Get a reference to jQuery that we can work with
4040
$j = jQuery.noConflict();
4141
42-
// Get an instance of the REST API client
43-
var client = new forcetk.Client('{!$Api.Session_ID}');
42+
// Get an instance of the REST API client and set the session ID
43+
var client = new forcetk.Client();
44+
client.setSessionToken('{!$Api.Session_ID}');
4445
4546
client.query("SELECT Name FROM Account LIMIT 1", function(response){
4647
$j('#accountname').html(response.records[0].Name);
@@ -70,9 +71,7 @@ Your HTML page will need to include jQuery and the toolkit, then create a client
7071
var redirectUri = 'PATH_TO_YOUR_APP/oauthcallback.html';
7172
var proxyUrl = 'PATH_TO_YOUR_APP/proxy.php?mode=native';
7273

73-
// We'll get an instance of the REST API client in a callback
74-
// after we do OAuth
75-
var client = null;
74+
var client = new forcetk.Client(clientId, loginUrl, proxyUrl);
7675

7776
$(document).ready(function() {
7877
$('#message').popupWindow({
@@ -95,8 +94,8 @@ Your HTML page will need to include jQuery and the toolkit, then create a client
9594
|| typeof oauthResponse['access_token'] === 'undefined') {
9695
$('#message').html('Error - unauthorized!');
9796
} else {
98-
client = new forcetk.Client(oauthResponse.access_token,
99-
null, oauthResponse.instance_url, proxyUrl);
97+
client.setSessionToken(oauthResponse.access_token, null,
98+
oauthResponse.instance_url);
10099

101100
client.query("SELECT Name FROM Account LIMIT 1",
102101
function(response){
@@ -131,9 +130,7 @@ An absolutely minimal sample using OAuth to obtain a session ID is:
131130
var clientId = 'YOUR_CLIENT_ID';
132131
var redirectUri = 'https://login.salesforce.com/services/oauth2/success';
133132

134-
// We'll get an instance of the REST API client in a callback
135-
// after we do OAuth
136-
var client = null;
133+
var client = new forcetk.Client(clientId, loginUrl);
137134

138135
$(document).ready(function() {
139136
var cb = ChildBrowser.install();
@@ -176,7 +173,8 @@ An absolutely minimal sample using OAuth to obtain a session ID is:
176173
responseText: 'No OAuth response'
177174
});
178175
} else {
179-
client = new forcetk.Client(oauthResponse.access_token, null, oauthResponse.instance_url);
176+
client.setSessionToken(oauthResponse.access_token, null,
177+
oauthResponse.instance_url);
180178
181179
client.query("SELECT Name FROM Account LIMIT 1",
182180
function(response){
@@ -190,5 +188,5 @@ An absolutely minimal sample using OAuth to obtain a session ID is:
190188
<p id="message">Click here.</p>
191189
</html>
192190

193-
A fully featured sample is provided in [phonegap.html](Force.com-JavaScript-REST-Toolkit/blob/master/phonegap.html).
191+
A fully featured sample (including persistence of the OAuth refresh token to the iOS Keychain) is provided in [phonegap.html](Force.com-JavaScript-REST-Toolkit/blob/master/phonegap.html).
194192

example.html

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -55,9 +55,7 @@
5555
var redirectUri = 'https://localhost/jsapp/oauthcallback.html';
5656
var proxyUrl = 'https://localhost/jsapp/proxy.php?mode=native';
5757

58-
// We'll get an instance of the REST API client in a callback after we do
59-
// OAuth
60-
var client = null;
58+
var client = new forcetk.Client(clientId, loginUrl, proxyUrl);
6159

6260
var ajaxgif = "<img src='static/ajax.gif' />";
6361

@@ -104,8 +102,8 @@
104102
responseText: 'No OAuth response'
105103
});
106104
} else {
107-
client = new forcetk.Client(oauthResponse.access_token, null,
108-
oauthResponse.instance_url, proxyUrl);
105+
client.setSessionToken(oauthResponse.access_token, null,
106+
oauthResponse.instance_url);
109107

110108
// Kick things off by doing a describe on Account...
111109
$j('#prompt').html(ajaxgif+" loading metadata...");

example.page

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,8 +51,9 @@ Sample Visualforce page for Force.com JavaScript REST Toolkit
5151
// Get a reference to jQuery that we can work with
5252
$j = jQuery.noConflict();
5353

54-
// Get an instance of the REST API client
55-
var client = new forcetk.Client('{!$Api.Session_ID}');
54+
// Get an instance of the REST API client and set the session ID
55+
var client = new forcetk.Client();
56+
client.setSessionToken('{!$Api.Session_ID}');
5657

5758
var ajaxgif = "<img src='{!URLFOR($Resource.sample, '/ajax.gif')}' />";
5859

forcetk.js

Lines changed: 93 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -31,58 +31,107 @@
3131
* https://na1.salesforce.com/ or similar) as a remote site - in the admin
3232
* console, go to Your Name | Setup | Security Controls | Remote Site Settings
3333
*/
34-
34+
3535
var forcetk = window.forcetk;
3636

3737
if (forcetk === undefined) {
3838
forcetk = {};
3939
}
40-
40+
4141
if (forcetk.Client === undefined) {
42-
43-
// We use $j rather than $ for jQuery so it works in Visualforce
44-
if (window.$j === undefined) {
45-
$j = $;
46-
}
42+
43+
// We use $j rather than $ for jQuery so it works in Visualforce
44+
if (window.$j === undefined) {
45+
$j = $;
46+
}
4747

4848
/**
4949
* The Client provides a convenient wrapper for the Force.com REST API,
5050
* allowing JavaScript in Visualforce pages to use the API via the Ajax
5151
* Proxy.
52+
* @param [clientId=null] 'Consumer Key' in the Remote Access app settings
53+
* @param [loginUrl='https://login.salesforce.com/'] Login endpoint
54+
* @param [proxyUrl=null] Proxy URL. Omit if running on Visualforce or
55+
* PhoneGap etc
56+
* @constructor
57+
*/
58+
forcetk.Client = function(clientId, loginUrl, proxyUrl) {
59+
this.clientId = clientId;
60+
this.loginUrl = loginUrl || 'https://login.salesforce.com/';
61+
if (typeof proxyUrl === 'undefined' || proxyUrl === null) {
62+
if (location.protocol === 'file:') {
63+
// In PhoneGap
64+
this.proxyUrl = null;
65+
} else {
66+
// In Visualforce
67+
this.proxyUrl = location.protocol + "//" + location.hostname
68+
+ "/services/proxy";
69+
}
70+
this.authzHeader = "Authorization";
71+
} else {
72+
// On a server outside VF
73+
this.proxyUrl = proxyUrl;
74+
this.authzHeader = "X-Authorization";
75+
}
76+
this.refreshToken = null;
77+
this.sessionId = null;
78+
this.apiVersion = null;
79+
this.instanceUrl = null;
80+
}
81+
82+
/**
83+
* Set a refresh token in the client.
84+
* @param refreshToken an OAuth refresh token
85+
*/
86+
forcetk.Client.prototype.setRefreshToken = function(refreshToken) {
87+
this.refreshToken = refreshToken;
88+
}
89+
90+
/**
91+
* Refresh the access token.
92+
* @param callback function to call on success
93+
* @param error function to call on failure
94+
*/
95+
forcetk.Client.prototype.refreshAccessToken = function(callback, error) {
96+
var that = this;
97+
var url = this.loginUrl + '/services/oauth2/token';
98+
$j.ajax({
99+
type: 'POST',
100+
url: (this.proxyUrl !== null) ? this.proxyUrl: url,
101+
processData: false,
102+
data: 'grant_type=refresh_token&client_id=' + this.clientId + '&refresh_token=' + this.refreshToken,
103+
success: callback,
104+
error: error,
105+
dataType: "json",
106+
beforeSend: function(xhr) {
107+
if (that.proxyUrl !== null) {
108+
xhr.setRequestHeader('SalesforceProxy-Endpoint', url);
109+
}
110+
}
111+
});
112+
}
113+
114+
/**
115+
* Set a session token and the associated metadata in the client.
52116
* @param sessionId a salesforce.com session ID. In a Visualforce page,
53117
* use '{!$Api.sessionId}' to obtain a session ID.
54118
* @param [apiVersion="21.0"] Force.com API version
55-
* @constructor
119+
* @param [instanceUrl] Omit this if running on Visualforce; otherwise
120+
* use the value from the OAuth token.
56121
*/
57-
forcetk.Client = function(sessionId, apiVersion, instanceUrl, proxyUrl) {
122+
forcetk.Client.prototype.setSessionToken = function(sessionId, apiVersion, instanceUrl) {
58123
this.sessionId = sessionId;
59124
this.apiVersion = (typeof apiVersion === 'undefined' || apiVersion == null)
60-
? 'v21.0' : apiVersion;
125+
? 'v21.0': apiVersion;
61126
if (typeof instanceUrl === 'undefined' || instanceUrl == null) {
62127
// location.hostname can be of the form 'abc.na1.visual.force.com' or
63128
// 'na1.salesforce.com'. Split on '.', and take the [1] or [0] element
64129
// as appropriate
65130
var elements = location.hostname.split(".");
66131
var instance = (elements.length == 3) ? elements[0] : elements[1];
67-
this.instance_url = "https://"+instance+".salesforce.com";
68-
} else {
69-
this.instance_url = instanceUrl;
70-
}
71-
if (typeof proxyUrl === 'undefined' || proxyUrl === null) {
72-
if (location.protocol === 'file:'){
73-
// In PhoneGap
74-
this.proxy_url = null;
75-
this.authzHeader = null;
76-
} else {
77-
// In Visualforce
78-
this.proxy_url = location.protocol + "//" + location.hostname
79-
+ "/services/proxy";
80-
this.authzHeader = "Authorization";
81-
}
132+
this.instanceUrl = "https://" + instance + ".salesforce.com";
82133
} else {
83-
// On a server outside VF
84-
this.proxy_url = proxyUrl;
85-
this.authzHeader = "X-Authorization";
134+
this.instanceUrl = instanceUrl;
86135
}
87136
}
88137

@@ -94,23 +143,28 @@ if (forcetk.Client === undefined) {
94143
* @param [method="GET"] HTTP method for call
95144
* @param [payload=null] payload for POST/PATCH etc
96145
*/
97-
forcetk.Client.prototype.ajax = function(path, callback, error, method, payload) {
146+
forcetk.Client.prototype.ajax = function(path, callback, error, method, payload, retry) {
98147
var that = this;
99-
var url = this.instance_url + '/services/data' + path;
148+
var url = this.instanceUrl + '/services/data' + path;
100149

101150
$j.ajax({
102-
type: (typeof method === 'undefined' || method == null)
103-
? "GET" : method,
104-
url: (this.proxy_url !== null) ? this.proxy_url : url,
151+
type: method || "GET",
152+
url: (this.proxyUrl !== null) ? this.proxyUrl: url,
105153
contentType: 'application/json',
106154
processData: false,
107-
data: (typeof payload === 'undefined' || payload == null)
108-
? null : payload,
155+
data: payload,
109156
success: callback,
110-
error: error,
157+
error: (!this.refreshToken || retry ) ? error : function() {
158+
that.refreshAccessToken(function(oauthResponse) {
159+
that.setSessionToken(oauthResponse.access_token, null,
160+
oauthResponse.instance_url);
161+
that.ajax(path, callback, error, method, payload, true);
162+
},
163+
error);
164+
},
111165
dataType: "json",
112166
beforeSend: function(xhr) {
113-
if (that.proxy_url !== null) {
167+
if (that.proxyUrl !== null) {
114168
xhr.setRequestHeader('SalesforceProxy-Endpoint', url);
115169
}
116170
xhr.setRequestHeader(that.authzHeader, "OAuth " + that.sessionId);
@@ -168,7 +222,7 @@ if (forcetk.Client === undefined) {
168222
* @param [error=null] function to which jqXHR will be passed in case of error
169223
*/
170224
forcetk.Client.prototype.describe = function(objtype, callback, error) {
171-
this.ajax('/' + this.apiVersion + '/sobjects/' + objtype
225+
this.ajax('/' + this.apiVersion + '/sobjects/' + objtype
172226
+ '/describe/', callback, error);
173227
}
174228

@@ -196,7 +250,7 @@ if (forcetk.Client === undefined) {
196250
* @param [error=null] function to which jqXHR will be passed in case of error
197251
*/
198252
forcetk.Client.prototype.retrieve = function(objtype, id, fieldlist, callback, error) {
199-
this.ajax('/' + this.apiVersion + '/sobjects/' + objtype + '/' + id
253+
this.ajax('/' + this.apiVersion + '/sobjects/' + objtype + '/' + id
200254
+ '?fields=' + fieldlist, callback, error);
201255
}
202256

mobile.html

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@
5050

5151
// We'll get an instance of the REST API client in a callback after we do
5252
// OAuth
53-
var client = null;
53+
var client = new forcetk.Client(clientId, loginUrl, proxyUrl);;
5454

5555
// We use $j rather than $ for jQuery
5656
if (window.$j === undefined) {
@@ -83,8 +83,8 @@
8383
responseText: 'No OAuth response'
8484
});
8585
} else {
86-
client = new forcetk.Client(oauthResponse.access_token, null,
87-
oauthResponse.instance_url, proxyUrl);
86+
client.setSessionToken(oauthResponse.access_token, null,
87+
oauthResponse.instance_url);
8888

8989
addClickListeners();
9090

mobile.page

Lines changed: 11 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -40,28 +40,22 @@ an HTML5 mobile app using jQuery Mobile
4040
<title>Accounts</title>
4141
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
4242
<!--
43-
For development, you may want to load jQuery/jQuery Mobile from their CDN.
43+
You must download jQuery and jQuery Mobile and put them in a
44+
static resource bundle like this to be able to correctly control ordering
45+
of JS includes.
4446
-->
45-
<link rel="stylesheet" href="http://code.jquery.com/mobile/1.0a4/jquery.mobile-1.0a4.min.css" />
46-
<script type="text/javascript" src="http://code.jquery.com/jquery-1.5.2.min.js"></script>
47-
<script type="text/javascript" src="http://code.jquery.com/mobile/1.0a4/jquery.mobile-1.0a4.min.js"></script>
48-
<!--
49-
Alternatively, you could download jQuery and jQuery Mobile, and put them in a
50-
static resource bundle like this. Don't forget the images for the CSS!
51-
<apex:stylesheet value="{!URLFOR($Resource.static, '/mobile/jquery.mobile-1.0a4.min.css')}" />
52-
<apex:includeScript value="{!URLFOR($Resource.static, '/jquery-1.5.2.min.js')}" />
53-
<apex:includeScript value="{!URLFOR($Resource.static, '/mobile/jquery.mobile-1.0a4.min.js')}" />
54-
-->
55-
<apex:includeScript
56-
value="{!URLFOR($Resource.sample, 'forcetk.js')}" />
57-
<apex:includeScript
58-
value="{!URLFOR($Resource.sample, 'mobileapp.js')}" />
47+
<apex:stylesheet value="{!URLFOR($Resource.static, 'static/jquery.mobile-1.0a4.min.css')}" />
48+
<apex:includeScript value="{!URLFOR($Resource.static, 'static/jquery-1.5.2.min.js')}" />
49+
<apex:includeScript value="{!URLFOR($Resource.static, 'static/jquery.mobile-1.0a4.min.js')}" />
50+
<apex:includeScript value="{!URLFOR($Resource.sample, 'forcetk.js')}" />
51+
<apex:includeScript value="{!URLFOR($Resource.sample, 'mobileapp.js')}" />
5952
<script type="application/javascript">
6053
// Get a reference to jQuery that we can work with
6154
$j = jQuery.noConflict();
6255

63-
// Get an instance of the REST API client
64-
var client = new forcetk.Client('{!$Api.Session_ID}');
56+
// Get an instance of the REST API client and set the session ID
57+
var client = new forcetk.Client();
58+
client.setSessionToken('{!$Api.Session_ID}');
6559

6660
// Kick things off...
6761
$j(document).ready(function(){

0 commit comments

Comments
 (0)