├── samples └── run_pivot_report │ ├── pretty.css │ ├── output_example.png │ ├── run_pivot_report.html │ ├── README.md │ └── run_pivot_report.js └── .github └── workflows └── publish.yaml /samples/run_pivot_report/pretty.css: -------------------------------------------------------------------------------- 1 | body { 2 | font-family:sans-serif;font-size:100%; 3 | } 4 | -------------------------------------------------------------------------------- /samples/run_pivot_report/output_example.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googleanalytics/analytics-data-javascript-examples/HEAD/samples/run_pivot_report/output_example.png -------------------------------------------------------------------------------- /.github/workflows/publish.yaml: -------------------------------------------------------------------------------- 1 | name: Push commit 2 | on: 3 | push: 4 | branches: 5 | - master 6 | jobs: 7 | sync-pages-branch: 8 | runs-on: ubuntu-latest 9 | steps: 10 | - uses: actions/checkout@v2 11 | - name: repo-sync 12 | uses: repo-sync/github-sync@v2 13 | with: 14 | source_repo: "https://github.com/googleanalytics/analytics-data-javascript-examples.git" 15 | source_branch: "master" 16 | destination_branch: "gh-pages" 17 | github_token: ${{ secrets.PAT }} 18 | update-pages-branch: 19 | needs: sync-pages-branch 20 | runs-on: ubuntu-latest 21 | steps: 22 | - name: Checkout target branch 23 | uses: actions/checkout@v2 24 | with: 25 | ref: gh-pages 26 | - name: Replace Client ID 27 | run: sed -i 's/YOUR-CLIENT-ID/${{ secrets.CLIENT_ID }}/g' samples/run_pivot_report/run_pivot_report.js 28 | - name: Commit 29 | run: | 30 | git config user.name github-actions 31 | git config user.email github-actions@github.com 32 | git add . 33 | git commit -m "replace the Client ID variable" 34 | git push --force -------------------------------------------------------------------------------- /samples/run_pivot_report/run_pivot_report.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Google Analytics Data API v1 Pivot Report Demo 5 | 6 | 7 | 9 | 10 | 11 |

Google Analytics Data API v1 Pivot Report Demo

12 | 13 | Check out the GitHub repository 14 | to download the demo sources and see deployment instructions. 15 | 16 |

This application demonstrates how to build a 17 | pivot report 18 | using the runPivotReport 19 | method of the Google Analytics Data API v1 20 | and visualize the result using JavaScript.

21 | 22 |

The API call builds a pivot report with country, language and browser 23 | pivots, displaying the number of sessions for the specified date range for each 24 | pivot column.

25 | 26 |

Click "Authorize" button to start the OAuth2 flow and run the query.

27 | 28 |

When running this demo using your own domain, make sure to edit 29 | the run_pivot_report.js file and replace the clientId variable with 30 | your OAuth2 client id.

31 | 32 |

Google Analytics 4 Property ID:

33 |

34 | 35 | 36 | 37 |

38 | 39 |
40 | 41 |
42 | 43 |

44 |   

