Skip to content

Commit a9ccb4d

Browse files
committed
feat: extract enhanced-measurement to top-level command group
1 parent a322768 commit a9ccb4d

File tree

8 files changed

+352
-279
lines changed

8 files changed

+352
-279
lines changed

README.md

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,8 @@ Manage GA4 accounts, properties, data streams, and run reports — all from your
3838
- **Event create & edit rules** — Manage server-side event creation and modification rules
3939
- **Firebase links** — List, create, and delete Firebase project links
4040
- **Google Ads links** — List, create, update, and delete Google Ads account links
41-
- **Property settings** — View and update attribution, Google Signals, and enhanced measurement settings
41+
- **Property settings** — View and update attribution and Google Signals settings
42+
- **Enhanced measurement** — View and update enhanced measurement settings per data stream
4243

4344
### Interactive usage
4445

@@ -196,7 +197,8 @@ export GA_CLI_SERVICE_ACCOUNT="/path/to/key.json"
196197
| `ga event-edit-rules` | `list`, `get`, `create`, `update`, `delete`, `reorder` | Manage event editing rules |
197198
| `ga firebase-links` | `list`, `create`, `delete` | Manage Firebase links |
198199
| `ga google-ads-links` | `list`, `create`, `update`, `delete` | Manage Google Ads links |
199-
| `ga property-settings` | `attribution`, `google-signals`, `enhanced-measurement` | View and update property settings |
200+
| `ga enhanced-measurement` | `get`, `update` | Manage enhanced measurement settings |
201+
| `ga property-settings` | `attribution`, `google-signals` | View and update property settings |
200202
| `ga reports` | `run`, `pivot`, `batch`, `funnel`, `check-compatibility`, `metadata`, `realtime`, `build` | Run and build reports |
201203
| `ga agent` | `guide` | AI agent quick reference |
202204
| `ga upgrade` | `--check`, `--force` | Check for and install updates |

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[project]
22
name = "google-analytics-cli"
3-
version = "0.1.3"
3+
version = "0.1.4"
44
description = "Command-line interface for Google Analytics 4"
55
readme = "README.md"
66
requires-python = ">=3.10"

