analytics_ops 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (52) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +47 -0
  3. data/CODE_OF_CONDUCT.md +10 -0
  4. data/CONTRIBUTING.md +67 -0
  5. data/LICENSE.txt +21 -0
  6. data/README.md +188 -0
  7. data/SECURITY.md +56 -0
  8. data/docs/api-support-matrix.md +50 -0
  9. data/docs/architecture.md +89 -0
  10. data/docs/authentication.md +86 -0
  11. data/docs/commands.md +112 -0
  12. data/docs/configuration-schema-v1.json +129 -0
  13. data/docs/configuration.md +167 -0
  14. data/docs/google-client-compatibility.md +59 -0
  15. data/docs/plan-format.md +89 -0
  16. data/docs/plan-schema-v1.json +224 -0
  17. data/docs/rails.md +73 -0
  18. data/docs/reports.md +124 -0
  19. data/docs/safety.md +93 -0
  20. data/docs/troubleshooting.md +103 -0
  21. data/exe/analytics-ops +6 -0
  22. data/lib/analytics_ops/applier.rb +52 -0
  23. data/lib/analytics_ops/canonical.rb +65 -0
  24. data/lib/analytics_ops/cli.rb +427 -0
  25. data/lib/analytics_ops/clients/admin.rb +446 -0
  26. data/lib/analytics_ops/clients/data.rb +314 -0
  27. data/lib/analytics_ops/configuration/document.rb +25 -0
  28. data/lib/analytics_ops/configuration/loader.rb +76 -0
  29. data/lib/analytics_ops/configuration/schema.rb +107 -0
  30. data/lib/analytics_ops/configuration/validator.rb +344 -0
  31. data/lib/analytics_ops/configuration.rb +19 -0
  32. data/lib/analytics_ops/desired_state.rb +40 -0
  33. data/lib/analytics_ops/errors.rb +31 -0
  34. data/lib/analytics_ops/plan.rb +551 -0
  35. data/lib/analytics_ops/planner.rb +233 -0
  36. data/lib/analytics_ops/rails/railtie.rb +65 -0
  37. data/lib/analytics_ops/rails.rb +5 -0
  38. data/lib/analytics_ops/redaction.rb +46 -0
  39. data/lib/analytics_ops/reports/catalog.rb +100 -0
  40. data/lib/analytics_ops/reports/definition.rb +226 -0
  41. data/lib/analytics_ops/reports/result.rb +90 -0
  42. data/lib/analytics_ops/reports.rb +13 -0
  43. data/lib/analytics_ops/resources.rb +79 -0
  44. data/lib/analytics_ops/snapshot.rb +45 -0
  45. data/lib/analytics_ops/version.rb +5 -0
  46. data/lib/analytics_ops/workspace.rb +212 -0
  47. data/lib/analytics_ops.rb +21 -0
  48. data/lib/generators/analytics_ops/install_generator.rb +23 -0
  49. data/lib/generators/analytics_ops/templates/analytics-ops +9 -0
  50. data/lib/generators/analytics_ops/templates/analytics_ops.yml +23 -0
  51. data/sig/analytics_ops.rbs +395 -0
  52. metadata +131 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 3bf17db3adc805f77cf7d22e2ea2840a3eaf44a007be8036a48bf58a4d2d3a36
