Lightning Connect Custom Adapters (技術(shù)貼)

作為一個自認(rèn)為特別有人文情懷的人,始終覺得寫技術(shù)貼特別沒勁兒赞警,于是“云上的世界”這個欄目基本上不死不活地空置了兩年琼蚯,最近突然覺得這么空下去挺對不起這個浪漫的專欄名赐俗,于是決定從即日開始阻逮,在這里寫一寫自己在force.com這個龐大的生態(tài)系統(tǒng)中探索時的些許感悟和積累与柑。

Salesforce在北美歐洲澳大利亞日本等地非常地不可一世,但在中國卻因為價格策略市場認(rèn)知等因素夜郁,再加上本地競爭者的有力阻擊,基本上還處在蓄勢待發(fā)的狀態(tài)庙睡,不用說普通讀者事富,即便對大多數(shù)互聯(lián)網(wǎng)或IT從業(yè)者而言,也還是一個相對比較新鮮的事物乘陪。

因此统台,我決定當(dāng)一個布道者。

雖然也了解的不多啡邑,但就當(dāng)投石問路贱勃,說不定有讀者看了這些文章之后決定成為一個Salesforce的管理員、開發(fā)者谤逼、咨詢顧問贵扰、架構(gòu)師呢,要是再能由此遇到一些志同道合的朋友流部,那就更完美了戚绕。

好了,閑話少說枝冀,直奔主題吧舞丛。

首先預(yù)告一下耘子,“Lightning Connect”這個系列都將會是純技術(shù)貼 - 我會努力把技術(shù)貼和和非技術(shù)貼分開,這樣瓷马,不同興趣的讀者可以各取所需 - 如果你不是Salesforce的開發(fā)者拴还,這篇文章可能不適合你。

Salesforce在Winter '15的產(chǎn)品發(fā)布當(dāng)中第一次介紹了Lightning Connect - 這是一個新的將Salesforce與外部數(shù)據(jù)源進(jìn)行整合的接口欧聘。

簡而言之片林,它允許你用非ETL的方式(這么說聽起來貌似很高端,其實說白了就是通過HTTP Callout來獲取數(shù)據(jù))將外部數(shù)據(jù)源集成到Salesforce中來怀骤,創(chuàng)建所謂的external object费封,并承諾這些external object和那些生存在Salesforce自己底端Oracle數(shù)據(jù)庫中的standard object或custom object有相似的行徑與平等的地位。

Lightning Connect本身的設(shè)置并不復(fù)雜蒋伦,Salesforce的免費在線教學(xué)網(wǎng)站Trailhead中有專門的一個Module來介紹弓摘,所以我就不贅言了,有興趣的朋友可以自行移步查看痕届。

https://developer.salesforce.com/trailhead/module/lightning_connect

然而韧献,Lightning Connect的局限性在于它只能識別那些符合OData協(xié)議的數(shù)據(jù)源。

即便一些大公司的產(chǎn)品研叫,比如SAP或Microsoft Dynamics锤窑,都有基于OData協(xié)議的數(shù)據(jù)接口,但畢竟不甚方便嚷炉,于是在Summer '15的產(chǎn)品發(fā)布中渊啰,Salesforce推出了一個讓人激動的new feature,也就是這篇文章的主題 - Lightning Connect Custom Adapters申屹。

Lightning Connect Custom Adapters绘证,或者叫做Apex Connect Framework,允許開發(fā)者自己用Apex來編寫自定義適配器(Custom Adapter)哗讥,然后實現(xiàn)和任何格式或協(xié)議的數(shù)據(jù)源進(jìn)行實時的完美集成嚷那。

Apex為此增加了一個叫做DataSource的命名空間,所有新的方法和數(shù)據(jù)類型都在此命名空間之下定義杆煞。作為開發(fā)者來說魏宽,需要做的只有兩件事:

1. 創(chuàng)建一個Apex class,繼承DataSource.Provider這個接口索绪,實現(xiàn)接口里預(yù)定義的函數(shù)湖员;

2. 創(chuàng)建另一個Apex class,繼承DataSource.DataSourceConnection這個接口贫悄,然后實現(xiàn)接口里預(yù)定義的函數(shù)瑞驱。

這兩個Apex class完成之后,在創(chuàng)建外部數(shù)據(jù)源時你會在Type下拉菜單中看到你自己的DataSource.Provider窄坦,選擇該Provider唤反,這個新的外部數(shù)據(jù)源就和你自定義的Adapter聯(lián)系起來了凳寺。

