Skip to content

Commit 8aa51b2

Browse files
fmontesclaude
andcommitted
feat: flatten REST endpoint response with named fields
Improved REST API response format from positional arrays to self-documenting named objects for better developer experience. **Before:** ```json { "rows": [ { "dimensions": ["2026-02-09", "/products"], "metrics": ["150", "120"] } ] } ``` **After:** ```json { "dimensions": ["date", "pagePath"], "metrics": ["sessions", "activeUsers"], "rows": [ { "date": "2026-02-09", "pagePath": "/products", "sessions": "150", "activeUsers": "120" } ] } ``` **Changes:** - Added dimension/metric name arrays to response metadata - Flattened row structure with named fields instead of positional arrays - Updated README with REST API usage examples - Added dimensions & metrics explainer section **Breaking change:** Response format changed, but acceptable since endpoint not yet released. Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
1 parent f4db711 commit 8aa51b2

2 files changed

Lines changed: 116 additions & 20 deletions

File tree

README.md

Lines changed: 87 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,58 @@ $gaRequest.setDimensions("date")
7272
</table>
7373
```
7474

75+
### REST API Usage
76+
77+
Query Google Analytics data via REST endpoint:
78+
79+
```bash
80+
curl -X POST http://localhost:8080/api/v1/googleanalytics/query \
81+
-H "Content-Type: application/json" \
82+
-u admin@dotcms.com:admin \
83+
-d '{
84+
"propertyId": "123456789",
85+
"startDate": "2026-02-09",
86+
"endDate": "2026-02-16",
87+
"metrics": ["sessions", "activeUsers"],
88+
"dimensions": ["date", "pagePath"],
89+
"filters": {
90+
"dimension": [
91+
{"field": "pagePath", "value": "/products", "operator": "CONTAINS"}
92+
]
93+
},
94+
"sort": "sessions",
95+
"maxResults": 100
96+
}'
97+
```
98+
99+
**Response:**
100+
101+
```json
102+
{
103+
"rowCount": 7,
104+
"dimensions": ["date", "pagePath"],
105+
"metrics": ["sessions", "activeUsers"],
106+
"rows": [
107+
{
108+
"date": "20260209",
109+
"pagePath": "/products",
110+
"sessions": "150",
111+
"activeUsers": "120"
112+
},
113+
{
114+
"date": "20260210",
115+
"pagePath": "/home",
116+
"sessions": "200",
117+
"activeUsers": "180"
118+
}
119+
],
120+
"metadata": {
121+
"currencyCode": "USD",
122+
"timeZone": "America/New_York"
123+
}
124+
}
125+
```
126+
75127
## Documentation
76128

77129
For complete setup instructions including Google Cloud configuration, Google Analytics permissions, advanced usage, and troubleshooting:
@@ -86,16 +138,46 @@ For complete setup instructions including Google Cloud configuration, Google Ana
86138
- **Troubleshooting** - OSGi issues, metric errors, variable name conflicts
87139
- **Available Metrics & Dimensions** - GA4 API schema reference
88140

89-
## Available Metrics
141+
## Understanding Dimensions and Metrics
142+
143+
When querying Google Analytics, you combine **dimensions** and **metrics** to get the data you need:
144+
145+
### Dimensions (What to group by)
90146

91-
Common GA4 metrics you can query:
92-
- `sessions` - Number of sessions
147+
Dimensions are categorical attributes that describe your data—they answer "what are we breaking this down by?"
148+
149+
Common dimensions:
150+
- `date` - When the activity happened (e.g., "20260209")
151+
- `pagePath` - Which page was viewed (e.g., "/products")
152+
- `country` - Where users are located (e.g., "United States")
153+
- `deviceCategory` - Device type (e.g., "desktop", "mobile", "tablet")
154+
- `browser` - Browser used (e.g., "Chrome", "Safari")
155+
- `city` - User's city (e.g., "New York")
156+
- `source` - Traffic source (e.g., "google", "facebook", "direct")
157+
158+
### Metrics (What to measure)
159+
160+
Metrics are the numerical measurements you want to analyze:
161+
- `sessions` - Number of sessions (visits)
93162
- `activeUsers` - Number of distinct users
94-
- `screenPageViews` - Total page and screen views
163+
- `screenPageViews` - Total page views
95164
- `bounceRate` - Percentage of single-page sessions
96165
- `averageSessionDuration` - Average session duration in seconds
97166

98-
See the [GA4 API Schema](https://developers.google.com/analytics/devguides/reporting/data/v1/api-schema) for the full list.
167+
### Example Query
168+
169+
"Show me sessions and active users, broken down by date and page path"
170+
171+
```json
172+
{
173+
"dimensions": ["date", "pagePath"],
174+
"metrics": ["sessions", "activeUsers"]
175+
}
176+
```
177+
178+
Each row in the response represents one unique combination of dimension values with its associated metrics.
179+
180+
See the [GA4 API Schema](https://developers.google.com/analytics/devguides/reporting/data/v1/api-schema) for the complete list of available dimensions and metrics.
99181

100182
## Version History
101183

src/main/java/com/dotcms/google/analytics/rest/GoogleAnalyticsResource.java

Lines changed: 29 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77
import com.dotcms.google.analytics.service.GoogleAnalyticsService;
88
import com.dotcms.rest.WebResource;
99
import com.dotmarketing.util.Logger;
10+
import com.google.analytics.data.v1beta.DimensionValue;
11+
import com.google.analytics.data.v1beta.MetricValue;
1012
import com.google.analytics.data.v1beta.RunReportResponse;
1113
import com.liferay.portal.model.User;
1214

@@ -149,26 +151,38 @@ public Response query(
149151
// Execute query
150152
final RunReportResponse gaResponse = analyticsService.query(analyticsRequest);
151153

154+
// Capture field names for flattened response
155+
final List<String> dimensionNames = queryRequest.getDimensions();
156+
final List<String> metricNames = queryRequest.getMetrics();
157+
152158
// Convert to JSON-friendly format
153159
final Map<String, Object> responseData = new HashMap<>();
154160
responseData.put("rowCount", gaResponse.getRowCount());
155161

156-
// Convert rows to simple structure
157-
final List<Map<String, Object>> rows = gaResponse.getRowsList().stream()
162+
// Add metadata with dimension and metric names
163+
responseData.put("dimensions", dimensionNames != null ? dimensionNames : new ArrayList<>());
164+
responseData.put("metrics", metricNames != null ? metricNames : new ArrayList<>());
165+
166+
// Convert rows to flattened structure with named fields
167+
final List<Map<String, String>> rows = gaResponse.getRowsList().stream()
158168
.map(row -> {
159-
final Map<String, Object> rowData = new HashMap<>();
160-
161-
// Extract dimensions
162-
final List<String> dimensions = row.getDimensionValuesList().stream()
163-
.map(dv -> dv.getValue())
164-
.collect(Collectors.toList());
165-
rowData.put("dimensions", dimensions);
166-
167-
// Extract metrics
168-
final List<String> metrics = row.getMetricValuesList().stream()
169-
.map(mv -> mv.getValue())
170-
.collect(Collectors.toList());
171-
rowData.put("metrics", metrics);
169+
final Map<String, String> rowData = new HashMap<>();
170+
171+
// Map dimension values to names
172+
final List<DimensionValue> dimensionValues = row.getDimensionValuesList();
173+
if (dimensionNames != null) {
174+
for (int i = 0; i < dimensionNames.size() && i < dimensionValues.size(); i++) {
175+
rowData.put(dimensionNames.get(i), dimensionValues.get(i).getValue());
176+
}
177+
}
178+
179+
// Map metric values to names
180+
final List<MetricValue> metricValues = row.getMetricValuesList();
181+
if (metricNames != null) {
182+
for (int i = 0; i < metricNames.size() && i < metricValues.size(); i++) {
183+
rowData.put(metricNames.get(i), metricValues.get(i).getValue());
184+
}
185+
}
172186

173187
return rowData;
174188
})

0 commit comments

Comments
 (0)