2016-11-22 170 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