😀

Salesforce API通信できるまで(2)

2024/12/14に公開

https://qiita.com/advent-calendar/2024/kaonavi

概要

  • カオナビではSalesforceとAPI連携してシステム開発をしてます
  • この記事では【Salesforce のAPI疎通確認方法】を投稿します
  • 前の記事では「Developer環境作成」 から「データの登録」「APIを利用するためのフレームワーク設定」までをまとめております
    • 今回はデータを登録した内容をAPIを通じて取得する疎通確認までやってみます
  • 開発や仕組みは理解してても具体的にどうやったらSalesforceとAPI通信ができるかが個人的には割らなかったので、その過程をまとめておりました

今回の流れ

  • データの登録
  • APIの疎通確認
    • shellで
    • phpで
  • 疎通確認でハマりやすい箇所と解析
    をお送りします

前提

前の記事でテストアカウントオブジェクトが登録できる状態にあることです

こんなかんじに「アカウントテスト」のレコードが2件登録します

テストアカウント

アカウトテスト名 テストチェックボックス 作成者 所有者 最終更新者
てすとほげほげ ON (自分の名前) (自分の名前) (自分の名前)
てすとほげほげ2 OFF (自分の名前) (自分の名前) (自分の名前)

これの取得するAPIを実装します

ソース

  • ちゃんとしたら長い実装になりそうですが、いったん疎通確認という理由で簡単なプログラムを用意いたします
test_sfdc.sh
#!/bin/bash

# Salesforce認証情報
CLIENT_ID="コンシューマーキー"
CLIENT_SECRET="コンシューマーシークレット"
USERNAME="ユーザー名"
PASSWORD="パスワード"
SECURITY_TOKEN="セキュリテートークン"

INSTANCE_URL="https://{your_domain}.develop.my.salesforce.com"

echo "Attempting to get access token..."

echo "Sending data:"
echo "grant_type=password&client_id=${CLIENT_ID}&client_secret=${CLIENT_SECRET}&username=${USERNAME}&password=${PASSWORD}${SECURITY_TOKEN}" | sed 's/./*/g'

CURL_RESPONSE=$(curl -s "${INSTANCE_URL}/services/oauth2/token" \
  -d "grant_type=password" \
  -d "client_id=${CLIENT_ID}" \
  -d "client_secret=${CLIENT_SECRET}" \
  -d "username=${USERNAME}" \
  -d "password=${PASSWORD}${SECURITY_TOKEN}")

echo "Full cURL Response:"
echo "$CURL_RESPONSE"

TOKEN_RESPONSE=$(echo "$CURL_RESPONSE" | grep -o '{.*}' | head -n 1)

echo "Extracted Token Response: $TOKEN_RESPONSE"

if echo "$TOKEN_RESPONSE" | jq -e '.access_token' > /dev/null 2>&1; then
  ACCESS_TOKEN=$(echo "$TOKEN_RESPONSE" | jq -r '.access_token')
  INSTANCE_URL=$(echo "$TOKEN_RESPONSE" | jq -r '.instance_url')
  echo "Access Token obtained successfully"

  QUERY="SELECT Id, Name FROM AccountTest1__c LIMIT 5"
  ENCODED_QUERY=$(echo $QUERY | sed 's/ /%20/g')

  echo "Attempting to query data..."

  RESPONSE=$(curl -s "${INSTANCE_URL}/services/data/v57.0/query?q=${ENCODED_QUERY}" \
    -H "Authorization: Bearer ${ACCESS_TOKEN}" \
    -H "Content-Type: application/json")

  echo "API Response: $RESPONSE"

  if echo "$RESPONSE" | jq -e '.records' > /dev/null 2>&1; then
    echo "Query successful. Records:"
    echo $RESPONSE | jq '.records[]'
  else
    echo "Error: Unable to retrieve records"
    echo $RESPONSE | jq '.'
  fi
else
  echo "Error: Failed to obtain access token"
  echo "Token Response: $TOKEN_RESPONSE"
  exit 1
fi
test_sfdc.php

同じ処理のphp版はこちらです

<?php

mb_internal_encoding('UTF-8');
mb_http_output('UTF-8');
mb_language('Japanese');

// Salesforce認証情報
$clientId = "コンシューマーキー";
$clientSecret = "コンシューマーシークレット";
$username = "ユーザー名";
$password = "パスワード";
$securityToken = "セキュリテートークン";

$instanceUrl = "https://{your_domain}.develop.my.salesforce.com";

echo "Attempting to get access token...\n";

// アクセストークンを取得
$tokenUrl = $instanceUrl . "/services/oauth2/token";
$params = [
    "grant_type" => "password",
    "client_id" => $clientId,
    "client_secret" => $clientSecret,
    "username" => $username,
    "password" => $password . $securityToken
];

