forked from anthonyreilly/NetCoreForce
-
Notifications
You must be signed in to change notification settings - Fork 0
/
UriFormatter.cs
381 lines (306 loc) · 16.8 KB
/
UriFormatter.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
using System;
using System.Collections.Generic;
using NetCoreForce.Client.Models;
namespace NetCoreForce.Client
{
/*
In each case, the URI for the resource
follows the base URI, which you retrieve from the authentication service: http://domain/services/data.
https://developer.salesforce.com/docs/atlas.en-us.api_rest.meta/api_rest/resources_list.htm
*/
public static class UriFormatter
{
const string BaseUriSegment = "services/data";
/// <summary>
/// SF Base URI
/// </summary>
/// <param name="instanceUrl"></param>
public static Uri BaseUri(string instanceUrl)
{
// e.g. https://na99.salesforce.com/services/data
if (string.IsNullOrEmpty(instanceUrl)) throw new ArgumentNullException("instanceUrl");
return new Uri(new Uri(instanceUrl), BaseUriSegment);
}
/// <summary>
/// Versions
/// </summary>
/// <param name="instanceUrl"></param>
public static Uri Versions(string instanceUrl)
{
if (string.IsNullOrEmpty(instanceUrl)) throw new ArgumentNullException("instanceUrl");
// format: /
Uri uri = new Uri(new Uri(instanceUrl), BaseUriSegment);
return uri;
}
//TODO: Resources By Version https://developer.salesforce.com/docs/atlas.en-us.api_rest.meta/api_rest/resources_discoveryresource.htm
/// <summary>
/// Limits Resource URL
/// <para>Use the Limits resource to list limits information for your organization.</para>
/// </summary>
public static Uri Limits(string instanceUrl, string apiVersion)
{
if (string.IsNullOrEmpty(instanceUrl)) throw new ArgumentNullException("instanceUrl");
if (string.IsNullOrEmpty(apiVersion)) throw new ArgumentNullException("apiVersion");
//format: /vXX.X/limits/
// return new Uri(new Uri(instanceUrl), string.Format("services/data/{0}/limits", apiVersion));
return new Uri(BaseUri(instanceUrl), LimitsResource(apiVersion));
}
//split off full URL formatter and resource relative url formatters?
//batch/tree reqs may need relative url parsers
/// <summary>
/// Limits Resource
/// <para>Use the Limits resource to list limits information for your organization.</para>
/// <para>format: /vXX.X/limits/</para>
/// </summary>
public static Uri LimitsResource(string apiVersion)
{
if (string.IsNullOrEmpty(apiVersion)) throw new ArgumentNullException("apiVersion");
return new Uri(string.Format("/{0}/limits/", apiVersion), UriKind.Relative);
}
/// <summary>
/// Describe Global
/// Use the Describe Global resource to list the objects available in your org and available to the logged-in user.
/// This resource also returns the org encoding, as well as maximum batch size permitted in queries.
/// </summary>
public static Uri DescribeGlobal(string instanceUrl, string apiVersion)
{
if (string.IsNullOrEmpty(instanceUrl)) throw new ArgumentNullException("instanceUrl");
if (string.IsNullOrEmpty(apiVersion)) throw new ArgumentNullException("apiVersion");
//format: /vXX.X/sobjects/
Uri uri = new Uri(new Uri(instanceUrl), string.Format("{0}/{1}/sobjects", BaseUriSegment, apiVersion));
return uri;
}
/// <summary>
/// SObject Basic Information
/// Describes the individual metadata for the specified object. Can also be used to create a new record for a given object.
/// </summary>
public static Uri SObjectBasicInformation(string instanceUrl, string apiVersion, string sObjectName)
{
if (string.IsNullOrEmpty(sObjectName)) throw new ArgumentNullException("sObjectName");
if (string.IsNullOrEmpty(instanceUrl)) throw new ArgumentNullException("instanceUrl");
if (string.IsNullOrEmpty(apiVersion)) throw new ArgumentNullException("apiVersion");
//format: /vXX.X/sobjects/SObjectName/
Uri uri = new Uri(new Uri(instanceUrl), string.Format("{0}/{1}/sobjects/{2}", BaseUriSegment, apiVersion, sObjectName));
return uri;
}
/// <summary>
/// SObject Describe
/// Completely describes the individual metadata at all levels for the specified object.
/// </summary>
public static Uri SObjectDescribe(string instanceUrl, string apiVersion, string sObjectName)
{
if (string.IsNullOrEmpty(sObjectName)) throw new ArgumentNullException("sObjectName");
if (string.IsNullOrEmpty(instanceUrl)) throw new ArgumentNullException("instanceUrl");
if (string.IsNullOrEmpty(apiVersion)) throw new ArgumentNullException("apiVersion");
//format: /vXX.X/sobjects/SObjectName/describe/
Uri uri = new Uri(new Uri(instanceUrl), string.Format("services/data/{0}/sobjects/{1}/describe", apiVersion, sObjectName));
return uri;
}
//TODO: SObject Get Deleted
//TODO: SObject Get Updated
//TODO: SObject Named Layouts
/// <summary>
/// SObject Rows Resource
/// Used for: Update, Delete, Field values
/// </summary>
/// <param name="instanceUrl">SFDC instance URL, e.g. "https://na99.salesforce.com"</param>
/// <param name="apiVersion">SFDC API version, e.g. "v41.0"</param>
/// <param name="sObjectName">SObject name, e.g. "Account"</param>
/// <param name="objectId">SObject ID</param>
/// <param name="fields">(optional) "fields" parameter, a list of object fields for GET requests</param>
/// <returns></returns>
public static Uri SObjectRows(string instanceUrl, string apiVersion, string sObjectName, string objectId, List<string> fields = null)
{
if (string.IsNullOrEmpty(instanceUrl)) throw new ArgumentNullException("instanceUrl");
if (string.IsNullOrEmpty(apiVersion)) throw new ArgumentNullException("apiVersion");
if (string.IsNullOrEmpty(sObjectName)) throw new ArgumentNullException("sObjectName");
if (string.IsNullOrEmpty(objectId)) throw new ArgumentNullException("objectId");
//https://developer.salesforce.com/docs/atlas.en-us.api_rest.meta/api_rest/resources_sobject_retrieve.htm
// format: /vXX.X/sobjects/SObjectName/id/
// example with field parameter: services/data/v20.0/sobjects/Account/001D000000INjVe?fields=AccountNumber,BillingPostalCode
Uri uri = new Uri(new Uri(instanceUrl), string.Format("services/data/{0}/sobjects/{1}/{2}", apiVersion, sObjectName, objectId));
if (fields != null && fields.Count > 0)
{
string fieldList = string.Join(",", fields);
uri = new Uri(QueryHelpers.AddQueryString(uri.ToString(), "fields", fieldList));
}
return uri;
}
/// <summary>
/// SObject Rows by External ID
/// Creates new records or updates existing records (upserts records) based on the value of a specified external ID field.
/// </summary>
public static Uri SObjectRowsByExternalId(string instanceUrl, string apiVersion, string sObjectName, string fieldName, string fieldValue)
{
//format: /vXX.X/sobjects/SObjectName/fieldName/fieldValue
Uri uri = new Uri(new Uri(instanceUrl), string.Format("services/data/{0}/sobjects/{1}/{2}/{3}", apiVersion, sObjectName, fieldName, fieldValue));
return uri;
}
//SObject Relationships Resource
//TODO: Traverse Relationships with Friendly URLs.
//https://developer.salesforce.com/docs/atlas.en-us.api_rest.meta/api_rest/resources_sobject_relationships.htm#topic-title
///format: vXX.X/sobjects/SObject/id/relationship field name
/// <summary>
/// SObject Rows
/// </summary>
public static Uri SObjectBlobRetrieve(string instanceUrl, string apiVersion, string sObjectName, string objectId, string blobField = "body")
{
if (string.IsNullOrEmpty(instanceUrl)) throw new ArgumentNullException("instanceUrl");
if (string.IsNullOrEmpty(apiVersion)) throw new ArgumentNullException("apiVersion");
if (string.IsNullOrEmpty(sObjectName)) throw new ArgumentNullException("sObjectName");
if (string.IsNullOrEmpty(objectId)) throw new ArgumentNullException("objectId");
if (string.IsNullOrEmpty(blobField)) throw new ArgumentNullException("blobField");
//format: /vXX.X/sobjects/SObjectName/id/
// https://yourInstance.salesforce.com/services/data/v20.0/sobjects/Attachment/001D000000INjVe/body
// https://yourInstance.salesforce.com/services/data/v20.0/sobjects/Document/015D0000000NdJOIA0/body
Uri uri = new Uri(new Uri(instanceUrl), string.Format("services/data/{0}/sobjects/{1}/{2}/{3}", apiVersion, sObjectName, objectId, blobField));
return uri;
}
/// <summary>
/// SOQL Query
/// </summary>
public static Uri Query(string instanceUrl, string apiVersion, string query, bool queryAll = false)
{
string queryType = "query";
if (queryAll)
{
queryType = "queryAll";
}
//Uri uri = new Uri(new Uri(instanceUrl), string.Format("/services/data/{0}/{1}/?q={2}", apiVersion, queryType, query));
Uri uri = new Uri(new Uri(instanceUrl), string.Format("/services/data/{0}/{1}", apiVersion, queryType));
string queryUri = QueryHelpers.AddQueryString(uri.ToString(), "q", query);
return new Uri(queryUri);
}
/// <summary>
/// Search Resource, for SOSL searches
/// </summary>
public static Uri Search(string instanceUrl, string apiVersion, string query)
{
Uri uri = new Uri(new Uri(instanceUrl), string.Format("/services/data/{0}/search", apiVersion));
string searchUri = QueryHelpers.AddQueryString(uri.ToString(), "q", query);
return new Uri(searchUri);
}
/// <summary>
/// Batch Resource
/// </summary>
/// <param name="instanceUrl"></param>
/// <param name="apiVersion"></param>
public static Uri Batch(string instanceUrl, string apiVersion)
{
if (string.IsNullOrEmpty(instanceUrl)) throw new ArgumentNullException("instanceUrl");
if (string.IsNullOrEmpty(apiVersion)) throw new ArgumentNullException("apiVersion");
//format: /vXX.X/composite/batch
Uri uri = new Uri(new Uri(instanceUrl), string.Format("services/data/{0}/composite/batch", apiVersion));
return uri;
}
/// <summary>
/// Formats an authentication URL for the User-Agent OAuth Authentication Flow
/// </summary>
/// <param name="loginUrl">(Required) Salesforce authorization endpoint.</param>
/// <param name="clientId">(Required) The Consumer Key from the connected app definition.</param>
/// <param name="redirectUrl">(Required) The Callback URL from the connected app definition.</param>
/// <param name="display">Changes the login page’s display type</param>
/// <param name="scope">OAuth scope - specifies what data your application can access</param>
/// <param name="state">Specifies any additional URL-encoded state data to be returned in the callback URL after approval.</param>
public static Uri UserAgentAuthenticationUrl(
string loginUrl,
string clientId,
string redirectUrl,
DisplayTypes display = DisplayTypes.Page,
string state = "",
string scope = "")
{
if (string.IsNullOrEmpty(loginUrl)) throw new ArgumentNullException("loginUrl");
if (string.IsNullOrEmpty(clientId)) throw new ArgumentNullException("clientId");
if (string.IsNullOrEmpty(redirectUrl)) throw new ArgumentNullException("redirectUrl");
const ResponseTypes responseType = ResponseTypes.Token;
Dictionary<string, string> prms = new Dictionary<string, string>();
prms.Add("response_type", responseType.ToString().ToLower());
prms.Add("client_id", clientId);
prms.Add("redirect_uri", redirectUrl);
prms.Add("display", display.ToString().ToLower());
if (!string.IsNullOrEmpty(scope))
{
prms.Add("scope", scope);
}
if (!string.IsNullOrEmpty(state))
{
prms.Add("state", state);
}
string url = QueryHelpers.AddQueryString(loginUrl, prms);
return new Uri(url);
}
//TODO: parser for redirect url result
/// <summary>
/// Formats an authentication URL for the Web Server Authentication Flow
/// </summary>
/// <param name="loginUrl">Required. Salesforce authorization endpoint.</param>
/// <param name="clientId">Required. The Consumer Key from the connected app definition.</param>
/// <param name="redirectUrl">Required. The Callback URL from the connected app definition.</param>
/// <param name="display">Changes the login page’s display type</param>
/// <param name="immediate">Determines whether the user should be prompted for login and approval. Default is false.</param>
/// <param name="scope">OAuth scope - specifies what data your application can access</param>
/// <param name="state">Specifies any additional URL-encoded state data to be returned in the callback URL after approval.</param>
public static Uri WebServerAuthenticationUrl(
string loginUrl,
string clientId,
string redirectUrl,
DisplayTypes display = DisplayTypes.Page,
bool immediate = false,
string scope = "",
string state = ""
)
{
if (string.IsNullOrEmpty(loginUrl)) throw new ArgumentNullException("loginUrl");
if (string.IsNullOrEmpty(clientId)) throw new ArgumentNullException("clientId");
if (string.IsNullOrEmpty(redirectUrl)) throw new ArgumentNullException("redirectUrl");
//TODO: code_challenge, login_hint, nonce, prompt params
const ResponseTypes responseType = ResponseTypes.Code;
Dictionary<string, string> prms = new Dictionary<string, string>();
prms.Add("response_type", responseType.ToString().ToLower());
prms.Add("client_id", clientId);
prms.Add("redirect_uri", redirectUrl);
prms.Add("display", display.ToString().ToLower());
prms.Add("immediate", immediate.ToString().ToLower());
if (!string.IsNullOrEmpty(scope))
{
prms.Add("scope", scope);
}
if (!string.IsNullOrEmpty(state))
{
prms.Add("state", state);
}
string url = QueryHelpers.AddQueryString(loginUrl, prms);
return new Uri(url);
}
/// <summary>
/// Formats a URL to request a OAuth Refresh Token
/// </summary>
/// <param name="tokenRefreshUrl"></param>
/// <param name="refreshToken">The refresh token the client application already received.</param>
/// <param name="clientId">The Consumer Key from the connected app definition.</param>
/// <param name="clientSecret">The Consumer Secret from the connected app definition. Required unless the Require Secret for Web Server Flow setting is not enabled in the connected app definition.</param>
/// <returns></returns>
public static Uri RefreshTokenUrl(
string tokenRefreshUrl,
string refreshToken,
string clientId,
string clientSecret = "")
{
if (tokenRefreshUrl == null) throw new ArgumentNullException("tokenRefreshUrl");
if (refreshToken == null) throw new ArgumentNullException("refreshToken");
if (clientId == null) throw new ArgumentNullException("clientId");
Dictionary<string, string> prms = new Dictionary<string, string>();
prms.Add("grant_type", "refresh_token");
prms.Add("refresh_token", refreshToken);
prms.Add("client_id", clientId);
if(!string.IsNullOrEmpty(clientSecret))
{
prms.Add("client_secret", clientSecret);
}
prms.Add("format", "json");
string url = QueryHelpers.AddQueryString(tokenRefreshUrl, prms);
return new Uri(url);
}
}
}