在新創(chuàng)建的數(shù)據(jù)源中點擊“Validate and Sync”按鈕(該截圖是取自Edit一個已有的數(shù)據(jù)源,所以只有“Sync”按鈕)彤侍,這時后臺代碼會調(diào)用DataSource.DataSourceConnection那個Apex class中的sync()函數(shù)(后文中會看到)肠缨,?并返回外部數(shù)據(jù)源中所有的表信息(該例中外部數(shù)據(jù)源只包含了一個叫做“Looper”的表,真實環(huán)境中每個外部數(shù)據(jù)源可能含有多個表)盏阶。

選擇你想要同步的表晒奕,然后進(jìn)行同步,Salesforce則會按照你在sync()函數(shù)中的定義創(chuàng)建相應(yīng)的外部數(shù)據(jù)(external object)名斟。如果你點擊新生成的外部數(shù)據(jù)查看其詳細(xì)信息的話脑慧,你會發(fā)現(xiàn)界面非常熟悉 - 和Salesforce自身的standard object與custom object基本一樣。

需要注意的是砰盐,external object的后綴是__x闷袒,而custom object的后綴是__c。

另外岩梳,由于外部數(shù)據(jù)的出現(xiàn)囊骤,Salesforce新增加了兩個字段類型External Lookup Relationship Field和Indirect Lookup Relationship Field。簡單說就是冀值,兩個都是用來建立Lookup Relationship的也物,不同之處是,前者external object是parent object池摧,child object可以是standard object或external object焦除;后者external object是child object,而parent object是standard object作彤。這些東西上面那個Module里都有詳細(xì)講解膘魄,在此我就不細(xì)說了。

接下來我們仔細(xì)看看DataSource.Provider和DataSource.DataSourceConnection這兩個接口竭讳。

DataSource.Provider

實現(xiàn)該接口的Apex class需要實現(xiàn)下面三個方法:

getAuthenticationCapabilities()告訴Salesforce外部數(shù)據(jù)源支持何種驗證方法创葡,包括匿名、BASIC绢慢、Oauth灿渴、或者Certificate。這些選項會在上面截圖的Identity Type選項中出現(xiàn)胰舆,本例支持匿名和BASIC骚露,但因為不做任何Http Callout,所以并沒有什么用處缚窿。

getCapabilities()告訴Salesforce外部數(shù)據(jù)源支持何種操作棘幸,ROW_QUERY、ROW_UPDATE倦零、ROW_CREATE误续、ROW_DELETE吨悍、SEARCH、REQUIRE_ENDPOINT蹋嵌、以及QUERY_PAGINATION_SERVER_DRIVEN育瓜。

getConnection()則返回一個實現(xiàn)了DataSource.DataSourceConnection的Apex class實例,來做真正的Adapter工作栽烂。注意躏仇,由于每一次對外部數(shù)據(jù)的SOQL或SOSL操作都會調(diào)用該函數(shù)產(chǎn)生一個DataSource.DataSourceConnection的Apex class實例,所以該Apex clas的構(gòu)造函數(shù)中不應(yīng)該有任何expensive的操作腺办,比如callout之類钙态。

P.S. 關(guān)于DataSource.Provider的一些額外解釋

ROW_QUERY - 所有的SOQL操作,包括瀏覽UI時候系統(tǒng)產(chǎn)生的SOQL操作菇晃;

ROW_UPDATE册倒、ROW_CREATE、ROW_DELETE - 常規(guī)的CRUD操作磺送;

SEARCH - 所有的SOSL操作驻子,包括在UI中進(jìn)行全局搜索;

REQUIRE_ENDPOINT - 控制是否在設(shè)置新外部數(shù)據(jù)源頁面中要求輸入一個endpoint地址估灿;

QUERY_PAGINATION_SERVER_DRIVEN - 告訴Salesforce外部數(shù)據(jù)源的分頁是否是server端控制的崇呵;

DataSource.ConnectionParams - getConnection()函數(shù)中會傳入一個叫做connectionParams的參數(shù),這個參數(shù)的值取決于設(shè)置外部數(shù)據(jù)源時管理員選擇的何種驗證方式 - 如果是BASIC的話則會包含USERNAME和PASSOWRD馅袁,如果是Oauth的話則會包含oauthToken...不過Salesforce建議使用named credentials來做callout域慷,而不是直接把credential提供給外部數(shù)據(jù)源。本例中因為不涉及callout汗销,所以用不著connectionParams犹褒,我會在后續(xù)的文章中介紹相關(guān)的用例。

DataSource.DataSourceConnection

實現(xiàn)DataSource.DataSourceConnection的Apex class實際上是真正做所有工作的幕后英雄弛针。

