Class: AnalyticsOps::Workspace

Inherits:
Object
  • Object
show all
Defined in:
lib/analytics_ops/workspace.rb,
sig/analytics_ops.rbs

Overview

Primary Ruby API. Loading a workspace validates local data but performs no network calls.

Defined Under Namespace

Classes: DoctorResult, Verification

Constant Summary collapse

MAX_EXPORT_ROWS =
1_000_000
DEFAULT_PAGE_SIZE =
100_000

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(desired_state:, admin: nil, data: nil, bigquery: nil, service_account: nil, transport: :grpc, timeout: nil, logger: nil) ⇒ Workspace

Returns a new instance of Workspace.



62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
# File 'lib/analytics_ops/workspace.rb', line 62

def initialize(desired_state:, admin: nil, data: nil, bigquery: nil, service_account: nil,
               transport: :grpc, timeout: nil, logger: nil)
  unless .nil? || .is_a?(ServiceAccount)
    raise ConfigurationError, "service_account must be an AnalyticsOps::ServiceAccount"
  end

  @desired_state = desired_state
  @admin = admin
  @injected_admin = admin
  @data = data
  @bigquery = bigquery
  @service_account = 
  @transport = transport
  @timeout = timeout
  @logger = logger
end

Instance Attribute Details

#desired_stateDesiredState (readonly)

Returns the value of attribute desired_state.

Returns:



45
46
47
# File 'lib/analytics_ops/workspace.rb', line 45

def desired_state
  @desired_state
end

Class Method Details

.load(path, profile:, environment: ENV, admin: nil, data: nil, bigquery: nil, service_account: nil, transport: :grpc, timeout: nil, logger: nil) ⇒ Object



47
48
49
50
51
52
53
54
55
56
57
58
59
60
# File 'lib/analytics_ops/workspace.rb', line 47

def self.load(path, profile:, environment: ENV, admin: nil, data: nil, bigquery: nil, service_account: nil,
              transport: :grpc, timeout: nil, logger: nil)
  document = Configuration.load(path, environment:)
  new(
    desired_state: document.profile(profile),
    admin:,
    data:,
    bigquery:,
    service_account:,
    transport:,
    timeout:,
    logger:
  )
end

Instance Method Details

#access_report(date_ranges: nil, limit: 1_000) ⇒ Object



215
216
217
218
# File 'lib/analytics_ops/workspace.rb', line 215

def access_report(date_ranges: nil, limit: 1_000)
  ranges = date_ranges || Reports::Period.resolve(last_days: Reports::Period::DEFAULT_DAYS, compare: false)
  admin.access_report(desired_state.property_id, date_ranges: ranges, limit:)
end

#apply(plan_or_path, confirm: false) ⇒ Applier::Result

Parameters:

  • plan_or_path (Plan, String)
  • confirm: (Boolean) (defaults to: false)

Returns:



95
96
97
98
99
# File 'lib/analytics_ops/workspace.rb', line 95

def apply(plan_or_path, confirm: false)
  saved_plan = plan_or_path.is_a?(Plan) ? plan_or_path : Plan.load(plan_or_path)
  validate_plan_target!(saved_plan)
  Applier.new(admin: edit_admin).call(saved_plan, confirm:)
end

#auditPlan

Returns:



87
88
89
# File 'lib/analytics_ops/workspace.rb', line 87

def audit
  plan
end

#change_history(limit: 100, days: 30) ⇒ Governance::ChangeHistory

Parameters:

  • limit: (Integer) (defaults to: 100)
  • days: (Integer) (defaults to: 30)

Returns:



211
212
213
# File 'lib/analytics_ops/workspace.rb', line 211

def change_history(limit: 100, days: 30)
  edit_admin.change_history(desired_state.property_id, limit:, days:)
end

#discoverArray[Resources::Account]

Returns:



79
80
81
# File 'lib/analytics_ops/workspace.rb', line 79

def discover
  admin.discover
end

#doctorDoctorResult

Returns:



249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
# File 'lib/analytics_ops/workspace.rb', line 249

