3

oauth メソッドと power bi を使用して youtube/google アナリティクスに接続しようとしています。私は途中までやり遂げましたが、助けが必要です。これは私がいる場所です:

以下を使用して手動で認証トークンを取得します。

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 の両方を取得できます。

ここに画像の説明を入力

ここで、ドキュメントを正しく理解していれば、access_token が 1 時間後に期限切れになると、取得した refresh_token を使用して、新しい access_token を自動的に作成できます。

Power Query でそれを行うことは可能ですか? 誰も試したことがありますか?

私はそれを行う方法について完全に無知であり、私は開発者ではないため、私のスキルは限られています:(

どんな助けでも大歓迎です!

4

3 に答える 3

2

組み込みコネクタの欠点を回避するために、Analytics API に直接接続するという同様のニーズがありました。Web バージョンの PowerBI が認証エンドポイントを「匿名」ソースとして受け入れるようにするのは少し厄介でしたが、リバース プロキシは「プローブ」GET 要求に200 OK. 主な 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

nonce は GA API では使用されません。ここでは、Power BI が最大 1 分間 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

自動 URL エンコーディングを使用して、Power BI の「ステップ エディター」UI を使用してクエリ パラメーターを調整できるようにします。

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

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

入力を変更せずに返します。この関数の主な用途は、便利な「ステップ エディター」UI を介して別の方法でクエリ変数を更新できるようにすることです。

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 データ ソースに一般化でき、強力な V4 API を操作するための調整も最小限で済みます。

于 2016-11-25T04:50:37.857 に答える
2

OAuth フローを自動的に実行する方法を理解することは、Power BI 開発者チームにとっても簡単ではありません;)

PowerBI DesktopGoogleAnalytics.Accounts()には、 OAuth トークンを自動的に処理するデータ ソース コネクタが組み込まれています。

(現在、Google アナリティクスは Power Query では利用できません。申し訳ありません。)

YouTube アナリティクスの場合、この機能の需要を追跡するPowerBI UserVoiceスレッドがあります。そこにあなたのサポートを示してください!

于 2016-11-23T16:09:31.543 に答える
0

I don't know power bia but if you can send a HTTP POST you should be able to get a new access token using a refresh token

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

Response should be something like this

{
"access_token" : "ya29.1.AADtN_XK16As2ZHlScqOxGtntIlevNcasMSPwGiE3pe5ANZfrmJTcsI3ZtAjv4sDrPDRnQ",
"token_type" : "Bearer",
"expires_in" : 3600
}
于 2016-11-23T07:58:58.087 に答える