質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
Google スプレッドシート

Google スプレッドシートは、フリーで利用できる表計算ソフト。Webアプリのためインターネットに接続することで利用できます。チャートやグラフの作成のほか、シートを他のユーザーと共有したり、同時に作業を進めることも可能です。

Google API

Googleは多種多様なAPIを提供していて、その多くはウェブ開発者向けのAPIです。それらのAPIは消費者に人気なGoogleのサービス(Google Maps, Google Earth, AdSense, Adwords, Google Apps,YouTube等)に基づいています。

Google Apps Script

Google Apps ScriptはGoogleの製品と第三者のサービスでタスクを自動化するためのJavaScriptのクラウドのスクリプト言語です。

JavaScript

JavaScriptは、プログラミング言語のひとつです。ネットスケープコミュニケーションズで開発されました。 開発当初はLiveScriptと呼ばれていましたが、業務提携していたサン・マイクロシステムズが開発したJavaが脚光を浴びていたことから、JavaScriptと改名されました。 動きのあるWebページを作ることを目的に開発されたもので、主要なWebブラウザのほとんどに搭載されています。

Q&A

0回答

1330閲覧

Hubspotというアプリから情報を取得して、Google Spreadsheetへ記載したい

penmon

総合スコア3

Google スプレッドシート

Google スプレッドシートは、フリーで利用できる表計算ソフト。Webアプリのためインターネットに接続することで利用できます。チャートやグラフの作成のほか、シートを他のユーザーと共有したり、同時に作業を進めることも可能です。

Google API

Googleは多種多様なAPIを提供していて、その多くはウェブ開発者向けのAPIです。それらのAPIは消費者に人気なGoogleのサービス(Google Maps, Google Earth, AdSense, Adwords, Google Apps,YouTube等)に基づいています。

Google Apps Script

Google Apps ScriptはGoogleの製品と第三者のサービスでタスクを自動化するためのJavaScriptのクラウドのスクリプト言語です。

JavaScript

JavaScriptは、プログラミング言語のひとつです。ネットスケープコミュニケーションズで開発されました。 開発当初はLiveScriptと呼ばれていましたが、業務提携していたサン・マイクロシステムズが開発したJavaが脚光を浴びていたことから、JavaScriptと改名されました。 動きのあるWebページを作ることを目的に開発されたもので、主要なWebブラウザのほとんどに搭載されています。

0グッド

0クリップ

投稿2021/05/31 04:35

編集2021/05/31 10:42

前提・実現したいこと

対象:Google Spreadsheet/Hubspot
実現したいこと:Hubspotというアプリから「取引」に関する情報を取得して、Google Spreadsheetへ自動で記載したい

参照

コード設計部分:https://goo.gl/64hQZb
スコープ部分:https://legacydocs.hubspot.com/docs/methods/pipelines/pipelines_overview
OAUthについて:https://developers.google.com/datastudio/connector/oauth2?hl=ja

発生している問題・エラーメッセージ

機能としては以下に切り分けられており、エラーが発生しているのは8になります(9,10は未検証)

1.getOAuth2Access
2.reset
3.getService
4.authCallback
5.logRedirectUri
6.getStages
7.getDeals
8.weiteStages
9.refresh
10.logStages
ーーーーーーーーーーーーーーーーーーーーー
8.weiteStages
Exception: The number of columns in the data does not match the number of columns in the range.
The data has 0 but the range has 2.
writeStages @ コード.gs:247

わからないこと

エラーからは抽出したデータがない(列が0)と読み取りましたが、データはきちんとHubspot内には登録されているので、
適切に抽出出来ていないと考えております。
ただ getStages も getDeals も問題なく実行が出来ており、かつ私がコードの誤っている部分が不明であったため、
ご質問させて頂いております。

※追記※

console.log()にて確認したところ、weiteStagesにおいてstagesがundefinedとなっておりました。

stages = undefined
matrix = [ [ 'StageID', 'StageName' ], undefined ]

getStagesにおいては、stagesにきちんと値が格納されておりました

該当のソースコード

JS