def doctor
  remote = snapshot
  checks = [
    { "name" => "configuration", "status" => "ok", "detail" => "Profile #{desired_state.profile} is valid" },
    { "name" => "credentials", "status" => "ok",
      "detail" => "Google accepted the configured service-account credentials" },
    { "name" => "admin_api", "status" => "ok", "detail" => "Admin API read succeeded" },
    { "name" => "property_access", "status" => "ok", "detail" => "Read properties/#{remote.property_id}" }
  ]
  checks.concat(credential_file_checks)
  checks.concat(compatibility_checks)
  checks << clock_check
  checks << edit_capability_check
  data.run(desired_state.property_id, doctor_report_definition)
  checks << { "name" => "data_api", "status" => "ok", "detail" => "Data API read succeeded" }
  checks << {
    "name" => "credential_scope",
    "status" => "ok",
    "detail" => "This read-only command uses the Analytics read-only scope"
  }
  admin.capabilities.each do |name, available|
    checks << {
      "name" => name,
      "status" => available ? "ok" : "unsupported",
      "detail" => available ? "Installed client exposes this capability" : "Installed client lacks this capability"
    }
  end
  DoctorResult.new(checks:)
end

#each_report_page(name_or_definition, date_ranges: nil, page_size: DEFAULT_PAGE_SIZE, max_rows: MAX_EXPORT_ROWS) ⇒ Object



152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
# File 'lib/analytics_ops/workspace.rb', line 152

def each_report_page(name_or_definition, date_ranges: nil, page_size: DEFAULT_PAGE_SIZE,
                     max_rows: MAX_EXPORT_ROWS)
  return enum_for(__method__, name_or_definition, date_ranges:, page_size:, max_rows:) unless block_given?

  definition, custom = resolved_report_definition(name_or_definition, kind: "standard")
  definition = definition.with_date_ranges(date_ranges) if date_ranges
  validate_report_definition(definition) if custom
  page_size = bounded_integer(page_size, "page_size", maximum: Reports::Definition::MAX_STANDARD_LIMIT)
  max_rows = bounded_integer(max_rows, "max_rows", maximum: MAX_EXPORT_ROWS)

  offset = definition.offset
  ending_offset = offset + max_rows
  while offset < ending_offset
    requested = [page_size, ending_offset - offset].min
    result = data.run(
      desired_state.property_id,
      definition.with_pagination(offset:, limit: requested)
    )
    yield result
    returned = result.rows.length
    break if returned.zero? || offset + returned >= result.row_count

    offset += returned
  end
end

#export_report(name_or_definition, path:, date_ranges: nil, page_size: DEFAULT_PAGE_SIZE, max_rows: MAX_EXPORT_ROWS) ⇒ Object



178
179
180
181
182
# File 'lib/analytics_ops/workspace.rb', line 178

def export_report(name_or_definition, path:, date_ranges: nil, page_size: DEFAULT_PAGE_SIZE,
                  max_rows: MAX_EXPORT_ROWS)
  pages = each_report_page(name_or_definition, date_ranges:, page_size:, max_rows:)
  Reports::CSVExport.write(path, pages)
end

#funnel(name, date_ranges: nil) ⇒ BigQuery::Result

Parameters:

  • name (String)
  • date_ranges: (Array[record], nil) (defaults to: nil)

Returns:



231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
# File 'lib/analytics_ops/workspace.rb', line 231

def funnel(name, date_ranges: nil)
  definition = desired_state.funnels.fetch(name.to_s) do
    available = desired_state.funnels.keys.sort.join(", ")
    raise InvalidRequestError, "Unknown funnel #{name.inspect}; available funnels: #{available}"
  end
  unless desired_state.bigquery
    raise UnsupportedCapabilityError,
          "Funnel reports currently require the optional BigQuery configuration"
  end
  ranges = date_ranges || Reports::Period.resolve(last_days: Reports::Period::DEFAULT_DAYS, compare: false)
  dates = BigQuery::Definition.from_date_ranges("event_counts", ranges, limit: 1)
  bigquery_client.run_funnel(
    definition,
    start_suffix: dates.start_suffix,
    end_suffix: dates.end_suffix
  )
end

#governanceGovernance::Result

Returns:



206
207
208
209
# File 'lib/analytics_ops/workspace.rb', line 206

def governance
  remote = admin.governance_snapshot(desired_state.property_id)
  Governance::Auditor.new(snapshot: remote, settings: desired_state.governance).call
end

#health(date_ranges: nil) ⇒ Health::Result

Parameters:

  • date_ranges: (Array[record], nil) (defaults to: nil)

Returns:



191
192
193
194
195
196
197
198
199
200
201
202
203
204
# File 'lib/analytics_ops/workspace.rb', line 191

def health(date_ranges: nil)
  ranges = date_ranges || Reports::Period.resolve(last_days: Reports::Period::DEFAULT_DAYS, compare: true)
  Health.validate_date_ranges!(ranges)
  definitions = Health::Definitions.for(ranges)
  reports = data.batch(desired_state.property_id, definitions)
  current_snapshot = snapshot
  current_plan = Planner.new(desired_state:, snapshot: current_snapshot).call
  Health::Runner.new(
    desired_state:,
    reports:,
    plan: current_plan,
    date_ranges: ranges
  ).call
