Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 25 additions & 5 deletions lib/manifold/templates/workspace_template.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,31 @@ timestamp:
metrics:
renders:
conditions:
mobile: IS_DESKTOP(context.device)
desktop: IS_MOBILE(context.device)
us: context.geo.country = 'US'
global: context.geo.country != 'US'

mobile:
args:
device: STRING
body: device = 'mobile'
desktop:
args:
device: STRING
body: device = 'desktop'
us:
args:
country: STRING
body: country = 'US'
global:
args:
country: STRING
body: country != 'US'
organic:
args:
acquisition: STRING
body: acquisition = 'organic'
paid:
args:
acquisition: STRING
body: acquisition = 'paid'

breakouts:
device:
- mobile
Expand Down
53 changes: 51 additions & 2 deletions lib/manifold/terraform/workspace_configuration.rb
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,7 @@ def build_table_config(table_id, schema_path = nil)
end

# Represents a Terraform configuration for a Manifold workspace.
class WorkspaceConfiguration < Configuration
class WorkspaceConfiguration < Configuration # rubocop:disable Metrics/ClassLength
attr_reader :name
attr_writer :dimensions_config, :manifold_config

Expand Down Expand Up @@ -214,7 +214,9 @@ def routine_config
"merge_dimensions" => dimensions_routine_attributes,
"merge_manifold" => manifold_routine_attributes
}.compact

# add user-defined condition routines, if any
conds = build_condition_routines
routines.merge!(conds) unless conds.empty?
routines.empty? ? nil : routines
end

Expand Down Expand Up @@ -243,6 +245,53 @@ def manifold_routine_attributes
"depends_on" => ["google_bigquery_dataset.#{name}"]
}
end

# generate scalar function routines for defined conditions
def build_condition_routines
return {} unless @manifold_config.is_a?(Hash) && @manifold_config["metrics"].is_a?(Hash)

@manifold_config["metrics"].each_with_object({}) do |(_grp, grp_cfg), routines|
next unless grp_cfg.is_a?(Hash) && grp_cfg["conditions"].is_a?(Hash)

grp_cfg["conditions"].each do |name, cfg|
id = build_routine_id(name)
attrs = condition_routine_attributes(id, cfg)
routines[id] = attrs
end
end
end

# build attributes for a single condition routine
def condition_routine_attributes(routine_id, cond_cfg)
attrs = {
"dataset_id" => name, "project" => "${var.project_id}", "routine_id" => routine_id,
"routine_type" => "SCALAR_FUNCTION", "language" => "SQL",
"definition_body" => cond_cfg["body"], "depends_on" => ["google_bigquery_dataset.#{name}"]
}
args = build_arguments(cond_cfg)
attrs["arguments"] = args if args
attrs["return_type"] = default_return_type
attrs
end

# helper to extract argument blocks from condition config
def build_arguments(cond_cfg)
return unless cond_cfg["args"].is_a?(Hash) && !cond_cfg["args"].empty?

cond_cfg["args"].map do |arg_name, arg_type|
{ "name" => arg_name, "data_type" => [{ "type_kind" => arg_type }] }
end
end

# default return type for condition routines
def default_return_type
[{ "data_type" => [{ "type_kind" => "BOOL" }] }]
end

# helper to build routine identifier from condition name
def build_routine_id(cond_name)
"is#{cond_name.to_s.split(/_|\\s+/).map(&:capitalize).join}"
end
end
end
end
50 changes: 50 additions & 0 deletions spec/manifold/terraform/workspace_configuration_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,56 @@
)
end
end

context "when metric conditions are present" do
before do
# define metrics with conditions for scalar function routines
config.manifold_config = {
"metrics" => {
"renders" => {
"conditions" => {
"organic" => {
"args" => { "acquisition" => "STRING" },
"body" => "acquisition = 'organic'"
},
"paid" => {
"args" => { "acquisition" => "STRING" },
"body" => "acquisition = 'paid'"
}
}
}
}
}
end

let(:routines) { json["resource"]["google_bigquery_routine"] }

it "includes scalar function routines for each condition" do
expect(routines.keys).to include("isOrganic", "isPaid")
end

# rubocop:disable RSpec/ExampleLength, RSpec/MultipleExpectations
it "correctly configures a condition routine" do
organic = routines["isOrganic"]
expect(organic).to include(
"dataset_id" => name,
"project" => "${var.project_id}",
"routine_id" => "isOrganic",
"routine_type" => "SCALAR_FUNCTION",
"language" => "SQL",
"definition_body" => "acquisition = 'organic'",
"depends_on" => ["google_bigquery_dataset.#{name}"]
)
# arguments and return_type blocks
expect(organic["arguments"]).to eq([
{ "name" => "acquisition", "data_type" => [{ "type_kind" => "STRING" }] }
])
expect(organic["return_type"]).to eq([
{ "data_type" => [{ "type_kind" => "BOOL" }] }
])
end
# rubocop:enable RSpec/ExampleLength, RSpec/MultipleExpectations
end
end

context "when manifold configuration is present" do
Expand Down
Loading