1/** 2 * # Description: This script let's you connect to Hubspot CRM and retrieve # 3 * # its data to populate a Google Spreadsheet. # # 4 * # Detail of the turorial: https://goo.gl/64hQZb 5 * # OAuth2:1B7FSrk5Zi6L1rSxxTDgDEUsPzlukDsi4KGuTMorsTQHhGBzBkMun4iDF  # 6 */ 7/** 8 * Fill in the following variables 9 * 連携のための準備を行う 10 */ 11var CLIENT_ID = 'クライアントID(本来であれば記入あり)'; 12var CLIENT_SECRET = 'クライアントシークレット(本来であれば記入あり)'; 13var SCOPE = 'contacts'; 14var AUTH_URL = 'https://app.hubspot.com/oauth/authorize'; 15var TOKEN_URL = 'https://api.hubapi.com/oauth/v1/token'; 16var API_URL = 'https://api.hubapi.com'; 17 18/** 19 * Create the following sheets in your spreadsheet 20 * "Stages" 21 * "Deals" 22 * 上記2つのシートを作っておく 23 * ログソースとログステージにはピンと来ていない 24 */ 25var sheetNameStages = "Stages"; 26var sheetNameDeals = "Deals"; 27var sheetNameLogSources = "Log: Sources"; 28var sheetNameLogStages = "Log: Stages"; 29 30/** 31 * # --------------------------- AUTHENTICATION ---------------------------- # 32 */ 33 34/** 35 * Authorizes and makes a request to get the deals from Hubspot. 36 * getServiceでサービスを利用する宣言を行い、そこで問題なくアクセスが出来るか確認している 37 */ 38function getOAuth2Access() { 39 var service = getService(); 40 if (service.hasAccess()) { 41 // ... do whatever ... 42 } else { 43 var authorizationUrl = service.getAuthorizationUrl(); 44 Logger.log('Open the following URL and re-run the script: %s', 45 authorizationUrl); 46 } 47} 48/** 49 * Reset the authorization state, so that it can be re-tested. 50 * 何度も行うコードのため、リセットするよう設定しておく 51 */ 52function reset() { 53 getService().reset(); 54} 55 56/** 57 * Configures the service. 58 * 実際に利用するサービスを定義/確認する 59 * エンドポイントURLとは、特定のリソースに対して与えられた固有の一意な URL 60 * OAuth2 ライブラリを追加して設定する 61 */ 62function getService() { 63 return OAuth2.createService('hubspot') 64 // Set the endpoint URLs. 65 .setTokenUrl(TOKEN_URL) 66 .setAuthorizationBaseUrl(AUTH_URL) 67 68 // Set the client ID and secret. 69 .setClientId(CLIENT_ID) 70 .setClientSecret(CLIENT_SECRET) 71 72 // Set the name of the callback function in the script referenced 73 //APIの利用を完了させるために必要 74 // above that should be invoked to complete the OAuth flow. 75 .setCallbackFunction('authCallback') 76 77 // Set the property store where authorized tokens should be persisted. 78 //ちゃんと許可されたという証拠を保存する 79 .setPropertyStore(PropertiesService.getUserProperties()) 80 .setScope(SCOPE); 81} 82 83/** 84 * Handles the OAuth2 callback. 85 * この関数は OAuth 2.0 フローを完了するために呼び出されます。 86 * サードパーティの認証サービスからのコールバック応答は引数として提供されるため、この関数で処理する必要があります。 87 */ 88function authCallback(request) { 89 var service = getService(); 90 var authorized = service.handleCallback(request); 91 if (authorized) { 92 return HtmlService.createHtmlOutput('Success!'); 93 } else { 94 return HtmlService.createHtmlOutput('Denied.'); 95 } 96} 97/** 98 * Logs the redict URI to register. 99 * URIとは、Uniform Resource Identifierの略で、URLの上位概念(URI:寿司、URL:ちらし寿司) 100 * 保存されたリクエストをトリガーするために、リダイレクトできる URI を取得する 101 * リダイレクト URI、すなわち応答 URL は、アプリが正しく承認され、認証コードまたはアクセス トークンが付与されると、承認サーバーがユーザーを送り出す場所です。 102 * ロギングとは、起こった出来事についての情報などを時間経過にそって記録する履歴のことです。 103 * ソフトウエアを使用する際に特にエラーが発生した場合にこの履歴を確認することで、どこでどんな不具合が発生したか把握でき対応を絞り込むことができます。 104 * Loggerで出力できる 105 */ 106function logRedirectUri() { 107 Logger.log(getService().getRedirectUri()); 108} 109 110/** 111 * # ------------------------------- GET DATA ------------------------------ # 112 */ 113 114/** 115 * Get the different stages in your Hubspot pipeline 116 * API & Documentation URL: https://developers.hubspot.com/docs/methods/deal-pipelines/get-deal-pipeline 117 * Bearer認証は, トークンを利用した認証・認可に使用されることを想定しており, OAuth 2.0の仕様の一部として定義されているが, その仕様内でHTTPでも使用しても良いと記述されている. 118bearerは「担い手」や「使い」といった意味を持つ. 119詳細は, 『RFC 6750 The OAuth 2.0 Authorization Framework: Bearer Token Usage』を参照. 120HTTPのAuthorizationヘッダにスキームとして指定でき, Authorization: Bearer <token> のようにして指定する. 121 */ 122function getStages() { 123 // Prepare authentication to Hubspot 124 var service = getService(); 125 var headers = {headers: {'Authorization': 'Bearer ' + service.getAccessToken()}}; 126 127/** 128 * UrlFetchAppはurlを入れるだけでもアクセスは出来ますが、相手先のウェブサービスやサーバーの条件に応じて様々なオプションを追加してあげる必要性があります。 129 * Web APIを利用するためには、「HTTPリクエストを送る」という操作をする必要があるわけですが、GASにはそのための機能が用意されています。 130 それを提供するのがUrlFetchサービスのfetchメソッドになります。 131 */ 132 // API request 133 var pipelineId = "default"; // Enter your pipeline id here. 134 var url = API_URL + "/crm-pipelines/v1/pipelines/deals"; 135 var response = UrlFetchApp.fetch(url, headers); 136 var result = JSON.parse(response.getContentText()); 137 var stages = Array(); 138 139 // Looping through the different pipelines you might have in Hubspot 140 result.results.forEach(function(item) { 141 if (item.pipelineId == pipelineId) { 142 var result_stages = item.stages; 143 // Let's sort the stages by displayOrder 144 result_stages.sort(function(a,b) { 145 return a.displayOrder-b.displayOrder; 146 }); 147 148 // Let's put all the used stages (id & label) in an array 149 result_stages.forEach(function(stage) { 150 stages.push([stage.stageId,stage.label]); 151 }); 152 } 153 }); 154 155 return stages; 156} 157 158/** 159 * Get the deals from your Hubspot pipeline 160 * API & Documentation URL: https://developers.hubspot.com/docs/methods/deals/get-all-deals 161 */ 162function getDeals() { 163 // Prepare authentication to Hubspot 164 var service = getService(); 165 var headers = {headers: {'Authorization': 'Bearer ' + service.getAccessToken()}}; 166 167 // Prepare pagination 168 // Hubspot lets you take max 250 deals per request. 169 // We need to make multiple request until we get all the deals. 170 var keep_going = true; 171 var offset = 0; 172 var deals = Array(); 173 174 while(keep_going) 175 { 176 // We'll take three properties from the deals: the source, the stage & the amount of the deal 177 var url = API_URL + "/deals/v1/deal/paged?properties=dealstage&properties=source&properties=amount&limit=250&offset="+offset; 178 var response = UrlFetchApp.fetch(url, headers); 179 var result = JSON.parse(response.getContentText()); 180 181 // Are there any more results, should we stop the pagination ? 182 keep_going = result.hasMore; 183 offset = result.offset; 184 185 // For each deal, we take the stageId, source & amount 186 result.deals.forEach(function(deal) { 187 var stageId = (deal.properties.hasOwnProperty("dealstage")) ? deal.properties.dealstage.value : "unknown"; 188 var source = (deal.properties.hasOwnProperty("source")) ? deal.properties.source.value : "unknown"; 189 var amount = (deal.properties.hasOwnProperty("amount")) ? deal.properties.amount.value : 0; 190 deals.push([stageId,source,amount]); 191 }); 192 } 193 194 return deals; 195} 196 197 198/** 199* # -------------------------- WRITE TO SPREADSHEET ----------------------- # 200*/ 201 202/** 203 * Print the different stages in your pipeline to the spreadsheet 204 */ 205function writeStages(stages) { 206 var ss = SpreadsheetApp.getActiveSpreadsheet(); 207 var sheet = ss.getSheetByName(sheetNameStages); 208 209 // Let's put some headers and add the stages to our table 210 var matrix = Array(["StageID","StageName"]); 211 matrix = matrix.concat(stages); 212 213 // Writing the table to the spreadsheet 214 //getRange(row, column, numRows, numColumns) 215 var range = sheet.getRange(1,1,matrix.length,matrix[0].length); 216 range.setValues(matrix); 217} 218 219/** 220* # -------------------------------- ROUTINE ------------------------------ # 221*/ 222 223/** 224 * This function will update the spreadsheet. This function should be called 225 * every hour or so with the Project Triggers. 226 */ 227function refresh() { 228 var service = getService(); 229 230 if (service.hasAccess()) { 231 var stages = getStages(); 232 writeStages(stages); 233 234 var deals = getDeals(); 235 writeDeals(deals); 236 237 } else { 238 var authorizationUrl = service.getAuthorizationUrl(); 239 Logger.log('Open the following URL and re-run the script: %s', 240 authorizationUrl); 241 } 242} 243 244/** 245 * This function will log the amount of leads per stage over time 246 * and print it into the sheet "Log: Stages" 247 * It should be called once a day with a Project Trigger 248 */ 249function logStages() { 250 var ss = SpreadsheetApp.getActiveSpreadsheet(); 251 var sheet = ss.getSheetByName("Stages: Count"); 252 var getRange = sheet.getRange("B2:B12"); 253 var row = getRange.getValues(); 254 row.unshift(new Date); 255 var matrix = [row]; 256 257 var ss = SpreadsheetApp.getActiveSpreadsheet(); 258 var sheet = ss.getSheetByName("Log: Stages"); 259 var lastRow = sheet.getLastRow(); 260 var lastCol = sheet.getLastColumn(); 261 262 // Writing at the end of the spreadsheet 263 var setRange = sheet.getRange(lastRow+1,1,1,row.length); 264 setRange.setValues(matrix); 265}

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

kikukiku

2021/05/31 05:11

writeStagesメソッド内のgetRangeの実行時にエラーになっていると思うので、 素直に、その直前で、matrixやstagesの内容を確認したら良いと思います。
penmon

2021/05/31 10:37

早急にご回答頂きありがとうございます。 ご指摘頂いた部分をconsole.log()にて確認したところ、stagesがundefinedとなっておりました。 こちらgetStages関数ではきちんとstagesに値が入っていたため、なぜその間に値がわからなくなったのか検討がつきません。 可能でしたら、どのような可能性が考えられるのかご教授頂きたいです。
kikukiku

2021/06/01 00:11

getStages関数で取得したstagesを、そのままwriteStages関数に渡しているようなので、 何か評価方法に間違いがあるように感じました。 例えば、getStages関数でstagesに値が入っていると確認したときには、 なんらかのエラーが発生していない状態であったとか。 なので、getStages関数と、writeStages関数の両方にログを入れて 同じタイミングで評価するようにしたら、原因の糸口が見つかるのではないかと思います。
kikukiku

2021/06/01 00:13

refresh関数が頻繁に呼ばれたりしていないかなども、確認しておいた方が良いかも。
penmon

2021/06/01 13:51

丁寧にありがとうございます。 先ほど再度確認をしたところ、下記部分にて別途エラーが発生しておりました。 またエラーを示す行(390)が存在しない行になるのですが、こちら何が類似事例はご存じでしょうか? 4.authCallback エラー TypeError: Cannot read property 'parameter' of undefined Service_.handleCallback @ Service.gs:390
kikukiku

2021/06/02 00:19

OAuth2.0に関しては経験がなく、解決まで導くことは難しいと判断し、 回答欄ではなく、ここにコメントさせて頂いています。 今までわかったことを、質問欄に記載し、他の回答者を待った方が良いかもしれません。
kikukiku

2021/06/02 00:22

今できることとしては、authCallback関数内に発生していることなのか(たぶんそうではない)。 .setCallbackFunction('authCallback')実行時に発生していることなのか(たぶんそうではない)。 を確認することはできると思います(ログ入れればわかるはず)。 上記想定通りだとすると、.setCallbackFunction('authCallback')以前に問題があるということになると 思います。
penmon

2021/06/05 08:24

経験がないにも関わらず、ご丁寧に対応頂きありがとうございました。 ご記載頂いた内容をもとに、自分でも試行錯誤いたします。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

まだ回答がついていません

会員登録して回答してみよう

アカウントをお持ちの方は

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問