end

#overview(date_ranges: nil) ⇒ Reports::OverviewResult

Parameters:

  • date_ranges: (Array[record], nil) (defaults to: nil)

Returns:



184
185
186
187
188
189
# File 'lib/analytics_ops/workspace.rb', line 184

def overview(date_ranges: nil)
  definitions = Reports::Catalog.overview
  definitions = definitions.map { |definition| definition.with_date_ranges(date_ranges) }.freeze if date_ranges
  reports = data.batch(desired_state.property_id, definitions)
  Reports::OverviewResult.new(property_id: desired_state.property_id, reports:)
end

#planPlan

Returns:



91
92
93
# File 'lib/analytics_ops/workspace.rb', line 91

def plan
  Planner.new(desired_state:, snapshot:).call
end

#raw_report(name, date_ranges: nil, limit: 1_000) ⇒ Object



220
221
222
223
224
225
226
227
228
229
# File 'lib/analytics_ops/workspace.rb', line 220

def raw_report(name, date_ranges: nil, limit: 1_000)
  settings = desired_state.bigquery
  unless settings
    raise ConfigurationError,
          "BigQuery is not configured for this profile; see docs/bigquery.md"
  end
  ranges = date_ranges || Reports::Period.resolve(last_days: Reports::Period::DEFAULT_DAYS, compare: false)
  definition = BigQuery::Definition.from_date_ranges(name, ranges, limit:)
  bigquery_client.run(definition)
end

#realtime(name_or_definition = "realtime_events") ⇒ Reports::Result

Parameters:

Returns:



112
113
114
115
116
# File 'lib/analytics_ops/workspace.rb', line 112

def realtime(name_or_definition = "realtime_events")
  definition, custom = resolved_report_definition(name_or_definition, kind: "realtime")
  validate_report_definition(definition) if custom
  data.run(desired_state.property_id, definition)
end

#report(name_or_definition, date_ranges: nil) ⇒ Object



105
106
107
108
109
110
# File 'lib/analytics_ops/workspace.rb', line 105

def report(name_or_definition, date_ranges: nil)
  definition, custom = resolved_report_definition(name_or_definition, kind: "standard")
  definition = definition.with_date_ranges(date_ranges) if date_ranges
  validate_report_definition(definition) if custom
  data.run(desired_state.property_id, definition)
end

#report_compatibility(name_or_definition) ⇒ Object



146
147
148
149
150
# File 'lib/analytics_ops/workspace.rb', line 146

def report_compatibility(name_or_definition)
  definition, = resolved_report_definition(name_or_definition, kind: "standard")
  validate_report_fields!(definition)
  data.check_compatibility(desired_state.property_id, definition)
end

#report_metadata(query: nil, kind: nil) ⇒ Reports::FieldList

Parameters:

  • query: (String, nil) (defaults to: nil)
  • kind: (String, nil) (defaults to: nil)

Returns:



118
119
120
121
# File 'lib/analytics_ops/workspace.rb', line 118

def (query: nil, kind: nil)
  fields = data.(desired_state.property_id).search(query, kind:)
  Reports::FieldList.new(property_id: desired_state.property_id, query:, kind: kind&.to_s, fields:)
end

#report_recipesReports::RecipeList

Returns:



123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
# File 'lib/analytics_ops/workspace.rb', line 123

def report_recipes
  built_in = Reports::Catalog.names.map do |name|
    definition = Reports::Catalog.fetch(name)
    Reports::Recipe.new(
      name:,
      kind: definition.kind,
      source: "built_in",
      dimensions: definition.dimensions,
      metrics: definition.metrics
    )
  end
  configured = desired_state.reports.map do |name, definition|
    Reports::Recipe.new(
      name:,
      kind: definition.kind,
      source: "configuration",
      dimensions: definition.dimensions,
      metrics: definition.metrics
    )
  end
  Reports::RecipeList.new(recipes: (built_in + configured).sort_by { |recipe| [recipe.name, recipe.source] })
end

#snapshotSnapshot

Returns:



83
84
85
# File 'lib/analytics_ops/workspace.rb', line 83

def snapshot
  admin.snapshot(desired_state.property_id)
end

#verifyVerification

Returns:



101
102
103
# File 'lib/analytics_ops/workspace.rb', line 101

def verify
  Verification.new(plan:)
end