2016-11-22 210 views
2

我正嘗試使用oauth方法和power bi連接到youtube/google分析。 我已經管理了一半,我需要一些幫助。這是我在這裏:Google oauth:刷新Power Query中的令牌

我獲得授權令牌使用手動:

https://accounts.google.com/o/oauth2/auth?scope=https://www.googleapis.com/auth/yt-analytics.readonly&response_type=code&access_type=offline&redirect_uri=urn:ietf:wg:oauth:2.0:oob&approval_prompt=force&client_id={clientid} 

一旦我有,我把它放在我的查詢和我能夠同時獲得的access_token和refresh_token:現在

enter image description here

,如果我理解正確的文件,當一個的access_token小時後到期,然後我就可以用我得到了refresh_token,自動創建一個新的access_token,

Power Query中可以這樣做嗎?有人試過嗎?

我對如何做到這一點完全沒有頭緒,而且我不是一個開發人員,所以我的技能是有限的:(

任何幫助表示讚賞!

回答

0

我不知道電力BIA但如果你可以送你應該能夠使用令牌刷新,以獲得新的訪問令牌的HTTP POST

POST https://accounts.google.com/o/oauth2/token 
client_id={ClientId}&client_secret={ClientSecret}&refresh_token={RefreshToken}&grant_type=refresh_token 

的反應應該是這樣的

{ 
"access_token" : "ya29.1.AADtN_XK16As2ZHlScqOxGtntIlevNcasMSPwGiE3pe5ANZfrmJTcsI3ZtAjv4sDrPDRnQ", 
"token_type" : "Bearer", 
"expires_in" : 3600 
} 
+0

是工作,我有錯誤HTTP!這就是爲什麼我得到了錯誤。我終於搞定了,謝謝! – ruthpozuelo

+0

我可以問一個跟進問題嗎?爲了獲得驗證碼,我使用了上面在瀏覽器中提到的URL,然後在Power Query中複製粘貼驗證碼。現在,Power Query中有沒有辦法做到這一點? – ruthpozuelo

1

搞清楚怎麼做的OAuth自動流是不容易的,我們的電源BI開發團隊要麼;)

PowerBI Desktop有一個內置的數據源連接器GoogleAnalytics.Accounts()它處理的OAuth令牌自動。

(谷歌Analytics(分析)不處於開機查詢今天,對不起。有售)

對於YouTube Analytics(分析),有此功能的一個PowerBI UserVoice線程跟蹤需求。在那裏展示你的支持!

+0

那麼,我很自豪,我來到目前爲止!我知道uservoice中的YouTube分析思路,不幸的是,我不想等待它,所以我正在嘗試自己做。 :)我已經爲Oauth連接器投了票,儘管... – ruthpozuelo

2

我們有類似的需求直接連接到Analytics API,以規避內置連接器的缺點。讓PowerBI的Web版本接受作爲「匿名」源的auth端點是有點尷尬的,但是一個反向代理可以通過用200 OK響應'probe'GET請求來欺騙它。 這裏的主要PowerQuery/M邏輯,分爲功能:

GetAccessToken_GA

let 
    Source = (optional nonce as text) as text => let 
     // use `nonce` to force a fresh fetch 

     someNonce = if nonce = null or nonce = "" 
      then "nonce" 
      else nonce, 

     // Reverse proxy required to trick PowerBI Cloud into allowing its malformed "anonymous" requests to return `200 OK`. 
     // We can skip this and connect directly to GA, but then the Web version will not be able to refresh. 
     url = "https://obfuscated.herokuapp.com/oauth2/v4/token", 

     GetJson = Web.Contents(url, 
      [ 
       Headers = [ 
        #"Content-Type"="application/x-www-form-urlencoded" 
       ], 
       Content = Text.ToBinary(
        // "code=" & #"Google API - Auth Code" 
        // "&redirect_uri=urn:ietf:wg:oauth:2.0:oob" 

        "refresh_token=" & #"Google API - Refresh Token" 
        & "&client_id=" & #"Google API - Client ID" 
        & "&client_secret=" & #"Google API - Client Secret" 
        // & "&scope=https://www.googleapis.com/auth/analytics.readonly" 
        & "&grant_type=refresh_token" 
        & "&nonce=" & someNonce 
       ) 
      ] 
     ), 
     FormatAsJson = Json.Document(GetJson), 

     // Gets token from the Json response 
     AccessToken = FormatAsJson[access_token], 
     AccessTokenHeader = "Bearer " & AccessToken 
    in 
     AccessTokenHeader 
