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.
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 | 
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 |
--------------------------------------------------------------------------------