WinActorのVBScriptで OAuth 認証を使って Salesforce APIにアクセスするサンプルプログラムを書いてみた。
背景
WinActorでSalesforce使おうと思ったら、スマホの認証が挟まったりして、全然自動化できない状態でした。
OAuth認証+APIアクセスを行うと Salesforceの多要素認証(MFA)化に対応できるかもと思い、
WinActorのVBScriptで OAuth 認証を使って Salesforce APIにアクセスするサンプルプログラムを書いてみました。
WinActorのスクリプトに貼り付けてみてください。
環境設定もなかなか大変ですが、
これが出来ると突破口になるかもです。
質問などいただいたら随時更新しようと思います。
前提条件
1.もし試行的に実行したいのであれば、
Salesforce Lightning Platform Developer Edition に登録すると良いです。
https://developer.salesforce.com/signup
※通常の試用版だとAPIアクセスが制限されててエラーしか出ませんでした・・・
2.Salesforce上で接続アプリケーションの作成を行う必要があります。
3.Salesforce上で作成したアプリケーションから、コンシューマ鍵、コンシューマの秘密を取得する必要があります。
上記画面で作成した接続アプリケーションの右側のドロップダウンから「参照」を選択
4.Salesforce上で私のセキュリティートークンのリセットをし、受信したメールにあるセキュリティートークン文字列が必要となります。
サンプルコード WinActor用 VBScript
WinActorのスクリプトノードに貼り付けて、各引数を設定してください。
Dim endpoint : endpoint = !endpoint!
Dim client_id : client_id = !client_id!
Dim client_secret : client_secret = !client_secret!
Dim username : username = !username!
Dim password : password = !password!
Dim secretToken : secretToken = !secretToken!
Dim access_token : access_token = $access_token$
Dim template_file : template_file = !テンプレートCSV!
Dim where : where = !データ絞込条件!
Dim limit : limit = !データ取得上限数!
Dim output_file : output_file = !出力先CSV!
Dim scenario_folder : scenario_folder = !シナリオフォルダ名!
Dim c
Set c = New SalesforceRestJsonController
c.Initialize endpoint
c.GetAccessToken client_id, client_secret, username, password, secretToken
c.CreateTemplateFiles scenario_folder
c.SaveJsonRecordsToCsv template_file, where, limit, output_file
Class SalesforceRestJsonController
Public TemplateFile
Public SObjectName
Public FieldsCSV
Public LabelsCSV
Public Fields
Public EndPoint
Public AccessToken
Public URL
Public JsonObject
Public JsonObjectEx
Public TotalSize
Public JsonTool
Sub Class_Initialize
End Sub
Sub Initialize(pEndPoint)
EndPoint = pEndPoint
End Sub
Function GetAccessToken(pClientId, pClientSecret, pUserName, pPassword, pSecretToken)
Dim url : url = "/services/oauth2/token"
Dim param
param = "grant_type=password" _
& "&client_id=" & pClientId _
& "&client_secret=" & pClientSecret _
& "&username=" & pUserName _
& "&password=" & pPassword & pSecretToken
Set jo = GetJsonObject(GetHttpResonseTextByPost(url, param))
Me.AccessToken = jo.access_token
GetAccessToken = Me.AccessToken
End Function
Function GetHttpResonseTextByPost(pUrl, pParam)
Dim oHTTP : Set oHTTP = CreateObject("MSXML2.ServerXMLHTTP")
oHTTP.Open "POST", Me.EndPoint & pUrl, False
oHTTP.SetRequestHeader "Content-Type", "application/x-www-form-urlencoded"
oHTTP.Send pParam
GetHttpResonseTextByPost = oHTTP.ResponseText
Set oHTTP = Nothing
End Function
Function GetHttpResonseTextByGet(pUrl)
Dim oHTTP : Set oHTTP = CreateObject("MSXML2.ServerXMLHTTP")
oHTTP.Open "GET", EndPoint & pUrl, False
oHTTP.SetRequestHeader "Content-Type", "application/json"
oHTTP.SetRequestHeader "Authorization", "Bearer " & Me.AccessToken
oHTTP.Send()
GetHttpResonseTextByGet = oHTTP.ResponseText
Set oHTTP = Nothing
End Function
Function GetJsonObject(pJsonString)
Set Me.JsonTool = CreateObject("HtmlFile")
Me.JsonTool.write "<meta http-equiv='X-UA-Compatible' content='IE=edge' />"
Me.JsonTool.write "<script>document.getJSArray=function () {return eval('[]');}</script>"
Me.JsonTool.write "<script>document.JsonParse=function (s) {return eval('(' + s + ')');}</script>"
Me.JsonTool.write "<script>document.JsonStringify=JSON.stringify;</script>"
Set Me.JsonObject = Me.JsonTool.JsonParse(pJsonString)
Set GetJsonObject = Me.JsonObject
End Function
Function SaveJsonRecordsToCsv(pTemplateFile, pWhere, pLimit, pOutputFile)
SetupQueryUrl pTemplateFile, pWhere, pLimit
Dim oHTTP
Set oHTTP = GetHttpObjectFromUrlAsync(Me.URL)
If oHTTP.readyState <> 4 Then
oHTTP.waitForResponse(600)
End If
WriteCsvHeader pOutputfile
Dim json
json = oHTTP.ResponseText
Set oHTTP = Nothing
AppendJsonRecordsToCsvAsync pOutputFile, json
End Function
Function SetupQueryUrl(pTemplateFile, pWhere, pLimit)
Me.TemplateFile = pTemplateFile
Set objRe = New RegExp
objRe.Pattern = "(.+\\)+(.+)_template_(.+)\.csv$"
Set matches = objRe.Execute(TemplateFile)
Me.SObjectName = matches(0).SubMatches(2)
Dim stream : Set stream = GetAdodbStreamForUTF8()
stream.Open
stream.LoadFromFile Me.TemplateFile
Me.LabelsCSV = stream.ReadText(-2)
Me.FieldsCSV = stream.ReadText(-2)
stream.Close
Me.Fields = Split(FieldsCSV, ",")
Me.URL = "/services/data/v53.0/query/?q=SELECT " & FieldsCSV & " FROM " & SObjectName
If pWhere <> "" Then
Me.URL = Me.URL & " WHERE " & pWhere
End If
If pLimit <> "" Then
Me.URL = Me.URL & " LIMIT " & pLimit
End If
End Function
Function WriteCsvHeader(pOutputfile)
Dim stream
Set stream = GetAdodbStreamForUTF8()
stream.Open
stream.WriteText c.LabelsCSV, 1
stream.WriteText c.FieldsCSV, 1
stream.SaveToFile pOutputfile, 2
stream.Close
End Function
Function GetJsonObjectFromUrl(pUrl)
Dim oHTTP : Set oHTTP = CreateObject("MSXML2.ServerXMLHTTP")
oHTTP.Open "GET", EndPoint & pUrl, False
oHTTP.SetRequestHeader "Content-Type", "application/json"
oHTTP.SetRequestHeader "Authorization", "Bearer " & Me.AccessToken
oHTTP.Send()
Set GetJsonObjectFromUrl = GetJsonObjectFromText(oHTTP.ResponseText)
Set oHTTP = Nothing
End Function
Function GetJsonObjectFromUrlEx(pUrl)
Dim oHTTP : Set oHTTP = CreateObject("MSXML2.ServerXMLHTTP")
oHTTP.Open "GET", EndPoint & pUrl, False
oHTTP.SetRequestHeader "Content-Type", "application/json"
oHTTP.SetRequestHeader "Authorization", "Bearer " & Me.AccessToken
oHTTP.Send()
GetJsonObjectFromTextEx(oHTTP.ResponseText)
Set oHTTP = Nothing
End Function
Function GetHttpObjectFromUrlAsync(pUrl)
Dim oHTTP : Set oHTTP = CreateObject("MSXML2.ServerXMLHTTP")
oHTTP.Open "GET", EndPoint & pUrl, True
oHTTP.SetRequestHeader "Content-Type", "application/json"
oHTTP.SetRequestHeader "Authorization", "Bearer " & Me.AccessToken
oHTTP.Send()
Set GetHttpObjectFromUrlAsync = oHTTP
End Function
Function AppendJsonRecordsToCsvAsync(pOutputFile, pJsonData)
Set Me.JsonObject = _
GetJsonObject( _
GetJsonRecordsToCsvForInnerJson( _
pJsonData _
) _
)
Dim isDone
isDone = Me.JsonObject.Json.done
Dim oHTTP
If Not isDone Then
Set oHTTP = GetHttpObjectFromUrlAsync(Me.JsonObject.Json.nextRecordsUrl)
End If
AppendCsvData pOutputFile, Me.JsonObject.getCsvData(Me.FieldsCSV)
If isDone Then
Exit Function
End If
If oHTTP.readyState <> 4 Then
oHTTP.waitForResponse(600)
End If
Dim json
json = oHTTP.ResponseText
Set oHTTP = Nothing
AppendJsonRecordsToCsvAsync pOutputFile, json
End Function
Function AppendCsvData(pOutputFile, pCsvData)
Dim stream
Set stream = GetAdodbStreamForUTF8()
stream.Open
stream.LoadFromFile pOutputFile
stream.Position = stream.Size
stream.WriteText pCsvData
stream.SaveToFile pOutputFile, 2
stream.Close
Set stream = Nothing
End Function
Function GetJsonRecordsToCsvForInnerJson(jsondata)
Dim jsondataEx
Dim fnc
fnc = "function(fields){ var fArr = fields.split(','); var ret = ''; for(i = 0; i<this.Json.records.length; i++){for(f = 0; f<fArr.length; f++){if(f != 0){ret += ','};if(!isNaN(this.Json.records[i][fArr[f]])){ret +='='};ret += '""' + String(this.Json.records[i][fArr[f]]).replace('""', '""""').replace('\r\n', '\n') + '""'} ret += '\r\n'}return ret;}"
jsondataEx = "{ ""getCsvData"": " & fnc & ",""Json"":" + jsondata + "}"
GetJsonRecordsToCsvForInnerJson = jsondataEx
End Function
Function GetRecordValue(pRowNumber, pFieldName)
Dim records
Set records = Me.JsonObject.records
GetRecordValue = Eval("records.[" & pRowNumber & "]." & pFieldName)
If Err.Number <> 0 Then
MsgBox pRowNumber & "," & pFieldName & ":Err" & Err.Message
End If
End Function
Function GetTextWriterForShiftJis(pFileName)
Dim fs
Set fs = CreateObject("Scripting.FileSystemObject")
Set GetTextWriterForShiftJis = fs.OpenTextFile(pFileName, 2, True, False)
End Function
Function GetTextAppendWriterForShiftJis(pFileName)
Dim fs
Set fs = CreateObject("Scripting.FileSystemObject")
Set GetTextAppendWriterForShiftJis = fs.OpenTextFile(pFileName, 8, True, False)
End Function
Function GetAdodbStreamForUTF8()
Dim s
Set s = CreateObject("ADODB.Stream")
s.Type = 2
s.Charset = "UTF-8"
Set GetAdodbStreamForUTF8 = s
End Function
Function CreateTemplateFiles(pScenarioFolder)
Dim json
json = GetHttpResonseTextByGet("/services/data/v53.0/sobjects/")
Set Me.JsonObject = GetJsonObject(GetJsonGlobalDescribeForInnerJson(json))
Dim sobjectsCSV
sobjectsCSV = Me.JsonObject.getSObjects()
Dim sobjects
sobjects = Split(sobjectsCSV, ",")
For Each sobjectName in sobjects
If sobjectName <> "" Then
Dim sobjectDescribeUrl
sobjectDescribeUrl = "/services/data/v53.0/sobjects/" & sobjectName & "/describe"
json = GetHttpResonseTextByGet(sobjectDescribeUrl)
Set Me.JsonObject = GetJsonObject(GetJsonFieldsForInnerJson(json))
Dim templateFilePath
templateFilePath = pScenarioFolder & "\テンプレート\" & Me.JsonObject.Json.label & "_template_" & sobjectName & ".csv"
Dim stream : Set stream = GetAdodbStreamForUTF8
stream.Open
stream.WriteText Me.JsonObject.getFields()
stream.SaveToFile templateFilePath, 2
stream.Close
End If
Next
End Function
Function GetJsonGlobalDescribeForInnerJson(jsondata)
Dim jsondataEx
Dim fnc
fnc = "function(){var ret = ''; for(i = 0; i < this.Json.sobjects.length; i++){if(this.Json.sobjects[i].searchable){ret += this.Json.sobjects[i].name + ','}}return ret;}"
jsondataEx = "{ ""getSObjects"": " & fnc & ",""Json"":" + jsondata + "}"
GetJsonGlobalDescribeForInnerJson = jsondataEx
End Function
Function GetJsonFieldsForInnerJson(jsondata)
Dim jsondataEx
Dim fnc
fnc = "function(){var retLabels = '';var retNames = ''; for(i = 0; i < this.Json.fields.length; i++){if(i != 0){retNames += ',';retLabels += ',';};retNames += this.Json.fields[i].name;retLabels += this.Json.fields[i].label;}return retLabels + '\r\n' + retNames;}"
jsondataEx = "{ ""getFields"": " & fnc & ",""Json"":" + jsondata + "}"
GetJsonFieldsForInnerJson = jsondataEx
End Function
End Class
'Ver 2022/03/22
引数
1.endpoint
:Salesforceのログイン先URLです。(末尾スラッシュ無し)
ex:https://sample-dev-ed.my.salesforce.com
2.client_id
:Salesforce上で接続アプリケーションの作成を行った際のコンシューマ鍵の文字列
ex:3MVG95mg0lk4batiTp4.qhyBwgJmnATd2Q_hD6m79H.YiNOOOqmVxREishaeTImADLMJWE2lob9JfpUlx3ybV
3.client_secret
:Salesforce上で接続アプリケーションの作成を行った際のコンシューマの秘密の文字列
ex:C1C295679976399E600CCE57D551577177E8A4B6677DFBE3501CF66A325C9E52
4.username
:Salesforceにログインする際のメールアドレス
ex:[email protected]
5.password
:Salesforceにログインする際のパスワード
ex:a-345678
6.secretToken
:Salesforceで私のセキュリティトークンのリセットを行った際にメールで送られてくる文字列
ex:hjHD1ZHPlRR1FIpj24hBstTq
7.テンプレートCSV
:このライブラリ独自の引数。実行時にシナリオファイル直下に「テンプレート」というフォルダが作成され、
そこにアクセス可能なデータ用のテンプレートCSVファイルが[日本語名]template[物理名].csvというファイル名で作成される。
そのCSVファイル名をフルパスで設定する。
ex:C:\Salesforceサンプル\テンプレート\リード_template_Lead.csv
8.データ絞込条件
:APIに渡す際の絞込条件。空でも利用可能
ex:
9.データ取得上限数
:データ取得する際の上限件数。
ex:500
10.出力先CSV
:取得したデータはテンプレートファイルをコピーした状態後、CSVファイル形式で保存される。
ファイルは、ヘッダーが2行で表示名が一行目、二行目が物理名となっており、データは三行目以降に記載される。
フルパスで指定
ex:リード.csv
11.シナリオフォルダ名
:シナリオファイルを配置しているパス。末尾の\は不要。
ex:C:\Salesforceサンプル
12.access_token
:実行中に指定したアクセストークンを返す変数名。呼び出すときは空でOK。
エラーが出たときの参考記事
「IP制限を緩和」+セキュリティトークン無しで動作
上記の
・接続アプリケーションの管理で「IP制限を緩和」に設定されている
->passwordの末尾にセキュリティトークンが付与されていないことを確認。IP制限を緩和の場合はセキュリティトークンは不要
に嵌って30分ほどロスしました・・・
Postman を使って色々検証
下記のツールを使ってWebAPIのレスポンス等を検証しました。フリーで使えてかなりお役立ちでした。
https://www.postman.com/
PostmanとSalesforceに関するお役立ち記事
Author And Source
この問題について(WinActorのVBScriptで OAuth 認証を使って Salesforce APIにアクセスするサンプルプログラムを書いてみた。), 我々は、より多くの情報をここで見つけました https://qiita.com/winactor_salesforce/items/2fd48ef998497c7c145a著者帰属:元の著者の情報は、元のURLに含まれています。著作権は原作者に属する。
Content is automatically searched and collected through network algorithms . If there is a violation . Please contact us . We will adjust (correct author information ,or delete content ) as soon as possible .