in 
    Source 

returnAccessHeaders_GA

現時值是不使用GA API,我用它這裏允許Power BI最多一分鐘緩存API請求。

let 
    returnAccessHeaders =() as text => let 
     nonce = DateTime.ToText(DateTime.LocalNow(), "yyyyMMddhhmm"), 
     AccessTokenHeader = GetAccessToken_GA(nonce) 

    in 
     AccessTokenHeader 
in 
    returnAccessHeaders 

parseJsonResponse_GA
let 
    fetcher = (jsonResponse as binary) as table => let 
     FormatAsJsonQuery = Json.Document(jsonResponse), 

     columnHeadersGA = FormatAsJsonQuery[columnHeaders], 
     listRows = Record.FieldOrDefault(
      FormatAsJsonQuery, 
      "rows", 
      {List.Transform(columnHeadersGA, each null)} 
      // a list of (lists of length exactly matching the # of columns) of null 
     ), 
     columnNames = List.Transform(columnHeadersGA, each Record.Field(_, "name")), 

     matchTypes = (column as record) as list => let 
      values = { 
       { "STRING", type text }, 
       { "FLOAT", type number }, 
       { "INTEGER", Int64.Type }, 
       { "TIME", type number }, 
       { "PERCENT", type number }, 
       { column[dataType], type text } // default type 
      }, 

      columnType = List.First(
       List.Select(
        values, 
        each _{0} = column[dataType] 
       ) 
      ){1}, 

      namedColumnType = { column[name], columnType } 

     in namedColumnType, 

     recordRows = List.Transform(
      listRows, 
      each Record.FromList(_, columnNames) 
     ), 

     columnTypes = List.Transform(columnHeadersGA, each matchTypes(_)), 
     rowsTable = Table.FromRecords(recordRows), 
     typedRowsTable = Table.TransformColumnTypes(rowsTable, columnTypes) 

    in typedRowsTable 

in fetcher 

fetchAndParseGA

Web.Contents()到的第一個參數必須是字符串文字或悲傷隨之而來。

let 
    AccessTokenHeader = returnAccessHeaders_GA(), 

    fetchAndParseGA_fn = (url as text) as table => let 
     JsonQuery = Web.Contents(
      "https://gapis-powerbi-revproxy.herokuapp.com/analytics", 
       [ 
        RelativePath = url, 
        Headers = [ 
         #"Authorization" = AccessTokenHeader 
        ] 
       ] 
      ), 
     Response = parseJsonResponse_GA(JsonQuery) 
    in 
     Response 
in 
    fetchAndParseGA_fn 

queryUrlHelper

可以讓我們美國的力量BI的 '步驟編輯器' UI調整查詢參數,自動URL編碼。

let 
    safeString = (s as nullable text) as text => let 
     result = if s = null 
      then "" 
      else s 
    in 
     result, 

    uriEncode = (s as nullable text) as text => let 
     result = Uri.EscapeDataString(safeString(s)) 
    in 
     result, 

    optionalParam = (name as text, s as nullable text) => let 
     result = if s = null or s = "" 
      then "" 
      else "&" & name & "=" & uriEncode(s) 
    in 
     result, 

    queryUrlHelper = (
     gaID as text, 
     startDate as text, 
     endDate as text, 
     metrics as text, 
     dimensions as nullable text, 
     sort as nullable text, 
     filters as nullable text, 
     segment as nullable text, 
     otherParameters as nullable text 
    ) as text => let 
     result = "/v3/data/ga?ids=" & uriEncode(gaID) 
      & "&start-date=" & uriEncode(startDate) 
      & "&end-date=" & uriEncode(endDate) 
      & "&metrics=" & uriEncode(metrics) 
      & optionalParam("dimensions", dimensions) 
      & optionalParam("sort", sort) 
      & optionalParam("filters", filters) 
      & optionalParam("segment", segment) 
      & safeString(otherParameters) 
    in 
     result, 

    Example = queryUrlHelper(
     "ga:59361446", // gaID 
     "MONTHSTART", // startDate 
     "MONTHEND", // endDate 
     "ga:sessions,ga:pageviews", // metrics 
     "ga:userGender", // dimensions 
     "-ga:sessions", // sort 
     null, // filters 
     "gaid::BD_Im9YKTJeO9xDxV4w6Kw", // segment 
     null // otherParameters (must be manually url-encoded, and start with "&") 
    ) 