45 | 
46 | 47 | 48 | 52 | 53 | 54 | -------------------------------------------------------------------------------- /samples/run_pivot_report/README.md: -------------------------------------------------------------------------------- 1 | # Google Analytics Data API v1 Pivot Report demo 2 | 3 | ## Overview 4 | 5 | This application demonstrates how to build a [pivot report](https://developers.google.com/analytics/devguides/reporting/data/v1/pivots) 6 | using the [runPivotReport](https://developers.google.com/analytics/devguides/reporting/data/v1/rest/v1beta/properties/runPivotReport) 7 | method of the [Google Analytics Data API v1](https://developers.google.com/analytics/devguides/reporting/data/v1) 8 | and visualize the result using JavaScript. 9 | 10 | The API call builds a pivot report with `country`, `language` and `browser` 11 | pivots, displaying the number of sessions for the specified date range for each 12 | pivot column. 13 | 14 | ## Output example 15 | 16 | Depending on the amount of data collected by your property within the query date 17 | range, your pivot report will look like this: 18 | 19 | ![Example output](output_example.png) 20 | 21 | ## Live demo 22 | 23 | Open the [live demo](https://googleanalytics.github.io/analytics-data-javascript-examples/samples/run_pivot_report/run_pivot_report.html) 24 | to try out the application. 25 | 26 | ## Running the demo locally 27 | 28 | 1) This demo application uses [OAuth2 flow](https://developers.google.com/identity/protocols/oauth2) 29 | to access data in the Google Analytics 4 property [on behalf of an end user](https://cloud.google.com/docs/authentication/end-user) 30 | and requires OAuth2 credentials created in your [Google Cloud project](https://cloud.google.com/resource-manager/docs/creating-managing-projects). 31 | 32 | 1) Follow these [steps to create an OAuth2 Client ID](https://support.google.com/cloud/answer/6158849). 33 | 1) Using the [Google Developers Console](https://console.developers.google.com/apis/credentials?project=_), 34 | edit your OAuth2 credentials to add a JavaScript origin that corresponds to the domain where you will be 35 | running the demo application (e.g. http://localhost:8080). 36 | 37 | **You may need to change the [publishing status](https://support.google.com/cloud/answer/10311615#publishing-status) 38 | of your application to "Testing" in order to be able to use HTTP urls as a 39 | valid JavaScript origin.** 40 | 1) Edit the `run_pivot_report.js` file and replace `clientId` variable a 41 | client ID created during the previous step. For example: 42 | ``` 43 | const clientId = '123456789-abcdef.apps.googleusercontent.com'; 44 | ``` 45 | 2) The easiest way to run the demo application using your local machine 46 | is to use [Python3](https://www.python.org/downloads/) as a simple web 47 | server. From the current directory, run the following command: 48 | ``` 49 | python3 -m http.server 8080 50 | ``` 51 | 52 | This will start a local web server on port `8080` serving files from the 53 | current directory. You may need to change the port number if port 8080 is 54 | already occupied. In this case, adjust other URLs in this guide accordingly. 55 | 56 | 3) Open the following URL in your browser to run the demo application: 57 | ``` 58 | http://localhost:8080/run_pivot_report.html 59 | ``` 60 | 61 | 5) Click "Authorize" button to start the OAuth2 consent flow and run the query. 62 | 63 | ## Implementation 64 | 65 | This application consists of two main components: 66 | 67 | * `run_pivot_report.js` The JavaScript library that handles authentication, 68 | invokes the Google Analytics Data API v1 and draws the resulting pivot report. 69 | 70 | * `run_pivot_report.html` The HTML file that contains the UI elements 71 | required to run the demo and requires the `run_pivot_report.js` to run. 72 | 73 | OAuth2 flow is handled using standard [Google Single Sign-in JavaScript client methods](https://developers.google.com/identity/sign-in/web/reference). 74 | 75 | [Google API JavaScript client](https://github.com/google/google-api-javascript-client) 76 | is used to invoke the Google Analytics Data API v1. 77 | -------------------------------------------------------------------------------- /samples/run_pivot_report/run_pivot_report.js: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the 'License'); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an 'AS IS' BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | // [START analyticsdata_pivot_demo] 15 | // [START analyticsdata_pivot_demo_initialize] 16 | /** 17 | * TODO(developer): Replace this variable with a client ID for your web 18 | * application from the Google API Console: 19 | * 20 | * https://console.developers.google.com/apis/credentials?project=_ 21 | * 22 | * In your API Console project, add a JavaScript origin that corresponds 23 | * to the domain where you will be running the script (e.g. http://localhost:8080). 24 | */ 25 | const clientId = 'YOUR-CLIENT-ID'; 26 | 27 | // The Google Analytics Data API v1 discovery document url. 28 | // See https://developers.google.com/analytics/devguides/reporting/data/v1/rest/ 29 | // for the most current url. 30 | const discoveryDocs = ['https://analyticsdata.googleapis.com/$discovery/rest?version=v1beta']; 31 | 32 | // Authorization scopes for the Google Analytics Data API reporting calls. 33 | const scopes = 'https://www.googleapis.com/auth/analytics.readonly'; 34 | 35 | 36 | const authorizeButton = document.getElementById('authorize-button'); 37 | const signoutButton = document.getElementById('signout-button'); 38 | const runQueryButton = document.getElementById('run-query-button'); 39 | 40 | function handleClientLoad() { 41 | // Load the API client and auth2 library 42 | gapi.load('client:auth2', initClient); 43 | } 44 | 45 | function initClient() { 46 | // Initialize the GAPI client and OAuth2 flow. 47 | gapi.client.init({ 48 | discoveryDocs: discoveryDocs, 49 | clientId: clientId, 50 | scope: scopes 51 | }).then( () => { 52 | // Listen for sign-in state changes. 53 | gapi.auth2.getAuthInstance().isSignedIn.listen(updateSigninStatus); 54 | 55 | // Handle the initial sign-in state. 56 | updateSigninStatus(gapi.auth2.getAuthInstance().isSignedIn.get()); 57 | 58 | authorizeButton.onclick = handleAuthClick; 59 | signoutButton.onclick = handleSignoutClick; 60 | runQueryButton.onclick = handleRunQueryClick; 61 | }).catch((error) => { 62 | // Display the error on the page. 63 | const errorOutput = document.getElementById('error'); 64 | const textNode = document.createTextNode(JSON.stringify(error, null, 2)); 65 | errorOutput.appendChild(textNode); 66 | }); 67 | } 68 | 69 | function updateSigninStatus(isSignedIn) { 70 | if (isSignedIn) { 71 | authorizeButton.style.display = 'none'; 72 | signoutButton.style.display = 'block'; 73 | runQueryButton.style.display = 'block'; 74 | } else { 75 | authorizeButton.style.display = 'block'; 76 | signoutButton.style.display = 'none'; 77 | runQueryButton.style.display = 'none'; 78 | } 79 | } 80 | 81 | function handleAuthClick(event) { 82 | gapi.auth2.getAuthInstance().signIn(); 83 | } 84 | 85 | function handleSignoutClick(event) { 86 | gapi.auth2.getAuthInstance().signOut(); 87 | } 88 | 89 | function handleRunQueryClick(event) { 90 | makeApiCall(); 91 | } 92 | // [END analyticsdata_pivot_demo_initialize] 93 | 94 | // [START analyticsdata_pivot_demo_make_api_call] 95 | // Load the API and make an API call. Display the results on the screen. 96 | function makeApiCall() { 97 | // Make a call to the Google Analytics Data API v1. This call builds a pivot 98 | // report with 'country', 'language' and 'browser' pivots, displaying the 99 | // number of sessions for each dimension combination. Adjust the date range 100 | // as necessary. 101 | // 102 | // See https://developers.google.com/analytics/devguides/reporting/data/v1/rest/v1beta/properties/runPivotReport 103 | // for more information on pivot report request structure. 104 | // [START analyticsdata_pivot_demo_make_api_call_request] 105 | // Get configuration values provided by a user in the UI. 106 | const propertyId = document.getElementById('property-id').value; 107 | const pivotReportQuery = { 108 | 'property': 'properties/' + propertyId, 109 | 'dateRanges': [ 110 | { 111 | 'startDate': '14daysAgo', 112 | 'endDate': 'yesterday' 113 | } 114 | ], 115 | 'pivots': [ 116 | { 117 | 'fieldNames': [ 118 | 'country' 119 | ], 120 | 'limit': 250, 121 | 'orderBys': [ 122 | { 123 | 'dimension': { 124 | 'dimensionName': 'country' 125 | } 126 | } 127 | ] 128 | }, 129 | { 130 | 'fieldNames': [ 131 | 'language' 132 | ], 133 | 'limit': 3, 134 | 135 | }, 136 | { 137 | 'fieldNames': [ 138 | 'browser' 139 | ], 140 | 'limit': 5, 141 | } 142 | ], 143 | 'metrics': [ 144 | { 145 | 'name': 'sessions' 146 | } 147 | ], 148 | 'dimensions': [ 149 | { 150 | 'name': 'country' 151 | }, 152 | { 153 | 'name': 'language' 154 | }, 155 | { 156 | 'name': 'browser' 157 | } 158 | ] 159 | }; 160 | gapi.client.analyticsdata.properties.runPivotReport(pivotReportQuery).then( 161 | (response) => { 162 | // [END analyticsdata_pivot_demo_make_api_call_request] 163 | 164 | // [START analyticsdata_pivot_demo_make_api_call_draw_headers] 165 | 166 | // ---------------------------------------------------------------- 167 | // Draw the horizontal pivot table headers. 168 | // ---------------------------------------------------------------- 169 | // Get a reference the result table element. 170 | const resultTable = document.getElementById('result'); 171 | 172 | // Clear the output. 173 | while (resultTable.firstChild) { 174 | resultTable.removeChild(resultTable.firstChild); 175 | } 176 | 177 | // Get a reference the header response object for each pivot. 178 | const countryPivotDimensionHeaders = response.result.pivotHeaders[0].pivotDimensionHeaders; 179 | const languagePivotDimensionHeaders = response.result.pivotHeaders[1].pivotDimensionHeaders; 180 | const browserPivotDimensionHeaders = response.result.pivotHeaders[2].pivotDimensionHeaders; 181 | 182 | // Insert a row for the 'browser' pivot header. 183 | const browserPivotRow = resultTable.insertRow(0); 184 | // Insert a row for the 'language' pivot header. 185 | const languagePivotRow = resultTable.insertRow(0); 186 | 187 | // Insert an empty cell for each horizontal header row. 188 | browserPivotRow.insertCell(0); 189 | languagePivotRow.insertCell(0); 190 | 191 | // Draw horizontal headers for 'language' and 'browser' pivots. 192 | for(const languagePivotDimensionHeader of languagePivotDimensionHeaders) 193 | { 194 | // Append a cell to the 'language' header row. 195 | const newCell = languagePivotRow.insertCell(-1); 196 | 197 | // Each 'language' pivot header cell spans multiple cells determined by 198 | // the 'browser' pivot size. 199 | newCell.colSpan = browserPivotDimensionHeaders.length; 200 | 201 | // Populate a 'language' pivot header cell. 202 | const value = languagePivotDimensionHeader.dimensionValues[0].value; 203 | const textNode = document.createTextNode(value); 204 | newCell.appendChild(textNode); 205 | 206 | // For every cell of the 'language' pivot header, multiple 'browser' pivot 207 | // header cells must be created. 208 | for(const browserPivotDimensionHeader of browserPivotDimensionHeaders) 209 | { 210 | // Append a cell to the 'browser' header row. 211 | const newCell = browserPivotRow.insertCell(-1); 212 | 213 | // Populate a 'browser' pivot header cell. 214 | const value = browserPivotDimensionHeader.dimensionValues[0].value; 215 | const textNode = document.createTextNode(value); 216 | newCell.appendChild(textNode); 217 | } 218 | } 219 | // [END analyticsdata_pivot_demo_make_api_call_draw_headers] 220 | 221 | // [START analyticsdata_pivot_demo_make_api_call_create_grid] 222 | 223 | // ---------------------------------------------------------------- 224 | // Draw the vertical pivot table header and create the placeholder 225 | // cell grid. 226 | // ---------------------------------------------------------------- 227 | 228 | // This is a mapping of a cell key to a cell element which will be used to 229 | // locate the table cell when populating the result table with metric values. 230 | // 231 | // Every row of an API response object contains data about a single pivot 232 | // table cell, so it is necessary to locate a cell corresponding to the 233 | // particular dimension combination when populating a table. 234 | const gridMapping = {}; 235 | 236 | // Create a new row for every country in the response. 237 | for(const countryPivotDimensionHeader of countryPivotDimensionHeaders) 238 | { 239 | // Insert a row at the end of the table. 240 | const newRow = resultTable.insertRow(-1); 241 | 242 | // Insert a cell in the row at index 0. 243 | const newCell = newRow.insertCell(0); 244 | 245 | // Populate the first cell of the row with a 'country' pivot dimension 246 | // value. 247 | const value = countryPivotDimensionHeader.dimensionValues[0].value; 248 | const textNode = document.createTextNode(value); 249 | newCell.appendChild(textNode); 250 | 251 | // Create blank placeholder cells of the result table. 252 | for(const languagePivotDimensionHeader of languagePivotDimensionHeaders) 253 | { 254 | for(const browserPivotDimensionHeader of browserPivotDimensionHeaders) 255 | { 256 | const countryDimensionValue = countryPivotDimensionHeader.dimensionValues[0].value; 257 | const languageDimensionValue = languagePivotDimensionHeader.dimensionValues[0].value; 258 | const browserDimensionValue = browserPivotDimensionHeader.dimensionValues[0].value; 259 | 260 | // Create a unique key for the current cell. 261 | const cellKey = [countryDimensionValue, 262 | languageDimensionValue, 263 | browserDimensionValue]; 264 | 265 | // Add a blank placeholder cell to the grid. This cell will be 266 | // populated with a metrics value later. 267 | const blankCell = newRow.insertCell() 268 | 269 | // Remember the reference to the cell by its key. This mapping will 270 | // be used to populate the table with metric values in the next step. 271 | gridMapping[cellKey] = blankCell; 272 | } 273 | } 274 | } 275 | // [END analyticsdata_pivot_demo_make_api_call_create_grid] 276 | 277 | // [START analyticsdata_pivot_demo_make_api_call_populate_metrics] 278 | // ---------------------------------------------------------------- 279 | // Populate the result table with metric values. 280 | // ---------------------------------------------------------------- 281 | 282 | // Populate the results table grid with metric values. Every row of an API 283 | // response object contains data about a single pivot table cell, so each 284 | // cell of the result table is populated individually. 285 | for(const row of response.result.rows) 286 | { 287 | const countryDimensionValue = row.dimensionValues[0].value; 288 | const languageDimensionValue = row.dimensionValues[1].value; 289 | const browserDimensionValue = row.dimensionValues[2].value; 290 | 291 | // The metric value to insert in a cell. 292 | const metricValue = row.metricValues[0].value; 293 | 294 | // Calculate the unique key for the cell. 295 | const cellKey = [countryDimensionValue, 296 | languageDimensionValue, 297 | browserDimensionValue]; 298 | 299 | // Lookup the cell by its key and populate it with the metric value. 300 | const textNode = document.createTextNode(metricValue); 301 | gridMapping[cellKey].appendChild(textNode); 302 | } 303 | // [END analyticsdata_pivot_demo_make_api_call_populate_metrics] 304 | }).catch((error) => { 305 | // Display the error on the page. 306 | const errorOutput = document.getElementById('error'); 307 | const textNode = document.createTextNode(JSON.stringify(error,null, 2)); 308 | errorOutput.appendChild(textNode); 309 | }); 310 | 311 | // Display the report query on the page for debug purposes. 312 | const debugOutput = document.getElementById('query'); 313 | // Clear the output. 314 | while (debugOutput.firstChild) { 315 | debugOutput.removeChild(debugOutput.firstChild); 316 | } 317 | 318 | const textNode = document.createTextNode('Pivot report query:\n' + 319 | JSON.stringify(pivotReportQuery, null, 2)); 320 | debugOutput.appendChild(textNode); 321 | } 322 | 323 | // [END analyticsdata_pivot_demo_make_api_call] 324 | // [END analyticsdata_pivot_demo] 325 | --------------------------------------------------------------------------------