這個class里面涉及到的東西很多叠骑,但大多數(shù)都只是一些DataSource下新的數(shù)據(jù)結(jié)構(gòu)而已,Salesforce的文檔很詳細(xì)削茁,我在代碼中也做了比較細(xì)致的注釋宙枷,所以基本的東西就不重復(fù)了(DataSource.Filter是一個很有意思的數(shù)據(jù)類型,我在注釋中也給予了額外的一些篇幅來解釋茧跋,提醒一下注意)慰丛。

我這里想著重說的是我覺得最重要的、但文檔中感覺并沒有解釋的很詳盡的兩個概念瘾杭,query()函數(shù)和search()函數(shù)诅病。

先說query()函數(shù)。

文檔中說每次對external object進(jìn)行SOQL操作時都會觸發(fā)query()函數(shù),然后所有和該SOQL相關(guān)的內(nèi)容都會以QueryContext參數(shù)的形式傳遞給query()函數(shù)睬隶,然后完全交給函數(shù)代碼來做相應(yīng)的操作。那么問題來了 - 究竟query()函數(shù)需要處理多少種SOQL用例呢页徐?處理 SELECT columns FROM table 當(dāng)然不用說苏潜,處理 SELECT columns FROM table WHERE externalId = 'something' 也不用說,那么再復(fù)雜一些的SOQL有處理的必要嗎变勇?

我不知道別人有沒有這樣的疑慮恤左,總之我是思忖了許久,后來終于醒悟過來 - 這完全取決于你安笮濉飞袋!你如果希望這個external object僅僅支持最基本的UI SOQL,那就實現(xiàn)上述兩個用例外加 SELECT COUNT()就好链患,你如果希望在代碼中對external object進(jìn)行更復(fù)雜的SOQL操作巧鸭,那就實現(xiàn)那些更復(fù)雜的SOQL操作,當(dāng)然麻捻,如果你沒有在query()代碼中實現(xiàn)相應(yīng)的SOQL操作邏輯纲仍,而又在別處試圖對這個external object做相應(yīng)的操作,那等待你的當(dāng)然就是exception了贸毕。

先來看兩個最簡單的例子郑叠。

假設(shè)你在Salesforce中已經(jīng)通過你的外部數(shù)據(jù)源創(chuàng)建了某個external object,并以此為基礎(chǔ)創(chuàng)建了一個tab(完全和對standard object的操作一樣)明棍,那么你在瀏覽該tab的時候Salesforce后臺會自動對該external object進(jìn)行SOQL查詢乡革。

比如/x00/o 這個操作是查找最近瀏覽過的數(shù)據(jù),而/xoo 則是列出所有的數(shù)據(jù)摊腋。

下面是DEBUG LOG中對兩個操作的數(shù)據(jù)沸版。

第一個截圖是對/xoo/o 的操作,雖然QueryContext的信息看不全兴蒸,但其實它傳進(jìn)來一個filter推穷,columnName是Id,type是EQUALS类咧,columnValue是‘0013....’馒铃,我的query()函數(shù)捕捉到了這些信息,然后對應(yīng)的進(jìn)行了操作(因為我的外部數(shù)據(jù)其實是映射Account數(shù)據(jù)痕惋,所以我做了一個SOQL操作区宇,其它情形還可能是對某個REST API進(jìn)行callout,諸如/endpoint/data?id='0013...'值戳,然后在對返回的數(shù)據(jù)進(jìn)行整理)议谷。

?第二個截圖是對/xoo 的操作,QueryContext中filter為null堕虹,所以我就簡單做了一個SELECT的操作卧晓,返回數(shù)據(jù)芬首。

如果你只是要通過UI來訪問external object,那這些基本上就差不多夠了(除非你設(shè)置定制化view)逼裆,但如果你想讓external object支持更復(fù)雜的SOQL操作郁稍,那就要將這些操作都在你的query()代碼中予以捕捉和實現(xiàn)。

本例中的external object支持了WHERE從句中的大部分SOQL關(guān)鍵字胜宇,我們可以通過workbench做一個實驗:

如果我把代碼中對DataSource.FilterType.LIKE_的支持去掉耀怜,那么執(zhí)行下面SOQL時會出現(xiàn)exception;

如果將代碼中對DataSource.FilterType.LIKE_的支持加回來桐愉,那么數(shù)據(jù)會順利返回财破;

對有AND的邏輯從句也能夠應(yīng)付(DataSource.Filter的type屬性和subfilters屬性居功至偉)。

再說說search()函數(shù)从诲。

理解了query()函數(shù)后左痢,search()函數(shù)就簡單多了。當(dāng)Salesforce進(jìn)行全局搜索的時候系洛,調(diào)用search()函數(shù)抖锥,而你無非就需要考慮兩件事:1. 外部數(shù)據(jù)源中的哪些external objects被搜索(可以通過遍歷SearchContext的tableSelections屬性來作出決定);2. 某個external object中的哪些字段被搜索碎罚。

