Newer Version Available
Salesforce Connect のスタックオーバーフローカスタムアダプタ
次の例は、外部参照関係と複数のテーブルをサポートする方法を示しています。外部参照関係は、子の標準、カスタム、外部オブジェクトを親の外部オブジェクトに結び付けます。各テーブルを、Salesforce 組織の外部オブジェクトにすることができます。
この例を機能させるために、取引先責任者標準オブジェクトにカスタム項目を作成します。カスタム項目に「github_username」という名前を付け、External ID および Unique 属性を選択します。
StackOverflowDataSourceConnection クラス
1/**
2 * Defines the connection to Stack Exchange API v2.2 to support
3 * querying of Stack Overflow users (stackoverflowUser)
4 * and posts (stackoverflowPost).
5 * Extends the DataSource.Connection class to enable
6 * Salesforce to sync the external system’s schema
7 * and to handle queries of the external data.
8 **/
9global class StackOverflowDataSourceConnection extends
10 DataSource.Connection {
11 private DataSource.ConnectionParams connectionInfo;
12
13 /**
14 * Constructor for StackOverflowDataSourceConnection
15 **/
16 global StackOverflowDataSourceConnection(
17 DataSource.ConnectionParams connectionInfo) {
18 this.connectionInfo = connectionInfo;
19 }
20
21 /**
22 * Defines the schema for the external system.
23 * Called when the administrator clicks “Validate and Sync”
24 * in the user interface for the external data source.
25 **/
26 override global List<DataSource.Table> sync() {
27 List<DataSource.Table> tables =
28 new List<DataSource.Table>();
29
30 // Defines columns for the table of Stack OverFlow posts
31 List<DataSource.Column> postColumns =
32 new List<DataSource.Column>();
33
34 // Defines the external lookup field.
35 postColumns.add(DataSource.Column.externalLookup(
36 'owner_id', 'stackoverflowUser__x'));
37 postColumns.add(DataSource.Column.text('title', 255));
38 postColumns.add(DataSource.Column.text('view_count', 255));
39 postColumns.add(DataSource.Column.text('question_id',255));
40 postColumns.add(DataSource.Column.text('creation_date',255));
41 postColumns.add(DataSource.Column.text('score',255));
42 postColumns.add(DataSource.Column.url('link'));
43 postColumns.add(DataSource.Column.url('DisplayUrl'));
44 postColumns.add(DataSource.Column.text('ExternalId',255));
45
46 tables.add(DataSource.Table.get('stackoverflowPost','title',
47 postColumns));
48
49 // Defines columns for the table of Stack OverFlow users
50 List<DataSource.Column> userColumns =
51 new List<DataSource.Column>();
52 userColumns.add(DataSource.Column.text('user_id', 255));
53 userColumns.add(DataSource.Column.text('display_name', 255));
54 userColumns.add(DataSource.Column.text('location',255));
55 userColumns.add(DataSource.Column.text('creation_date',255));
56 userColumns.add(DataSource.Column.url('website_url',255));
57 userColumns.add(DataSource.Column.text('reputation',255));
58 userColumns.add(DataSource.Column.url('link'));
59 userColumns.add(DataSource.Column.url('DisplayUrl'));
60 userColumns.add(DataSource.Column.text('ExternalId',255));
61
62 tables.add(DataSource.Table.get('stackoverflowUser',
63 'Display_name', userColumns));
64
65 return tables;
66 }
67
68 /**
69 * Called to query and get results from the external
70 * system for SOQL queries, list views, and detail pages
71 * for an external object that’s associated with the
72 * external data source.
73 *
74 * The QueryContext argument represents the query to run
75 * against a table in the external system.
76 *
77 * Returns a list of rows as the query results.
78 **/
79 override global DataSource.TableResult query(
80 DataSource.QueryContext context) {
81 DataSource.Filter filter = context.tableSelection.filter;
82 String url;
83
84 // Sets the URL to query Stack Overflow posts
85 if (context.tableSelection.tableSelected
86.equals('stackoverflowPost')) {
87 if (filter != null) {
88 String thisColumnName = filter.columnName;
89 if (thisColumnName != null &&
90 thisColumnName.equals('ExternalId'))
91 url = 'https://api.stackexchange.com/2.2/'
92 + 'questions/' + filter.columnValue
93 + '?order=desc&sort=activity'
94 + '&site=stackoverflow';
95 else
96 url = 'https://api.stackexchange.com/2.2/'
97 + 'questions'
98 + '?order=desc&sort=activity'
99 + '&site=stackoverflow';
100 } else {
101 url = 'https://api.stackexchange.com/2.2/'
102 + 'questions'
103 + '?order=desc&sort=activity'
104 + '&site=stackoverflow';
105 }
106 // Sets the URL to query Stack Overflow users
107 } else if (context.tableSelection.tableSelected
108.equals('stackoverflowUser')) {
109 if (filter != null) {
110 String thisColumnName = filter.columnName;
111 if (thisColumnName != null &&
112 thisColumnName.equals('ExternalId'))
113 url = 'https://api.stackexchange.com/2.2/'
114 + 'users/' + filter.columnValue
115 + '?order=desc&sort=reputation'
116 + '&site=stackoverflow';
117 else
118 url = 'https://api.stackexchange.com/2.2/'
119 + 'users' +
120'?order=desc&sort=reputation&site=stackoverflow';
121 } else {
122 url = 'https://api.stackexchange.com/2.2/'
123 + 'users' + '?order=desc&sort=reputation'
124 + '&site=stackoverflow';
125 }
126 }
127
128 /**
129 * Filters, sorts, and applies limit and offset clauses.
130 **/
131 List<Map<String, Object>> rows =
132 DataSource.QueryUtils.process(context, getData(url));
133 return DataSource.TableResult.get(true, null,
134 context.tableSelection.tableSelected, rows);
135 }
136
137 /**
138 * Helper method to parse the data.
139 * The url argument is the URL of the external system.
140 * Returns a list of rows from the external system.
141 **/
142 public List<Map<String, Object>> getData(String url) {
143 String response = getResponse(url);
144
145 List<Map<String, Object>> rows =
146 new List<Map<String, Object>>();
147
148 Map<String, Object> responseBodyMap = (Map<String, Object>)
149 JSON.deserializeUntyped(response);
150
151 /**
152 * Checks errors.
153 **/
154 Map<String, Object> error =
155 (Map<String, Object>)responseBodyMap.get('error');
156 if (error!=null) {
157 List<Object> errorsList =
158 (List<Object>)error.get('errors');
159 Map<String, Object> errors =
160 (Map<String, Object>)errorsList[0];
161 String errorMessage = (String)errors.get('message');
162 throw new
163 DataSource.OAuthTokenExpiredException(errorMessage);
164 }
165
166 List<Object> fileItems=
167 (List<Object>)responseBodyMap.get('items');
168 if (fileItems != null) {
169 for (Integer i=0; i < fileItems.size(); i++) {
170 Map<String, Object> item =
171 (Map<String, Object>)fileItems[i];
172 rows.add(createRow(item));
173 }
174 } else {
175 rows.add(createRow(responseBodyMap));
176 }
177
178 return rows;
179 }
180
181 /**
182 * Helper method to populate the External ID and Display
183 * URL fields on external object records based on the 'id'
184 * value that’s sent by the external system.
185 *
186 * The Map<String, Object> item parameter maps to the data
187 * that represents a row.
188 *
189 * Returns an updated map with the External ID and
190 * Display URL values.
191 **/
192 public Map<String, Object> createRow(
193 Map<String, Object> item) {
194 Map<String, Object> row = new Map<String, Object>();
195 for ( String key : item.keySet() ) {
196 if (key.equals('question_id') || key.equals('user_id')) {
197 row.put('ExternalId', item.get(key));
198 } else if (key.equals('link')) {
199 row.put('DisplayUrl', item.get(key));
200 } else if (key.equals('owner')) {
201 Map<String, Object> ownerMap =
202 (Map<String, Object>)item.get(key);
203 row.put('owner_id', ownerMap.get('user_id'));
204 }
205
206 row.put(key, item.get(key));
207 }
208 return row;
209 }
210
211 /**
212 * Helper method to make the HTTP GET call.
213 * The url argument is the URL of the external system.
214 * Returns the response from the external system.
215 **/
216 public String getResponse(String url) {
217 // Perform callouts for production (non-test) results.
218 Http httpProtocol = new Http();
219 HttpRequest request = new HttpRequest();
220 request.setEndPoint(url);
221 request.setMethod('GET');
222 HttpResponse response = httpProtocol.send(request);
223 return response.getBody();
224 }
225}StackOverflowPostDataSourceProvider クラス
1/**
2 * Extends the DataSource.Provider base class to create a
3 * custom adapter for Salesforce Connect. The class informs
4 * Salesforce of the functional and authentication
5 * capabilities that are supported by or required to connect
6 * to an external system.
7 **/
8global class StackOverflowPostDataSourceProvider
9 extends DataSource.Provider {
10
11 /**
12 * For simplicity, this example declares that the external
13 * system doesn’t require authentication by returning
14 * AuthenticationCapability.ANONYMOUS as the sole entry
15 * in the list of authentication capabilities.
16 **/
17 override global List<DataSource.AuthenticationCapability>
18 getAuthenticationCapabilities() {
19 List<DataSource.AuthenticationCapability> capabilities =
20 new List<DataSource.AuthenticationCapability>();
21 capabilities.add(
22 DataSource.AuthenticationCapability.ANONYMOUS);
23 return capabilities;
24 }
25
26 /**
27 * Declares the functional capabilities that the
28 * external system supports, in this case
29 * only SOQL queries.
30 **/
31 override global List<DataSource.Capability>
32 getCapabilities() {
33 List<DataSource.Capability> capabilities =
34 new List<DataSource.Capability>();
35 capabilities.add(DataSource.Capability.ROW_QUERY);
36 return capabilities;
37 }
38
39 /**
40 * Declares the associated DataSource.Connection class.
41 **/
42 override global DataSource.Connection getConnection(
43 DataSource.ConnectionParams connectionParams) {
44 return new
45 StackOverflowDataSourceConnection(connectionParams);
46 }
47}