in 
    queryUrlHelper 

getLinkForQueryExplorer

只是一個方便,在the Query Explorer.

let 
    getLinkForQueryExplorer = (querySuffixUrl as text) as text => let 
     // querySuffixUrl should start like `/v3/data/ga?ids=ga:132248814&...` 
     link = Text.Replace(
      querySuffixUrl, 
      "/v3/data/ga", 
      "https://ga-dev-tools.appspot.com/query-explorer/" 
     ) 
    in 
     link 
in 
    getLinkForQueryExplorer 

打開查詢

Identity

返回其I輸入不變;此功能的主要用途是允許通過方便的「步驟編輯器」UI以另一種方式訪問​​update query variables

let 
    Identity = (x as any) as any => let 
     x = x 
    in 
     x 
in 
    Identity 

getMonthBoundary
// Get a list of the start and end dates of the relative month, as ISO 8601 formatted dates. 
// 
// The end date of the current month is considered to be the current date. 
// 
// E.g.: 
// ``` 
// { 
//  "2016-09-01", 
//  "2016-09-31" 
// } 
// ``` 
// 
// Source: <https://gist.github.com/r-k-b/db1eb0e00364cb592e1d8674bb03cb5c> 

let 
    GetMonthDates = (monthOffset as number) as list => let 
     now = DateTime.LocalNow(), 
     otherMonth = Date.AddMonths(now, monthOffset), 
     month1Start = Date.StartOfMonth(otherMonth), 
     month1End = Date.AddDays(Date.EndOfMonth(otherMonth), -1), 

     dates = { 
      month1Start, 
      month1End 
     }, 

     result = List.Transform(
      dates, 
      each DateTime.ToText(_, "yyyy-MM-dd") 
     ) 
    in 
     result 
in 
    GetMonthDates 

replaceUrlDates

// 
// E.g., on 2016-10-19 this is the result: 
// ``` 
// replaceDates(-1, "/foo?s=MONTHSTART&e=MONTHEND") === "/foo?s=2016-09-01&e=2016-09-28" 
// ``` 

let 
    replaceDates = (monthOffset as number, rawUrl as text) as text => let 
     boundaryList = getMonthBoundary(monthOffset), 

     stage01 = Text.Replace(
      rawUrl, 
      "MONTHSTART", 
      boundaryList{0} 
     ), 

     stage02 = Text.Replace(
      stage01, 
      "MONTHEND", 
      boundaryList{1} 
     ), 

     stage03 = replaceViewNames(stage02) 
    in 
     stage03 

in 
    replaceDates 

示例查詢

let 
    QueryBase = queryUrlHelper("All Web Site Data", "MONTHSTART", "today", "ga:sessions,ga:pageviews,ga:pageviewsPerSession", "ga:deviceCategory,ga:yearMonth", null, null, null, null), 
    MonthOffset = Identity(#"Months Back to Query"), 
    QueryURL = replaceUrlDates(MonthOffset, QueryBase), 
    CopyableLinkToQueryExplorer = getLinkForQueryExplorer(QueryURL), 
    Source = fetchAndParseGA(QueryURL) 
in 
    Source 

作爲獎勵,這可以推廣到任何OAuthV2數據源,還需要最少的調整與the powerful V4 API.

+1

哇 - 感謝分享。你能描述你的身份和替換UrlDates功能嗎? –

+0

對於每個請求,隨機數應該是唯一的。我不確定Google對nonce格式的要求是什麼,但是您可以使用類似於「Text.NewGuid()」或「Text.Replace(Text.NewGuid(),」 - 「,」「)」的東西。 –

+1

感謝分享你的代碼:)我會給你的代碼一個去,如果你來了瑞典我會給你買一瓶啤酒!乾杯! – ruthpozuelo