比如Salesforce提供了一個Util方法searchByName()磅废,其實就是當(dāng)search發(fā)生時,拿著searchPhrase對external object的Name字段進(jìn)行一個關(guān)鍵字為CONTAINS的query荆烈,如果使用這個Util方法拯勉,那么當(dāng)你在Salesforce里做如下針對電話號碼的全局搜索時,對應(yīng)的external object數(shù)據(jù)不會出現(xiàn)憔购,因為該方法只針對Name做query宫峦。

在本例代碼中,我注釋掉了該Util方法玫鸟,轉(zhuǎn)而自定義了一個針對Name和Phone兩個字段的query导绷,這樣的話,做同樣的一個全局搜索屎飘,你可以看到external object對應(yīng)的數(shù)據(jù)出現(xiàn)在了返回結(jié)果中妥曲。

最后補充一句,每當(dāng)你做全局搜索的時候钦购,?一個SearchThreadPools操作會在DEBUG LOG中產(chǎn)生檐盟,你可以看到,在后代的確調(diào)用了我們自定義的search()函數(shù)代碼押桃,做了一個基于Name和Phone兩個字段的query操作葵萎。

好了,這篇文章已經(jīng)很冗長了,就在這里結(jié)束吧羡忘,?下一篇再見~

GitHub

https://github.com/jacky1999cn2000/lightningconnect

參考資料

https://developer.salesforce.com/blogs/engineering/2015/05/introducing-lightning-connect-custom-adapters.html

https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/apex_connector_top.htm

https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/apex_connector_example_loopback.htm

MAY THE FORCE BE WITH YOU!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末谎痢,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子卷雕,更是在濱河造成了極大的恐慌节猿,老刑警劉巖,帶你破解...
    沈念sama閱讀 221,576評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件爽蝴,死亡現(xiàn)場離奇詭異,居然都是意外死亡纫骑,警方通過查閱死者的電腦和手機蝎亚,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,515評論 3 399
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來先馆,“玉大人发框,你說我怎么就攤上這事∶呵剑” “怎么了梅惯?”我有些...
    開封第一講書人閱讀 168,017評論 0 360
  • 文/不壞的土叔 我叫張陵,是天一觀的道長仿野。 經(jīng)常有香客問我铣减,道長,這世上最難降的妖魔是什么脚作? 我笑而不...
    開封第一講書人閱讀 59,626評論 1 296
  • 正文 為了忘掉前任葫哗,我火速辦了婚禮,結(jié)果婚禮上球涛,老公的妹妹穿的比我還像新娘劣针。我一直安慰自己,他們只是感情好亿扁,可當(dāng)我...
    茶點故事閱讀 68,625評論 6 397
  • 文/花漫 我一把揭開白布捺典。 她就那樣靜靜地躺著,像睡著了一般从祝。 火紅的嫁衣襯著肌膚如雪襟己。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,255評論 1 308
  • 那天牍陌,我揣著相機與錄音稀蟋,去河邊找鬼。 笑死呐赡,一個胖子當(dāng)著我的面吹牛退客,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 40,825評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼萌狂,長吁一口氣:“原來是場噩夢啊……” “哼档玻!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起茫藏,我...
    開封第一講書人閱讀 39,729評論 0 276
  • 序言:老撾萬榮一對情侶失蹤误趴,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后务傲,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體凉当,經(jīng)...
    沈念sama閱讀 46,271評論 1 320
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,363評論 3 340
  • 正文 我和宋清朗相戀三年售葡,在試婚紗的時候發(fā)現(xiàn)自己被綠了看杭。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,498評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡挟伙,死狀恐怖楼雹,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情尖阔,我是刑警寧澤贮缅,帶...
    沈念sama閱讀 36,183評論 5 350
  • 正文 年R本政府宣布,位于F島的核電站介却,受9級特大地震影響谴供,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜齿坷,卻給世界環(huán)境...
    茶點故事閱讀 41,867評論 3 333
  • 文/蒙蒙 一憔鬼、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧胃夏,春花似錦轴或、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,338評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至答恶,卻和暖如春饺蚊,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背悬嗓。 一陣腳步聲響...
    開封第一講書人閱讀 33,458評論 1 272
  • 我被黑心中介騙來泰國打工污呼, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人包竹。 一個月前我還...
    沈念sama閱讀 48,906評論 3 376
  • 正文 我出身青樓燕酷,卻偏偏與公主長得像籍凝,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子苗缩,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,507評論 2 359

推薦閱讀更多精彩內(nèi)容