src/ga_cli/commands/agent_cmd.py

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -231,9 +231,15 @@
231231
```bash
232232
ga property-settings attribution [-p PROPERTY_ID] [--attribution-model MODEL] [--acquisition-lookback VAL] [--other-lookback VAL] [--ads-export-scope VAL] [-o json]
233233
ga property-settings google-signals [-p PROPERTY_ID] [--state GOOGLE_SIGNALS_ENABLED|GOOGLE_SIGNALS_DISABLED] [-o json]
234-
ga property-settings enhanced-measurement [-p PROPERTY_ID] -s STREAM_ID [--scrolls/--no-scrolls] [--outbound-clicks/--no-outbound-clicks] [...] [-o json]
235234
```
236-
Get/set hybrid: no update flags = GET, any update flag = PATCH. Enhanced measurement requires `--stream-id`.
235+
Get/set hybrid: no update flags = GET, any update flag = PATCH.
236+
237+
### Enhanced Measurement Settings (alpha)
238+
```bash
239+
ga enhanced-measurement get -p PROPERTY_ID -s STREAM_ID [-o json]
240+
ga enhanced-measurement update -p PROPERTY_ID -s STREAM_ID [--scrolls/--no-scrolls] [--outbound-clicks/--no-outbound-clicks] [--site-search/--no-site-search] [--video-engagement/--no-video-engagement] [--file-downloads/--no-file-downloads] [--page-changes/--no-page-changes] [--form-interactions/--no-form-interactions] [--stream-enabled/--no-stream-enabled] [--search-query-parameter VAL] [--uri-query-parameter VAL] [--dry-run] [-o json]
241+
```
242+
Requires both `--property-id` and `--stream-id`. `get` retrieves current settings; `update` requires at least one toggle flag.
237243
238244
### Reports
239245
```bash
Lines changed: 157 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,157 @@
1+
"""Enhanced measurement settings commands for a data stream."""
2+
3+
from typing import Optional
4+
5+
import typer
6+
7+
from ..api.client import get_admin_alpha_client
8+
from ..config.store import get_effective_value
9+
from ..utils import (
10+
handle_dry_run,
11+
handle_error,
12+
output,
13+
require_options,
14+
resolve_output_format,
15+
success,
16+
)
17+
18+
enhanced_measurement_app = typer.Typer(
19+
name="enhanced-measurement",
20+
help="Manage enhanced measurement settings for a data stream",
21+
no_args_is_help=True,
22+
)
23+
24+
25+
def _resource_name(property_id: str, stream_id: str) -> str:
26+
return (
27+
f"properties/{property_id}/dataStreams/{stream_id}"
28+
"/enhancedMeasurementSettings"
29+
)
30+
31+
32+
@enhanced_measurement_app.command("get")
33+
def get_cmd(
34+
property_id: Optional[str] = typer.Option(
35+
None, "--property-id", "-p", help="Property ID (numeric)"
36+
),
37+
stream_id: str = typer.Option(
38+
..., "--stream-id", "-s", help="Data stream ID"
39+
),
40+
output_format: Optional[str] = typer.Option(
41+
None, "--output", "-o", help="Output format (json, table, compact)"
42+
),
43+
):
44+
"""Get enhanced measurement settings for a data stream."""
45+
try:
46+
effective_property = get_effective_value(property_id, "default_property_id")
47+
require_options({"property_id": effective_property}, ["property_id"])
48+
effective_format = resolve_output_format(output_format)
49+
50+
admin = get_admin_alpha_client()
51+
settings = (
52+
admin.properties()
53+
.dataStreams()
54+
.getEnhancedMeasurementSettings(
55+
name=_resource_name(effective_property, stream_id)
56+
)
57+
.execute()
58+
)
59+
output(settings, effective_format)
60+
except Exception as e:
61+
handle_error(e)
62+
63+
64+
@enhanced_measurement_app.command("update")
65+
def update_cmd(
66+
property_id: Optional[str] = typer.Option(
67+
None, "--property-id", "-p", help="Property ID (numeric)"
68+
),
69+
stream_id: str = typer.Option(
70+
..., "--stream-id", "-s", help="Data stream ID"
71+
),
72+
stream_enabled: Optional[bool] = typer.Option(
73+
None,
74+
"--stream-enabled/--no-stream-enabled",
75+
help="Enable/disable enhanced measurement for this stream",
76+
),
77+
scrolls: Optional[bool] = typer.Option(
78+
None, "--scrolls/--no-scrolls", help="Capture scroll events"
79+
),
80+
outbound_clicks: Optional[bool] = typer.Option(
81+
None, "--outbound-clicks/--no-outbound-clicks", help="Capture outbound click events"
82+
),
83+
site_search: Optional[bool] = typer.Option(
84+
None, "--site-search/--no-site-search", help="Capture site search events"
85+
),
86+
video_engagement: Optional[bool] = typer.Option(
87+
None, "--video-engagement/--no-video-engagement", help="Capture video engagement events"
88+
),
89+
file_downloads: Optional[bool] = typer.Option(
90+
None, "--file-downloads/--no-file-downloads", help="Capture file download events"
91+
),
92+
page_changes: Optional[bool] = typer.Option(
93+
None, "--page-changes/--no-page-changes", help="Capture page change (history) events"
94+
),
95+
form_interactions: Optional[bool] = typer.Option(
96+
None, "--form-interactions/--no-form-interactions", help="Capture form interaction events"
97+
),
98+
search_query_parameter: Optional[str] = typer.Option(
99+
None, "--search-query-parameter", help="URL query parameters for site search"
100+
),
101+
uri_query_parameter: Optional[str] = typer.Option(
102+
None, "--uri-query-parameter", help="Additional URL query parameters"
103+
),
104+
dry_run: bool = typer.Option(
105+
False, "--dry-run", help="Preview the request without executing"
106+
),
107+
output_format: Optional[str] = typer.Option(
108+
None, "--output", "-o", help="Output format (json, table, compact)"
109+
),
110+
):
111+
"""Update enhanced measurement settings for a data stream."""
112+
try:
113+
effective_property = get_effective_value(property_id, "default_property_id")
114+
require_options({"property_id": effective_property}, ["property_id"])
115+
effective_format = resolve_output_format(output_format)
116+
117+
field_map = {
118+
"streamEnabled": stream_enabled,
119+
"scrollsEnabled": scrolls,
120+
"outboundClicksEnabled": outbound_clicks,
121+
"siteSearchEnabled": site_search,
122+
"videoEngagementEnabled": video_engagement,
123+
"fileDownloadsEnabled": file_downloads,
124+
"pageChangesEnabled": page_changes,
125+
"formInteractionsEnabled": form_interactions,
126+
"searchQueryParameter": search_query_parameter,
127+
"uriQueryParameter": uri_query_parameter,
128+
}
129+
body = {k: v for k, v in field_map.items() if v is not None}
130+
131+
if not body:
132+
raise typer.BadParameter(
133+
"At least one update flag must be specified (e.g. --scrolls, "
134+
"--stream-enabled, --search-query-parameter)."
135+
)
136+
137+
update_mask = ",".join(body.keys())
138+
resource = _resource_name(effective_property, stream_id)
139+
140+
if dry_run:
141+
handle_dry_run("update", "PATCH", resource, body, update_mask=update_mask)
142+
143+
admin = get_admin_alpha_client()
144+
settings = (
145+
admin.properties()
146+
.dataStreams()
147+
.updateEnhancedMeasurementSettings(
148+
name=resource, updateMask=update_mask, body=body
149+
)
150+
.execute()
151+
)
152+
success("Enhanced measurement settings updated.")
153+
output(settings, effective_format)
154+
except (typer.BadParameter, typer.Exit):
155+
raise
156+
except Exception as e:
157+
handle_error(e)

src/ga_cli/commands/property_settings.py

Lines changed: 1 addition & 103 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
"""Property settings commands (attribution, Google Signals, enhanced measurement)."""
1+
"""Property settings commands (attribution, Google Signals)."""
22

33
from typing import Optional
44

@@ -183,105 +183,3 @@ def google_signals_cmd(
183183
raise
184184
except Exception as e:
185185
handle_error(e)
186-
187-
188-
# --- Enhanced Measurement Settings ---
189-
190-
191-
@property_settings_app.command("enhanced-measurement")
192-
def enhanced_measurement_cmd(
193-
property_id: Optional[str] = typer.Option(
194-
None, "--property-id", "-p", help="Property ID (numeric)"
195-
),
196-
stream_id: str = typer.Option(
197-
..., "--stream-id", "-s", help="Data stream ID"
198-
),
199-
stream_enabled: Optional[bool] = typer.Option(
200-
None,
201-
"--stream-enabled/--no-stream-enabled",
202-
help="Enable/disable enhanced measurement for this stream",
203-
),
204-
scrolls: Optional[bool] = typer.Option(
205-
None, "--scrolls/--no-scrolls", help="Capture scroll events"
206-
),
207-
outbound_clicks: Optional[bool] = typer.Option(
208-
None, "--outbound-clicks/--no-outbound-clicks", help="Capture outbound click events"
209-
),
210-
site_search: Optional[bool] = typer.Option(
211-
None, "--site-search/--no-site-search", help="Capture site search events"
212-
),
213-
video_engagement: Optional[bool] = typer.Option(
214-
None, "--video-engagement/--no-video-engagement", help="Capture video engagement events"
215-
),
216-
file_downloads: Optional[bool] = typer.Option(
217-
None, "--file-downloads/--no-file-downloads", help="Capture file download events"
218-
),
219-
page_changes: Optional[bool] = typer.Option(
220-
None, "--page-changes/--no-page-changes", help="Capture page change (history) events"
221-
),
222-
form_interactions: Optional[bool] = typer.Option(
223-
None, "--form-interactions/--no-form-interactions", help="Capture form interaction events"
224-
),
225-
search_query_parameter: Optional[str] = typer.Option(
226-
None, "--search-query-parameter", help="URL query parameters for site search"
227-
),
228-
uri_query_parameter: Optional[str] = typer.Option(
229-
None, "--uri-query-parameter", help="Additional URL query parameters"
230-
),
231-
output_format: Optional[str] = typer.Option(
232-
None, "--output", "-o", help="Output format (json, table, compact)"
233-
),
234-
):
235-
"""Get or update enhanced measurement settings.
236-
237-
With no update flags, displays current settings.
238-
"""
239-
try:
240-
effective_property = get_effective_value(property_id, "default_property_id")
241-
require_options({"property_id": effective_property}, ["property_id"])
242-
effective_format = resolve_output_format(output_format)
243-
244-
resource_name = (
245-
f"properties/{effective_property}/dataStreams/{stream_id}"
246-
"/enhancedMeasurementSettings"
247-
)
248-
admin = get_admin_alpha_client()
249-
250-
field_map = {
251-
"streamEnabled": stream_enabled,
252-
"scrollsEnabled": scrolls,
253-
"outboundClicksEnabled": outbound_clicks,
254-
"siteSearchEnabled": site_search,
255-
"videoEngagementEnabled": video_engagement,
256-
"fileDownloadsEnabled": file_downloads,
257-
"pageChangesEnabled": page_changes,
258-
"formInteractionsEnabled": form_interactions,
259-
"searchQueryParameter": search_query_parameter,
260-
"uriQueryParameter": uri_query_parameter,
261-
}
262-
body = {k: v for k, v in field_map.items() if v is not None}
263-
264-
if body:
265-
update_mask = ",".join(body.keys())
266-
settings = (
267-
admin.properties()
268-
.dataStreams()
269-
.updateEnhancedMeasurementSettings(
270-
name=resource_name, updateMask=update_mask, body=body
271-
)
272-
.execute()
273-
)
274-
success("Enhanced measurement settings updated.")
275-
else:
276-
settings = (
277-
admin.properties()
278-
.dataStreams()
279-
.getEnhancedMeasurementSettings(name=resource_name)
280-
.execute()
281-
)
282-
283-
output(settings, effective_format)
284-
except typer.BadParameter:
285-
raise
286-
except Exception as e:
287-
handle_error(e)

src/ga_cli/main.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
from .commands.custom_metrics import custom_metrics_app
2020
from .commands.data_retention import data_retention_app
2121
from .commands.data_streams import data_streams_app
22+
from .commands.enhanced_measurement import enhanced_measurement_app
2223
from .commands.event_create_rules import event_create_rules_app
2324
from .commands.event_edit_rules import event_edit_rules_app
2425
from .commands.firebase_links import firebase_links_app
@@ -53,6 +54,7 @@
5354
app.add_typer(bigquery_links_app, name="bigquery-links")
5455
app.add_typer(calculated_metrics_app, name="calculated-metrics")
5556
app.add_typer(channel_groups_app, name="channel-groups")
57+
app.add_typer(enhanced_measurement_app, name="enhanced-measurement")
5658
app.add_typer(event_create_rules_app, name="event-create-rules")
5759
app.add_typer(event_edit_rules_app, name="event-edit-rules")
5860
app.add_typer(firebase_links_app, name="firebase-links")

0 commit comments

Comments
 (0)