$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $tokenUrl);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($params));
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);

$response = curl_exec($ch);
curl_close($ch);

echo "Full cURL Response:\n" . $response . "\n";

$result = json_decode($response, true);

if (isset($result['access_token'])) {
    $accessToken = $result['access_token'];
    $instanceUrl = $result['instance_url'];
    echo "Access Token obtained successfully\n";

    // データのクエリ
    $query = "SELECT Id, Name FROM AccountTest1__c LIMIT 5";
    $encodedQuery = urlencode($query);

    echo "Attempting to query data...\n";

    $queryUrl = $instanceUrl . "/services/data/v57.0/query?q=" . $encodedQuery;

    $ch = curl_init();
    curl_setopt($ch, CURLOPT_URL, $queryUrl);
    curl_setopt($ch, CURLOPT_HTTPHEADER, [
        "Authorization: Bearer " . $accessToken,
        "Content-Type: application/json"
    ]);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);

    $response = curl_exec($ch);
    curl_close($ch);

    echo "API Response:\n" . $response . "\n";

    $result = json_decode($response, true);

    if (isset($result['records'])) {
        echo "Query successful. Records:\n";
        foreach ($result['records'] as $record) {
            echo json_encode($record, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE) . "\n";
        }
    } else {
        echo "Error: Unable to retrieve records\n";
        echo json_encode($result, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE) . "\n";
    }
} else {
    echo "Error: Failed to obtain access token\n";
    echo "Token Response: " . $response . "\n";
}

?>

実行結果

実行結果
$ sh test_sfdc.sh
Attempting to get access token...
Sending data:
****************************************************************************************************************************************************************************************************************************************************************************
Full cURL Response:

{"access_token":"********************","instance_url":"https://shokunincom-dev-ed.develop.my.salesforce.com","id":"https://login.salesforce.com/id/00DGA000009nds92AA/005GA00000B0tUJYAZ","token_type":"Bearer","issued_at":"1734161779431","signature":"********"}
Access Token obtained successfully
Attempting to query data...
API Response: {"totalSize":2,"done":true,"records":[{"attributes":{"type":"AccountTest1__c","url":"/services/data/v57.0/sobjects/AccountTest1__c/a00GA00007aI2x0YAC"},"Id":"a00GA00007aI2x0YAC","Name":"テストほげほげ2"},{"attributes":{"type":"AccountTest1__c","url":"/services/data/v57.0/sobjects/AccountTest1__c/a00GA00007aI2Q5YAK"},"Id":"a00GA00007aI2Q5YAK","Name":"テストほげほげ"}]}
Query successful. Records:
{
  "attributes": {
    "type": "AccountTest1__c",
    "url": "/services/data/v57.0/sobjects/AccountTest1__c/a00GA00007aI2x0YAC"
  },
  "Id": "a00GA00007aI2x0YAC",
  "Name": "テストほげほげ2"
}
{
  "attributes": {
    "type": "AccountTest1__c",
    "url": "/services/data/v57.0/sobjects/AccountTest1__c/a00GA00007aI2Q5YAK"
  },
  "Id": "a00GA00007aI2Q5YAK",
  "Name": "テストほげほげ"
}

ハマったポイント

ユーザー名

ユーザー名はログインするメールアドレス式の名前です
必ずしも自分が利用している生きてるメールアドレスではありませんのでご注意ください

パスワード

パスワードはSalesforceでログインするときのパスワードです

passwordのセットは${パスワード} ${セキュリティ−トークン}

oauth認証するときのpasswordは[パスワード]+[セキュリテートークン]です、

    "password" => $password . $securityToken

ログイン履歴で何が失敗したかを解析

api疎通で原因がよくわからないままエラーが出ることが初期は多いかと思います
シンプルにcurlで実行して確認をしてみましょう

$ curl https://{your_domain}.develop.my.salesforce.com/services/oauth2/token \
-d 'grant_type=password' \
-d 'client_id=*****************' \
-d 'client_secret=***************' \
-d 'username=*********' \
-d 'password=**********'

{"error":"invalid_grant","error_description":"authentication failure"}%

invalid_grant authentication failureとでても、じゃあ具体的に何が悪いんだ?となります、そういうときは「ログイン履歴」をみて原因を調べてみましょう

設定 -> クイック検索 -> ログ -> ログイン履歴で画面遷移できます

「ユーザー名-パスワードフローが無効になっています」
の原因はOAuth ユーザー名パスワードフローを許可されてないのが原因でした

「設定」から検索ボックスで「OAuth および OpenID Connect 設定」を検索
OAuth および OpenID Connect 設定画面で「OAuth ユーザー名パスワードフローを許可」をON

Discussion