4
+ data.tar.gz: 1642e17fb59a976a471636a92f8da2f1290c3e13eb7bee515722efd8315a2e4a
5
+ SHA512:
6
+ metadata.gz: c934c5e8856505c2ec5d8e4311ac9f9e3d23ba47078fc478170dcda4d08e5d5be990402fa2795d56138ba30d2c8e92dbe368fdb1fb3da254bead9ba429338f09
7
+ data.tar.gz: b96a8112f501416f763d3528419435e2364526f66cd43266ec3880825f61c964550eca739ece89607d7d89a583051b44d35d1f3f62a018260dab625d13a871ef
data/CHANGELOG.md ADDED
@@ -0,0 +1,47 @@
1
+ # Changelog
2
+
3
+ Analytics Ops follows Semantic Versioning for its public Ruby, CLI,
4
+ configuration, and plan contracts.
5
+
6
+ ## [0.1.0] - 2026-07-22
7
+
8
+ ### Added
9
+
10
+ - Strict version-1 YAML configuration with safe Psych parsing, no ERB or
11
+ aliases, a 1 MiB limit, allowlisted environment interpolation, fail-closed
12
+ keys, quoted numeric identifiers, and committed JSON Schema.
13
+ - Immutable desired state, normalized Admin resources, canonical JSON,
14
+ snapshot fingerprints, and deterministic planning.
15
+ - Version-1 saved plans with strict scalar/payload/identity validation,
16
+ duplicate-key and secret-field rejection, cross-property protection,
17
+ atomic mode-0600 writes, and committed JSON Schema.
18
+ - Create/update-only planning for web stream default URI, retention, key
19
+ events, custom dimensions, and custom metrics. Unmanaged resources are never
20
+ deleted or archived.
21
+ - Explicit guarded apply with saved-plan-only execution, fresh snapshot
22
+ validation, stale-plan rejection, stop-on-first-failure behavior, and
23
+ structured partial reconciliation.
24
+ - Lazy official Google Admin and Data adapters, typed/redacted errors,
25
+ deterministic protobuf contract tests, and no generated types in public
26
+ results.
27
+ - `doctor`, `discover`, `snapshot`, `audit`, `plan`, `apply`,
28
+ `verify`, `report`, `realtime`, and `schema` CLI commands.
29
+ - Human, automation-safe JSON, and report-only CSV output with stable exit
30
+ statuses.
31
+ - Immutable standard/realtime report definitions, normalized result metadata
32
+ and quota information, and seven built-in commercial recipes.
33
+ - Optional Rails 7.2–8.1 Railtie, install generator, binstub, and read-only
34
+ operator Rake tasks.
35
+ - Public RBS signatures, security/redaction checks, Ruby 3.2–4.0 CI, Rails
36
+ compatibility jobs, package inspection, dependency audit, and OIDC Trusted
37
+ Publishing workflow.
38
+ - Complete operator, safety, report, Rails, architecture, compatibility, and
39
+ troubleshooting documentation.
40
+
41
+ ### Security
42
+
43
+ - Requiring the gem, loading configuration, and booting Rails perform no
44
+ network I/O.
45
+ - Credentials and report rows are excluded from configuration and plan
46
+ contracts; common credential patterns are redacted from errors.
47
+ - Ordinary plans cannot delete or archive resources.
@@ -0,0 +1,10 @@
1
+ # Code of Conduct
2
+
3
+ "analytics_ops" follows [The Ruby Community Conduct Guideline](https://www.ruby-lang.org/en/conduct) in all "collaborative space", which is defined as community communications channels (such as mailing lists, submitted patches, commit comments, etc.):
4
+
5
+ * Participants will be tolerant of opposing views.
6
+ * Participants must ensure that their language and actions are free of personal attacks and disparaging personal remarks.
7
+ * When interpreting the words and actions of others, participants should always assume good intentions.
8
+ * Behaviour which can be reasonably considered harassment will not be tolerated.
9
+
10
+ If you have any concerns about behaviour within this project, please contact us at ["nelsonchavespro@gmail.com"](mailto:"nelsonchavespro@gmail.com").
data/CONTRIBUTING.md ADDED
@@ -0,0 +1,67 @@
1
+ # Contributing
2
+
3
+ Thank you for helping make Analytics Ops safer and easier to operate.
4
+
5
+ ## Setup
6
+
7
+ ```bash
8
+ git clone https://github.com/nelsonchaves/analytics_ops.git
9
+ cd analytics_ops
10
+ bin/setup
11
+ bin/check
12
+ ```
13
+
14
+ Useful focused commands:
15
+
16
+ ```bash
17
+ bundle exec rspec spec/analytics_ops/clients/data_spec.rb
18
+ bundle exec rubocop
19
+ bundle exec rbs -I sig validate
20
+ bundle exec rake build
21
+ ```
22
+
23
+ Use `RSPEC_STATUS_FILE=/tmp/analytics_ops-rspec-status` when the repository is
24
+ mounted read-only except for source edits.
25
+
26
+ ## Design rules
27
+
28
+ - Keep the core plain Ruby and independent of Rails and Active Support.
29
+ - Use Google's official Ruby clients; do not add another HTTP or OAuth stack.
30
+ - Keep generated Google objects behind adapters.
31
+ - Do not introduce telemetry, a database, browser analytics, or boot-time
32
+ network access.
33
+ - Preserve the read-only default and saved-plan confirmation boundary.
34
+ - Do not add ordinary delete/archive operations.
35
+ - Use immutable gem-owned public values and typed errors.
36
+
37
+ ## Tests
38
+
39
+ Use RSpec. Run focused specs while changing a subsystem, then run the complete
40
+ suite before the milestone commit. Ordinary tests must use injected fakes and
41
+ must never contact a real property.
42
+
43
+ An opt-in live check may use only a dedicated disposable GA4 property. It must
44
+ use uniquely prefixed resources and clean up only resources it created. Never
45
+ run a live test against production.
46
+
47
+ Changes to a Google adapter need request/response coercion tests against the
48
+ supported official generated classes. Changes to planning need deterministic
49
+ output, stale-state, cross-property, and idempotency coverage.
50
+
51
+ ## Pull requests
52
+
53
+ Keep commits focused and explain:
54
+
55
+ - the user-visible problem and public contract
56
+ - read/write behavior and rollback implications
57
+ - tests and compatibility checks performed
58
+ - documentation or schema changes
59
+
60
+ Update `CHANGELOG.md` and the relevant support matrix when behavior changes.
61
+ Configuration and plan incompatibilities require an explicit schema version;
62
+ they never migrate silently.
63
+
64
+ Never commit credentials, real property exports, visitor data, production
65
+ plans, or production identifiers. Follow [SECURITY.md](SECURITY.md) for private
66
+ vulnerability reports and [CODE_OF_CONDUCT.md](CODE_OF_CONDUCT.md) for project
67
+ conduct.
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2026 Nelson Chaves
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,188 @@
1
+ # Analytics Ops
2
+
3
+ **Google Analytics 4 configuration as code and reporting for Ruby and Rails.**
4
+
5
+ Analytics Ops gives you a review-first way to inspect GA4, detect drift, run
6
+ useful reports, and apply a small set of safe configuration changes. The core
7
+ is plain Ruby; Rails support is optional.
8
+
9
+ ## Five-minute read-only start
10
+
11
+ You need Ruby 3.2 or newer, a GA4 property you can read, and the
12
+ [Google Cloud CLI](https://cloud.google.com/sdk/docs/install).
13
+
14
+ 1. Add Analytics Ops to your bundle:
15
+
16
+ ```ruby
17
+ # Gemfile
18
+ gem "analytics_ops", "~> 0.1", group: :development
19
+ ```
20
+
21
+ ```bash
22
+ bundle install
23
+ ```
24
+
25
+ 2. Enable the Google Analytics Admin API and Data API in your own Google Cloud
26
+ project, then create local Application Default Credentials:
27
+
28
+ ```bash
29
+ gcloud auth application-default login \
30
+ --scopes="https://www.googleapis.com/auth/cloud-platform,https://www.googleapis.com/auth/analytics.readonly"
31
+ ```
32
+
33
+ 3. Create `config/analytics_ops.yml`:
34
+
35
+ ```yaml
36
+ version: 1
37
+
38
+ profiles:
39
+ production:
40
+ property_id: "123456789"
41
+ ```
42
+
43
+ 4. Check access without changing anything:
44
+
45
+ ```bash
46
+ bundle exec analytics-ops doctor
47
+ bundle exec analytics-ops discover
48
+ bundle exec analytics-ops audit
49
+ bundle exec analytics-ops report traffic_acquisition
50
+ ```
51
+
52
+ Every command above is read-only. `audit` exits with status 2 when it finds
53
+ drift, which makes it useful in CI.
54
+
55
+ ## The safe change workflow
56
+
57
+ ```text
58
+ configuration → audit → saved plan → review → apply → verify
59
+ ```
60
+
61
+ ```bash
62
+ # Read-only: save a deterministic plan with mode 0600
63
+ bundle exec analytics-ops plan --output tmp/ga4-plan.json
64
+
65
+ # Review the JSON before doing anything
66
+ less tmp/ga4-plan.json
67
+
68
+ # Mutating: prints the exact saved operations and asks you to type yes
69
+ bundle exec analytics-ops apply tmp/ga4-plan.json
70
+
71
+ # Read-only: confirm managed settings now match
72
+ bundle exec analytics-ops verify
73
+ ```
74
+
75
+ `apply` refreshes the remote snapshot, rejects stale plans, and executes only
76
+ the operations in the saved file. Ordinary plans never delete or archive
77
+ anything. If one operation fails, execution stops and reports what succeeded,
78
+ what failed, and what remains.
79
+
80
+ ## Common commands
81
+
82
+ | Command | Purpose | Remote writes? |
83
+ | --- | --- | --- |
84
+ | `analytics-ops doctor` | Check configuration, credentials, APIs, property access, clients, and clock | No |
85
+ | `analytics-ops discover` | List accessible accounts, properties, and streams | No |
86
+ | `analytics-ops snapshot` | Print normalized managed remote state | No |
87
+ | `analytics-ops audit` | Show drift without writing a plan file | No |
88
+ | `analytics-ops plan --output FILE` | Review and save deterministic changes | No |
89
+ | `analytics-ops apply FILE` | Apply one reviewed, non-stale plan | **Yes** |
90
+ | `analytics-ops verify` | Check convergence | No |
91
+ | `analytics-ops report NAME` | Run a built-in standard report | No |
92
+ | `analytics-ops realtime` | Run the realtime-events report | No |
93
+ | `analytics-ops schema --format json` | Print the configuration schema | No |
94
+
95
+ Use `--format json` for automation. CSV is available only for reports:
96
+
97
+ ```bash
98
+ bundle exec analytics-ops report landing_pages --format csv
99
+ ```
100
+
101
+ See [Commands](docs/commands.md) for every option and exit status.
102
+
103
+ ## IDs: the quick distinction
104
+
105
+ Use fake values like these in examples and tests:
106
+
107
+ | Value | Example | Where it belongs |
108
+ | --- | --- | --- |
109
+ | Account ID | `100000001` | Discovery output only |
110
+ | Property ID | `123456789` | `property_id` in configuration and API requests |
111
+ | Stream ID | `987654321` | `stream_id` in configuration |
112
+ | Measurement ID | `G-EXAMPLE1` | Browser tagging; never use it as a stream ID |
113
+ | OAuth client | A Cloud project client ID/secret | Owned by your application, never this YAML |
114
+ | Service-account identity | `ga-reader@example-project.iam.gserviceaccount.com` | Granted GA access; its key is never this YAML |
115
+
116
+ ## Ruby API
117
+
118
+ ```ruby
119
+ workspace = AnalyticsOps::Workspace.load(
120
+ "config/analytics_ops.yml",
121
+ profile: "production"
122
+ )
123
+
124
+ plan = workspace.plan
125
+ plan.write("tmp/ga4-plan.json")
126
+
127
+ report = workspace.report("calculator_completions")
128
+ report.rows.each { |row| puts row.fetch("eventCount") }
129
+ ```
130
+
131
+ Loading the gem, loading YAML, and booting Rails do not contact Google.
132
+ Network calls happen only when a workspace operation is invoked.
133
+
134
+ ## Rails
135
+
136
+ ```ruby
137
+ # Gemfile
138
+ gem "analytics_ops", require: "analytics_ops/rails", group: :development
139
+ ```
140
+
141
+ ```bash
142
+ bin/rails generate analytics_ops:install
143
+ bin/rake analytics:doctor
144
+ bin/rake 'analytics:report[traffic_acquisition]'
145
+ ```
146
+
147
+ The integration is a Railtie, not an Engine. It adds a generator and operator
148
+ Rake tasks—no models, migrations, routes, controllers, views, JavaScript, or
149
+ boot-time network calls. See [Rails integration](docs/rails.md).
150
+
151
+ ## What Analytics Ops does not do
152
+
153
+ - It does not inject browser analytics or manage consent banners.
154
+ - It does not store credentials, tokens, report rows, or local state.
155
+ - It does not delete accounts, properties, streams, or unmanaged resources.
156
+ - It does not claim to manage settings that Google exposes only in the UI.
157
+ - Experimental declarations are findings only in version 0.1.0; they are not
158
+ silently applied through Alpha APIs.
159
+
160
+ ## Documentation
161
+
162
+ | Guide | Topic |
163
+ | --- | --- |
164
+ | [Authentication](docs/authentication.md) | ADC, scopes, service accounts, and safe automation |
165
+ | [Configuration](docs/configuration.md) | Complete strict YAML contract |
166
+ | [Commands](docs/commands.md) | CLI syntax, formats, and exit statuses |
167
+ | [Reports](docs/reports.md) | Built-in recipes and GA reporting limitations |
168
+ | [Rails](docs/rails.md) | Generator and Rake tasks |
169
+ | [Safety](docs/safety.md) | Plans, stale-state protection, rollback, and credentials |
170
+ | [Plan format](docs/plan-format.md) | Version-1 JSON contract |
171
+ | [API support](docs/api-support-matrix.md) | Exactly what is managed, manual, or unsupported |
172
+ | [Client compatibility](docs/google-client-compatibility.md) | Tested official Google gem versions |
173
+ | [Troubleshooting](docs/troubleshooting.md) | Common failures and fixes |
174
+ | [Architecture](docs/architecture.md) | Boundaries and data flow |
175
+
176
+ For development, run:
177
+
178
+ ```bash
179
+ bin/setup
180
+ bin/check
181
+ ```
182
+
183
+ Security issues belong in private vulnerability reporting; see
184
+ [SECURITY.md](SECURITY.md). Contributions follow [CONTRIBUTING.md](CONTRIBUTING.md).
185
+
186
+ Analytics Ops is MIT licensed and is not affiliated with, sponsored by, or
187
+ endorsed by Google LLC. Google Analytics is a trademark of Google LLC and is
188
+ named only to describe API compatibility.
data/SECURITY.md ADDED
@@ -0,0 +1,56 @@
1
+ # Security policy
2
+
3
+ ## Supported versions
4
+
5
+ Analytics Ops is pre-release. Security fixes currently target the latest
6
+ commit on `master`. A published support table will replace this section before
7
+ 1.0.
8
+
9
+ ## Report privately
10
+
11
+ Do not open a public issue for a vulnerability or exposed credential. Use
12
+ GitHub's private vulnerability reporting for this repository.
13
+
14
+ Include:
15
+
16
+ - affected version or commit
17
+ - smallest safe reproduction
18
+ - expected and observed behavior
19
+ - potential impact
20
+ - whether a disposable or production property was involved
21
+
22
+ Do not include credentials, access or refresh tokens, authorization headers,
23
+ private keys, visitor data, report exports, or production plan files.
24
+
25
+ If credentials may be exposed, revoke or rotate them with Google immediately;
26
+ do not wait for a project response.
27
+
28
+ ## Security guarantees
29
+
30
+ Analytics Ops:
31
+
32
+ - uses Application Default Credentials or an explicitly injected Google
33
+ credential object
34
+ - refuses credential-shaped configuration and plan fields
35
+ - redacts common authorization material from translated errors
36
+ - performs no network I/O while requiring the gem, loading YAML, or booting
37
+ Rails
38
+ - defaults to read-only commands
39
+ - requires a strictly validated saved plan and explicit confirmation to mutate
40
+ - rejects stale and cross-property plans
41
+ - excludes delete/archive operations from ordinary plans
42
+ - never logs report results automatically
43
+ - has no telemetry, credential store, database, or browser injection
44
+ - publishes through RubyGems Trusted Publishing without a stored API key
45
+
46
+ Plan files still describe operational configuration and are created with mode
47
+ 0600. Report output may contain sensitive aggregate data and should be handled
48
+ under the operator's data policy.
49
+
50
+ ## Operator responsibilities
51
+
52
+ Analytics Ops cannot secure an overprivileged Cloud project, service account,
53
+ GitHub organization, CI runner, GA property, or exported report. Use
54
+ least-privilege read credentials for routine work, isolate mutation
55
+ credentials, protect release environments, and keep production mutation
56
+ credentials out of Rails web containers.
@@ -0,0 +1,50 @@
1
+ # API support matrix
2
+
3
+ This matrix describes Analytics Ops 0.1.0—not every method Google exposes.
4
+
5
+ | State | Meaning |
6
+ | --- | --- |
7
+ | Managed | Read and safely create/update within the stated limit |
8
+ | Audited | Read-only |
9
+ | Experimental | Explicitly declared, but Alpha behavior is isolated and may change |
10
+ | Manual | Reported as an operator checklist |
11
+ | Unsupported | Deliberately outside the public contract |
12
+
13
+ | Capability | State | Exact 0.1.0 behavior |
14
+ | --- | --- | --- |
15
+ | Account/property discovery | Audited | Lists accessible summaries |
16
+ | Data-stream discovery | Audited | Lists web, Android, and iOS streams |
17
+ | Web stream default URI | Managed | Updates an existing web stream |
18
+ | Stream create/delete | Unsupported | Missing streams become findings |
19
+ | Data retention | Managed | Reads and updates supported periods/reset behavior |
20
+ | Key events | Managed | Reads and creates missing events; no delete |
21
+ | Custom dimensions | Managed | Reads, creates, and updates display name/description; user ads flag where supported |
22
+ | Custom metrics | Managed | Reads, creates, and updates display name/description |
23
+ | Standard Data API reports | Audited | Immutable definitions and normalized results |
24
+ | Realtime Data API reports | Audited | Immutable definitions and normalized results |
25
+ | Batch reports | Unsupported | Use individual definitions |
26
+ | Enhanced Measurement | Experimental | Validated declaration and finding only; no mutation |
27
+ | Google Signals | Experimental | Validated declaration and finding only; no mutation |
28
+ | Consent coverage | Manual | Must be verified in tagging/consent systems |
29
+ | Ads-personalization regions | Manual | Not inferred from similarly named API fields |
30
+ | Stream data redaction | Manual | UI/API policy must be verified separately |
31
+ | User-provided data setting | Unsupported | Not read or changed |
32
+ | Event create/edit rules | Unsupported | Alpha, outside 0.1.0 |
33
+ | Audiences and expanded datasets | Unsupported | Alpha, outside 0.1.0 |
34
+ | Google Ads, AdSense, DV360, SA360 links | Unsupported | Not read or changed |
35
+ | BigQuery links | Unsupported | Not read or changed |
36
+ | Access bindings | Unsupported | Not read or changed |
37
+ | Measurement Protocol secrets/events | Unsupported | Never managed |
38
+ | Account/property/stream deletion | Unsupported | No destructive path |
39
+
40
+ Google marks some Admin API capabilities as Alpha and warns that Alpha
41
+ contracts may break. Analytics Ops never labels a capability managed merely
42
+ because a generated class exists. The supported operations above have
43
+ executable protobuf-coercion contract specs against the pinned official Ruby
44
+ clients.
45
+
46
+ References:
47
+
48
+ - [Google Analytics Admin API overview](https://developers.google.com/analytics/devguides/config/admin/v1)
49
+ - [Google Analytics Data API](https://developers.google.com/analytics/devguides/reporting/data/v1)
50
+ - [Google Analytics Admin API changelog](https://developers.google.com/analytics/devguides/config/admin/v1/changelog)
@@ -0,0 +1,89 @@
1
+ # Architecture
2
+
3
+ Analytics Ops is a plain-Ruby library and CLI with optional Rails hooks. It has
4
+ no database and keeps Google's generated classes behind two narrow adapters.
5
+
6
+ ```text
7
+ strict YAML
8
+ → immutable desired state
9
+ → Admin API snapshot
10
+ → deterministic planner
11
+ → versioned saved plan
12
+ → explicit guarded apply
13
+ → convergence verification
14
+
15
+ immutable report definition
16
+ → Data API adapter
17
+ → immutable normalized result
18
+ ```
19
+
20
+ ## Core objects
21
+
22
+ | Object | Responsibility |
23
+ | --- | --- |
24
+ | `Configuration` | Bounded safe YAML, environment interpolation, schema validation |
25
+ | `DesiredState` | Immutable configuration for one profile |
26
+ | `Resources` | Gem-owned account/property/stream/settings values |
27
+ | `Snapshot` | Canonical managed remote state and SHA-256 fingerprint |
28
+ | `Planner` | Pure desired-versus-snapshot comparison; no client access |
29
+ | `Plan` | Strict deterministic JSON mutation contract |
30
+ | `Applier` | Confirmation, fresh-snapshot check, sequential saved operations |
31
+ | `Clients::Admin` | Official Admin request translation and response normalization |
32
+ | `Reports::Definition` | Strict immutable Data API query |
33
+ | `Clients::Data` | Standard/realtime request translation and result normalization |
34
+ | `Workspace` | Public orchestration API with independently injectable clients |
35
+ | `CLI` | Option validation, formats, stable statuses, and explicit confirmation |
36
+ | `Railtie` | Optional generator and operator Rake tasks |
37
+
38
+ ## Dependency direction
39
+
40
+ Domain and planning code know nothing about protobufs, gRPC, Rails, Active
41
+ Support, OAuth flows, or HTTP transports. Admin and Data adapters translate
42
+ official generated values immediately into immutable Analytics Ops values.
43
+ The adapters can be injected independently, so tests use deterministic fakes.
44
+
45
+ The core requires Google's wrapper gems lazily. Requiring `analytics_ops`
46
+ defines classes only; it does not instantiate a client or discover ADC.
47
+
48
+ ## State model
49
+
50
+ Google Analytics is the remote source of truth. There is no local state
51
+ database. The configuration declares desired state and a snapshot captures the
52
+ relevant remote state. Canonical key ordering and normalized arrays make
53
+ snapshot fingerprints and plan bytes deterministic.
54
+
55
+ Apply accepts an already validated plan, refreshes the snapshot, compares its
56
+ fingerprint, then sends only the saved operations. A partial response is
57
+ explicit reconciliation data, not a hidden retry or rollback.
58
+
59
+ ## Error boundary
60
+
61
+ Expected failures become typed Analytics Ops errors:
62
+
63
+ - configuration and invalid plan
64
+ - authentication and authorization
65
+ - unsupported capability and identity conflict
66
+ - stale plan and confirmation required
67
+ - quota, timeout, invalid request, and remote failure
68
+ - partial apply with a structured result
69
+
70
+ Messages are redacted before user-visible output. Callers do not need to parse
71
+ Google exception text.
72
+
73
+ An injected logger receives small JSON request events containing only the
74
+ service method and target resource. Request bodies, authorization data, and
75
+ report results are never logged automatically. CLI logging defaults to
76
+ `warn`.
77
+
78
+ ## Rails boundary
79
+
80
+ `require "analytics_ops/rails"` adds a Railtie only. It does not add an
81
+ Engine, model, migration, route, controller, view, asset, or boot-time network
82
+ call. Browser analytics and consent remain application concerns.
83
+
84
+ ## Experimental boundary
85
+
86
+ Google identifies certain Admin capabilities as Alpha. Version 0.1.0 accepts
87
+ explicit Enhanced Measurement and Google Signals declarations only so they can
88
+ appear as experimental findings. It does not apply them. Stable/beta
89
+ operations do not depend on an experimental public Analytics Ops contract.
@@ -0,0 +1,86 @@
1
+ # Authentication
2
+
3
+ Analytics Ops uses [Google Application Default Credentials (ADC)](https://cloud.google.com/docs/authentication/application-default-credentials)
4
+ through Google's official Ruby clients. It does not implement OAuth, store
5
+ tokens, or accept credential fields in configuration.
6
+
7
+ ## Local read-only use
8
+
9
+ Use an identity that has read access to the target GA4 property:
10
+
11
+ ```bash
12
+ gcloud auth application-default login \
13
+ --scopes="https://www.googleapis.com/auth/cloud-platform,https://www.googleapis.com/auth/analytics.readonly"
14
+ ```
15
+
16
+ Enable both APIs in the Google Cloud project used for authentication:
17
+
18
+ - Google Analytics Admin API
19
+ - Google Analytics Data API
20
+
21
+ Then run:
22
+
23
+ ```bash
24
+ analytics-ops doctor
25
+ ```
26
+
27
+ `doctor` makes small read-only calls to both APIs. It confirms that Google
28
+ accepts the credentials and that the property can be read. The installed
29
+ clients do not expose a reliable contract for inspecting the scopes inside an
30
+ already-issued token, so `doctor` reports scope inspection as `unknown`
31
+ instead of guessing.
32
+
33
+ ## Applying changes
34
+
35
+ Read-only credentials cannot apply a plan. Use a separately protected identity
36
+ with the `analytics.edit` scope and sufficient access to the GA4 property:
37
+
38
+ ```bash
39
+ gcloud auth application-default login \
40
+ --scopes="https://www.googleapis.com/auth/cloud-platform,https://www.googleapis.com/auth/analytics.readonly,https://www.googleapis.com/auth/analytics.edit"
41
+ ```
42
+
43
+ Keep mutation credentials out of a deployed Rails web container. Apply plans
44
+ from an administrator workstation or a protected release job.
45
+
46
+ ## Automation
47
+
48
+ Prefer one of these:
49
+
50
+ - A dedicated service account with only the GA properties and roles it needs.
51
+ - Workload Identity Federation, which avoids a long-lived JSON key.
52
+ - An explicitly injected Google credentials object when using the Ruby API.
53
+
54
+ Cloud IAM permission alone does not grant access to a GA4 property. Add the
55
+ service-account identity—such as
56
+ `ga-reader@example-project.iam.gserviceaccount.com`—to the property with the
57
+ appropriate Google Analytics role.
58
+
59
+ ## Values that are easy to confuse
60
+
61
+ | Value | Example | Meaning |
62
+ | --- | --- | --- |
63
+ | Property ID | `123456789` | Numeric GA4 property identifier used by Admin and Data APIs |
64
+ | Stream ID | `987654321` | Numeric data-stream resource identifier |
65
+ | Measurement ID | `G-EXAMPLE1` | Web tagging identifier; not accepted as `property_id` or `stream_id` |
66
+ | Account ID | `100000001` | Numeric Analytics account identifier shown by discovery |
67
+ | OAuth client | Client ID and secret in your Cloud project | Starts a user OAuth flow; never put it in Analytics Ops YAML |
68
+ | Service account | An IAM email identity | Receives GA property access; a JSON private key is credential material |
69
+
70
+ ## Credential rules
71
+
72
+ - Never put credentials, paths to credentials, access tokens, refresh tokens,
73
+ private keys, API keys, or OAuth secrets in `analytics_ops.yml`.
74
+ - Never put credentials in a saved plan.
75
+ - Never commit service-account JSON.
76
+ - Never paste credentials into an issue, fixture, log, or report.
77
+ - Revoke or rotate credentials immediately after suspected exposure.
78
+ - Do not create a shared public OAuth client for this gem.
79
+
80
+ Analytics Ops redacts common credential patterns from translated errors, but
81
+ redaction is a final safety net—not a safe way to handle secrets.
82
+
83
+ Official Google setup references:
84
+
85
+ - [Admin API quickstart](https://developers.google.com/analytics/devguides/config/admin/v1/quickstart)
86
+ - [Data API quickstart](https://developers.google.com/analytics/devguides/reporting/data/v1/quickstart-client-libraries)