strata-cli 0.1.11 → 0.1.13

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 371199d421786d4bc06748770b1155f59206d49f115eb0d2c664df2a0de139d8
4
- data.tar.gz: eb1ecac2dc8e40864f5c8ad278353ed8b8924f99fc54a2dad5c356f63dacd06d
3
+ metadata.gz: 61be99c9ce107eae809e67fa23dd4108a471dcc90b6adb039a298b5d59005106
4
+ data.tar.gz: '080076fa7d113e1e066caca27a3e33e3c438a0bb8d2e53a602ed75cf7a5b5ec5'
5
5
  SHA512:
6
- metadata.gz: 6f65de98ebf7994129da1cc2b6ae16a3591020e799ab61583afb1eea725f69b73fa6e35f3d8e69ebe0540bf8cdfe02f38a2f8d9f5c9e2ca606d0bf81536fa1d1
7
- data.tar.gz: f6e89761a4ef0fd8586d8a82a4262594f22f95a14f814abc6b48ab2d62e20e840429ee0dbf215dc58f2c8a4d350eace329d7be6528f1a079b2fb17a223cf88c2
6
+ metadata.gz: eaac835415776492c4858204340238a71fc9a8e45ad61183d46f532a22723774d39a16e9dfb30db5841bc897a12547a9a8fe93c1543e3b7e1975daecdf5b2d82
7
+ data.tar.gz: d443533139c4aea533e2bf0fd2d3f14e7de3bf7f1bb87e004e174a7232802b8c9086c7e470bb1640c697522fad07445db6bbfa60185e1a667fbe0f7c838ede58
data/CHANGELOG.md CHANGED
@@ -1,5 +1,26 @@
1
1
  ## [Unreleased]
2
2
 
3
+ ## [0.1.13] - 2026-06-20
4
+
5
+ ### Changed
6
+
7
+ - **Documentation**: ClickHouse adapter docs in README, gemspec description, `datasource add` help text, and CHANGELOG notes for the 0.1.12 feature set.
8
+
9
+ ## [0.1.12] - 2026-06-20
10
+
11
+ ### Added
12
+
13
+ - **ClickHouse adapter**: `strata datasource add clickhouse` with connection prompts and optional password credentials.
14
+ - **Security policy scaffolding**: `strata create policy` to add policies to `security.yml`; new projects include a `security.yml` template.
15
+
16
+ ### Changed
17
+
18
+ - **dwh dependency**: Require `dwh` `~> 0.5.0` (ClickHouse adapter and dialect keyword/aggregate support).
19
+
20
+ ### Fixed
21
+
22
+ - **Datasource introspection**: Pass only `catalog` and `schema` qualifiers to DWH for `tables` and `meta` (avoids leaking CLI flags like `--agent` or `--pattern` to adapters).
23
+
3
24
  ## [0.1.11] - 2026-05-22
4
25
 
5
26
  ### Changed
data/README.md CHANGED
@@ -375,6 +375,17 @@ my_databricks:
375
375
  catalog: main
376
376
  schema: default
377
377
  auth_mode: oauth_m2m
378
+
379
+ my_clickhouse:
380
+ adapter: clickhouse
381
+ name: ClickHouse
382
+ protocol: http
383
+ host: localhost
384
+ port: 8123
385
+ database: default
386
+ username: default
387
+ tier: warm
388
+ query_timeout: 3600
378
389
  ```
379
390
 
380
391
  ### Databricks authentication
@@ -403,6 +414,7 @@ Use service principal credentials and run `strata ds auth my_databricks` to save
403
414
  - **Druid** - Apache Druid support
404
415
  - **Redshift** - Amazon Redshift support
405
416
  - **Databricks** - Databricks SQL warehouse support
417
+ - **ClickHouse** - ClickHouse HTTP/native SQL support
406
418
  - **SQLite** - SQLite database support
407
419
 
408
420
  ## Security
@@ -48,6 +48,8 @@ module Strata
48
48
  credentials["auth_mode"] = "oauth_m2m"
49
49
  credentials["oauth_client_id"] = @prompt.ask("OAuth Client ID:")
50
50
  credentials["oauth_client_secret"] = @prompt.ask("OAuth Client Secret:")
51
+ when "clickhouse"
52
+ credentials["password"] = @prompt.mask("Enter Password (leave blank if none):")
51
53
  else
52
54
  if required?
53
55
  unless %w[postgres mysql trino sqlserver].include?(adapter)
@@ -18,4 +18,4 @@ Examples:
18
18
 
19
19
  strata datasource add snowflake # Directly add Snowflake datasource
20
20
 
21
- Supported adapters: postgres, mysql, sqlserver, snowflake, athena, trino, duckdb, druid
21
+ Supported adapters: postgres, mysql, sqlserver, snowflake, athena, trino, duckdb, druid, clickhouse, databricks, redshift, sqlite
@@ -0,0 +1,68 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "group"
4
+ require_relative "../output"
5
+
6
+ module Strata
7
+ module CLI
8
+ module Generators
9
+ # Appends a new policy entry to the project's security.yml.
10
+ # Receives a fully-resolved attrs hash from the Create subcommand.
11
+ class Policy < Group
12
+ argument :attrs, type: :hash
13
+
14
+ def append_policy_to_security_yml
15
+ security_file = "security.yml"
16
+
17
+ unless File.exist?(security_file)
18
+ raise Strata::CommandError,
19
+ "security.yml not found in the current directory. Run 'strata init' first."
20
+ end
21
+
22
+ content = File.read(security_file)
23
+ new_entry = format_policy_yaml(attrs)
24
+
25
+ updated = if content.match?(/^policies:\s*\[\]/)
26
+ # First policy — replace inline empty array with block sequence
27
+ content.sub(/^policies:\s*\[\]/, "policies:\n#{new_entry}")
28
+ else
29
+ # Subsequent policy — append after existing entries
30
+ content.rstrip + "\n#{new_entry}"
31
+ end
32
+
33
+ File.write(security_file, updated)
34
+ Output.print_status(:updated, security_file, type: :success, context: self)
35
+ end
36
+
37
+ private
38
+
39
+ def format_policy_yaml(a)
40
+ lines = []
41
+ lines << " - name: #{a[:name]}"
42
+ lines << " mode: #{a[:mode]}"
43
+ lines << " mask_value: \"#{a[:mask_value]}\"" if a[:mode] == "mask_data"
44
+ lines << " triggers:"
45
+
46
+ tags = Array(a[:field_tags])
47
+ lines << " field_tags: [#{tags.join(", ")}]"
48
+
49
+ field_names = Array(a[:field_names]).reject(&:empty?)
50
+ unless field_names.empty?
51
+ lines << " field_names:"
52
+ field_names.each { |n| lines << " - #{n}" }
53
+ end
54
+
55
+ lines << " context_dimension: #{a[:context_dimension]}"
56
+ lines << " permission_resolution:"
57
+ lines << " source: #{a[:permission_source]}"
58
+ lines << " value_from: #{a[:value_from]}"
59
+ lines << " tag_key: #{a[:tag_key]}" if a[:tag_key].to_s.strip.length > 0
60
+ lines << " bypass:"
61
+ lines << " system_admin: #{a[:bypass_system_admin]}"
62
+ lines << " project_admin: #{a[:bypass_project_admin]}"
63
+ lines.join("\n") + "\n"
64
+ end
65
+ end
66
+ end
67
+ end
68
+ end
@@ -57,7 +57,7 @@ module Strata
57
57
  def create_project_structure
58
58
  return if cloned_from_git?
59
59
 
60
- empty_directory uid
60
+ empty_directory uid unless existing_directory?
61
61
  empty_directory File.join(uid, "models")
62
62
  empty_directory File.join(uid, "tests")
63
63
  end
@@ -102,6 +102,12 @@ module Strata
102
102
  end
103
103
  end
104
104
 
105
+ def create_security_file
106
+ return if cloned_from_git?
107
+
108
+ copy_file "security.yml", "#{uid}/security.yml"
109
+ end
110
+
105
111
  def initialize_git
106
112
  return if cloned_from_git?
107
113
 
@@ -113,7 +119,8 @@ module Strata
113
119
 
114
120
  def setup_datasource
115
121
  return if cloned_from_git?
116
- return if options.key?(:datasource) # Already specified via CLI option
122
+ return if options.key?(:datasource)
123
+ return if existing_directory?
117
124
 
118
125
  print_status(:setup, "Let's configure your first datasource", type: :info)
119
126
  print_info("\n")
@@ -140,6 +147,10 @@ module Strata
140
147
  @cloned_from_git ||= false
141
148
  end
142
149
 
150
+ def existing_directory?
151
+ File.directory?(name.to_s)
152
+ end
153
+
143
154
  def fetch_project_info
144
155
  conn = Faraday.new(url: options[:source]) do |f|
145
156
  f.request :authorization, "Bearer", options[:api_key]
@@ -180,6 +191,8 @@ module Strata
180
191
  def uid
181
192
  @uid ||= if options.key?(:source) && @project_info
182
193
  @project_info["uid"] || options[:source].split("/").last
194
+ elsif existing_directory?
195
+ name.to_s
183
196
  else
184
197
  Utils.url_safe_str(name)
185
198
  end
@@ -28,11 +28,13 @@ Read this file end-to-end before creating or editing `models/**/*.yml`. Do not m
28
28
 
29
29
  These are non-negotiable. Violations break deploy, query planning, or production reports.
30
30
 
31
- 1. **Every field `name` is unique project-wide** — one name = one dimension or measure entity across all `tbl.*.yml` files. Strata has no `table.field` namespaces; bracket references like `[Total Revenue]` resolve by name alone.
31
+ 1. **Every field `name` is unique project-wide per dimension or measure** — one name = one dimension or measure entity across all `tbl.*.yml` files. Strata has no `table.field` namespaces; bracket references like `[Total Revenue]` resolve by name alone.
32
32
  2. **`many_to_many` join cardinality is not supported.** Use a junction/bridge table with two relationships (`one_to_many` + `many_to_one`) instead.
33
33
  3. **Measures on unrelated detail facts must have distinct names** — e.g. `Store Gross Sales`, `Catalog Gross Sales`, not one `Gross Sales` on three channel facts. See [Naming conventions](#naming-conventions).
34
- 4. **Dimensions may share names** when the business role is the same; Strata picks among tables at query time. **Measures do not combine that way** — duplicate measure names attach more SQL to the same measure entity (double-count risk).
34
+ 4. **Dimensions may share names** when the business role is the same; Strata picks among tables at query time. **Measures do not combine that way** — duplicate measure names implies varying levels of aggregation of the same basic fact. The correct table and measure combination will be determined by context (i.e. what dimensions are present in the same query.)
35
35
  5. **Cross-fact totals use compound measures or blending** — not the same measure name on multiple facts. See [Combined metrics across facts](#combined-metrics-across-facts).
36
+ 6. Security policies can be implemented in the security.yml file. Use the guidelines documented therin.
37
+ 7. Strata supports role playing or aliasing of dimension tables. For example, user wants date_dim table to also be used when query "Order Return Date". In that case, we simply point to the same physical_name "date_dim" but set the table name to "Order Return Date". If the user asks to alias an dimension table from an existing model we can simply copy the existing model, change the table name, and prefix all the dimensions in it with the appropriate prefix representing the role.
36
38
 
37
39
  ## Unsupported or impossible requirements
38
40
 
@@ -0,0 +1,77 @@
1
+ # Semantic Security Policies
2
+ #
3
+ # This file defines row-level security policies enforced at query planning time.
4
+ # Policies are branch-scoped and deployed alongside your semantic model.
5
+ #
6
+ # Two enforcement modes:
7
+ #
8
+ # mask_data — Sensitive column values are replaced with a mask string.
9
+ # Rows remain in the result set; users can see that restricted
10
+ # data exists but cannot read it.
11
+ #
12
+ # filter_data — Rows for context values the user cannot access are removed
13
+ # entirely. Restricted data is invisible to the user.
14
+ #
15
+ # How it works:
16
+ # 1. A policy is triggered when a query projects a field tagged with one of
17
+ # the trigger tags (e.g. "pii").
18
+ # 2. The context_dimension defines the security boundary (e.g. "Call Center ID").
19
+ # It must be reachable in every universe that contains a triggered field,
20
+ # otherwise the query is rejected with a hard error.
21
+ # 3. Permission resolution determines which context dimension values the current
22
+ # user is allowed to see, derived from their group memberships or user record.
23
+ #
24
+ # Tagging fields: add a tags key to any dimension in your table YAML files:
25
+ #
26
+ # fields:
27
+ # - type: dimension
28
+ # name: Agent Name
29
+ # tags: [pii]
30
+ # data_type: string
31
+ # expression:
32
+ # sql: agent_name
33
+
34
+ policies: []
35
+
36
+ # ── Example: mask PII dimensions scoped by call center ──────────────────────
37
+ #
38
+ # - name: PII Call Center Masking
39
+ # mode: mask_data # mask_data | filter_data
40
+ # mask_value: "#######" # Value shown to users without access (mask_data only)
41
+ #
42
+ # triggers:
43
+ # field_tags: [pii] # Triggers on any field tagged "pii"
44
+ # # field_names: # Optional: trigger on specific fields by display name
45
+ # # - Agent Name # Resolved to UIDs at deploy time
46
+ #
47
+ # # Dimension that scopes visibility. Resolved by name at deploy time.
48
+ # # Must be reachable in the universe for any query that includes a triggered field.
49
+ # context_dimension: Call Center ID
50
+ #
51
+ # permission_resolution:
52
+ # source: groups # groups | user
53
+ # value_from: tag # groups: name | tag / user: email | tag
54
+ # tag_key: call_center_id # Required when value_from is "tag".
55
+ # # Extracts the value from group tags formatted as "call_center_id:TMNT"
56
+ #
57
+ # bypass:
58
+ # system_admin: true # System admins bypass this policy entirely
59
+ # project_admin: false
60
+ #
61
+ # ── Example: filter rows so each user sees only their own employee record ────
62
+ #
63
+ # - name: Employee Self-Service
64
+ # mode: filter_data
65
+ #
66
+ # triggers:
67
+ # field_tags: [employee_pii]
68
+ #
69
+ # context_dimension: Employee Email
70
+ #
71
+ # permission_resolution:
72
+ # source: user # Resolve from the current user's own attributes
73
+ # value_from: email # Allowed value = the authenticated user's email address
74
+ #
75
+ # bypass:
76
+ # system_admin: true
77
+ # project_admin: false
@@ -28,6 +28,21 @@ module Strata
28
28
  MSG_MODELS_LIST_HEADER = "\n Semantic Models:\n"
29
29
  MSG_MODELS_COUNT = "\n Total: %d model(s)\n"
30
30
 
31
+ # Policy Command Prompts
32
+ MSG_POLICY_NAME = " Policy name:"
33
+ MSG_POLICY_MODE = " Enforcement mode:"
34
+ MSG_POLICY_MASK_VALUE = " Mask value (shown for restricted data):"
35
+ MSG_POLICY_FIELD_TAGS = " Field tags that trigger this policy (comma-separated, e.g. pii,hipaa):"
36
+ MSG_POLICY_FIELD_NAMES = " Specific field names (comma-separated, optional — leave blank to skip):"
37
+ MSG_POLICY_CONTEXT_DIM = " Context dimension name:"
38
+ MSG_POLICY_PERM_SOURCE = " Permission source:"
39
+ MSG_POLICY_VALUE_FROM = " Resolve allowed values from:"
40
+ MSG_POLICY_TAG_KEY = " Tag key (e.g. call_center_id):"
41
+ MSG_POLICY_BYPASS_ADMIN = " Bypass for system admins?"
42
+ MSG_POLICY_BYPASS_PROJECT = " Bypass for project admins?"
43
+ MSG_POLICY_CREATED = "\n✔ Policy '%s' added to security.yml"
44
+ MSG_POLICY_HINT = "\n💡 Run 'strata deploy' to activate this policy"
45
+
31
46
  # Migration hook options
32
47
  MIGRATION_HOOK_OPTIONS = {
33
48
  "pre (before deployment)" => "pre",
@@ -30,7 +30,7 @@ module Strata
30
30
  ALLOWED_FIELD_KEYS = %w[
31
31
  type name description hidden grains data_type display_type format
32
32
  secure disable_listing value_list_size snapshot exclusion_type
33
- exclusions inclusions extended_blend_group synonyms expression
33
+ exclusions inclusions extended_blend_group synonyms expression tags
34
34
  ].freeze
35
35
  ALLOWED_EXPRESSION_KEYS = %w[sql array lookup primary_key].freeze
36
36
  EXPRESSION_BOOLEAN_KEYS = %w[array lookup primary_key].freeze
@@ -51,6 +51,21 @@ module Strata
51
51
  end
52
52
  end
53
53
 
54
+ desc "policy [NAME]", "Scaffold a new security policy in security.yml"
55
+ method_option :interactive, aliases: "-i", type: :boolean, default: false,
56
+ desc: "Walk through all options interactively"
57
+ method_option :mode, type: :string, desc: "Enforcement mode: mask_data or filter_data"
58
+ method_option :tags, type: :array, desc: "Field tags that trigger this policy"
59
+ method_option :context, type: :string, desc: "Context dimension name"
60
+
61
+ def policy(name = nil)
62
+ attrs = policy_interactive_mode? ? collect_policy_interactive(name) : collect_policy_fast(name)
63
+ require_relative "../generators/policy"
64
+ Generators::Policy.new([attrs]).invoke_all
65
+ say MSG_POLICY_CREATED % attrs[:name], :green
66
+ say MSG_POLICY_HINT, :yellow
67
+ end
68
+
54
69
  desc "relation RELATION_PATH", "Create a relation (join) definition file"
55
70
  long_desc_from_file("create/relation")
56
71
 
@@ -122,6 +137,94 @@ module Strata
122
137
 
123
138
  private
124
139
 
140
+ # ── Policy helpers ────────────────────────────────────────────────────
141
+
142
+ def policy_interactive_mode?
143
+ options[:interactive] ||
144
+ (options[:mode].nil? && options[:tags].nil? && options[:context].nil?)
145
+ end
146
+
147
+ def collect_policy_fast(name)
148
+ {
149
+ name: name || "New Policy",
150
+ mode: options[:mode] || "mask_data",
151
+ mask_value: "#######",
152
+ field_tags: Array(options[:tags]),
153
+ field_names: [],
154
+ context_dimension: options[:context] || "<context_dimension>",
155
+ permission_source: "groups",
156
+ value_from: "tag",
157
+ tag_key: "<tag_key>",
158
+ bypass_system_admin: true,
159
+ bypass_project_admin: false
160
+ }
161
+ end
162
+
163
+ def collect_policy_interactive(name)
164
+ say "\n Policy\n", :bold
165
+ policy_name = name || prompt.ask(MSG_POLICY_NAME) { |q| q.required true }
166
+
167
+ mode = prompt.select(MSG_POLICY_MODE, [
168
+ {name: "mask_data — replace restricted values with a mask", value: "mask_data"},
169
+ {name: "filter_data — remove restricted rows entirely", value: "filter_data"}
170
+ ])
171
+
172
+ mask_value = if mode == "mask_data"
173
+ prompt.ask(MSG_POLICY_MASK_VALUE, default: "#######")
174
+ end
175
+
176
+ say "\n Triggers\n", :bold
177
+ tags_raw = prompt.ask(MSG_POLICY_FIELD_TAGS, default: "")
178
+ field_tags = tags_raw.to_s.split(",").map(&:strip).reject(&:empty?)
179
+
180
+ names_raw = prompt.ask(MSG_POLICY_FIELD_NAMES, default: "")
181
+ field_names = names_raw.to_s.split(",").map(&:strip).reject(&:empty?)
182
+
183
+ say "\n Context\n", :bold
184
+ context_dimension = prompt.ask(MSG_POLICY_CONTEXT_DIM) { |q| q.required true }
185
+
186
+ say "\n Permission Resolution\n", :bold
187
+ permission_source = prompt.select(MSG_POLICY_PERM_SOURCE, [
188
+ {name: "groups — derive from the user's group memberships", value: "groups"},
189
+ {name: "user — derive from the user's own attributes", value: "user"}
190
+ ])
191
+
192
+ value_from_choices = if permission_source == "groups"
193
+ [
194
+ {name: "tag — extract value from a group tag (e.g. call_center_id:TMNT)", value: "tag"},
195
+ {name: "name — use the group's display name as the allowed value", value: "name"}
196
+ ]
197
+ else
198
+ [
199
+ {name: "email — the user's email address", value: "email"},
200
+ {name: "tag — extract value from a user tag", value: "tag"}
201
+ ]
202
+ end
203
+ value_from = prompt.select(MSG_POLICY_VALUE_FROM, value_from_choices)
204
+
205
+ tag_key = if value_from == "tag"
206
+ prompt.ask(MSG_POLICY_TAG_KEY) { |q| q.required true }
207
+ end
208
+
209
+ say "\n Bypass\n", :bold
210
+ bypass_system_admin = prompt.yes?(MSG_POLICY_BYPASS_ADMIN, default: true)
211
+ bypass_project_admin = prompt.yes?(MSG_POLICY_BYPASS_PROJECT, default: false)
212
+
213
+ {
214
+ name: policy_name,
215
+ mode: mode,
216
+ mask_value: mask_value,
217
+ field_tags: field_tags,
218
+ field_names: field_names,
219
+ context_dimension: context_dimension,
220
+ permission_source: permission_source,
221
+ value_from: value_from,
222
+ tag_key: tag_key,
223
+ bypass_system_admin: bypass_system_admin,
224
+ bypass_project_admin: bypass_project_admin
225
+ }
226
+ end
227
+
125
228
  def handle_table_creation_with_path(table_path)
126
229
  # Parse the path to extract schema, physical name, and model path
127
230
  parsed = parse_table_path(table_path)
@@ -187,7 +187,7 @@ module Strata
187
187
  return unless ds_key
188
188
 
189
189
  adapter = create_adapter(ds_key)
190
- tables = adapter.tables(**options)
190
+ tables = adapter.tables(**adapter_qualifiers)
191
191
  tables = tables.select { it =~ /#{options[:pattern]}/ } if options[:pattern]
192
192
 
193
193
  if agent_mode?
@@ -214,7 +214,7 @@ module Strata
214
214
  method_option :schema, aliases: "s", type: :string, desc: "Change the schema from the configured one."
215
215
  def meta(ds_key, table_name)
216
216
  adapter = create_adapter(ds_key)
217
- md = adapter.metadata(table_name, **options.transform_keys(&:to_sym))
217
+ md = adapter.metadata(table_name, **adapter_qualifiers)
218
218
 
219
219
  if agent_mode?
220
220
  columns = md.columns.map do |column|
@@ -275,6 +275,10 @@ module Strata
275
275
  false
276
276
  end
277
277
 
278
+ def adapter_qualifiers
279
+ options.transform_keys(&:to_sym).slice(:catalog, :schema)
280
+ end
281
+
278
282
  def validate_file_path(file_path, project_path = Dir.pwd)
279
283
  expanded = File.expand_path(file_path, project_path)
280
284
  project_root = File.expand_path(project_path)
@@ -370,6 +374,14 @@ module Strata
370
374
  "schema" => "default",
371
375
  "auth_mode" => "oauth_m2m"
372
376
  }
377
+ when "clickhouse"
378
+ {
379
+ "protocol" => "http",
380
+ "host" => "localhost",
381
+ "port" => 8123,
382
+ "database" => "test_db",
383
+ "username" => "default"
384
+ }
373
385
  else
374
386
  {}
375
387
  end
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Strata
4
4
  module CLI
5
- VERSION = "0.1.11"
5
+ VERSION = "0.1.13"
6
6
  end
7
7
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: strata-cli
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.11
4
+ version: 0.1.13
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ajo Abraham
@@ -16,14 +16,14 @@ dependencies:
16
16
  requirements:
17
17
  - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: 0.4.2
19
+ version: 0.5.0
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
- version: 0.4.2
26
+ version: 0.5.0
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: aws-sdk-athena
29
29
  requirement: !ruby/object:Gem::Requirement
@@ -224,7 +224,7 @@ description: |
224
224
  Comprehensive CLI tool for managing Strata Semantic Analytics projects. Create and initialize
225
225
  projects, manage datasource connections, build semantic table models with AI assistance, run
226
226
  audits and validations, and deploy projects to Strata servers. Supports multiple data warehouse
227
- adapters including PostgreSQL, MySQL, SQL Server, Snowflake, Athena, Trino, and more.
227
+ adapters including PostgreSQL, MySQL, SQL Server, Snowflake, Athena, Trino, ClickHouse, and more.
228
228
  email:
229
229
  - ajo@strata.site
230
230
  - allen@strata.site
@@ -268,6 +268,7 @@ files:
268
268
  - lib/strata/cli/generators/datasource.rb
269
269
  - lib/strata/cli/generators/group.rb
270
270
  - lib/strata/cli/generators/migration.rb
271
+ - lib/strata/cli/generators/policy.rb
271
272
  - lib/strata/cli/generators/project.rb
272
273
  - lib/strata/cli/generators/relation.rb
273
274
  - lib/strata/cli/generators/table.rb
@@ -286,6 +287,7 @@ files:
286
287
  - lib/strata/cli/generators/templates/migration.swap.yml
287
288
  - lib/strata/cli/generators/templates/project.yml
288
289
  - lib/strata/cli/generators/templates/rel.domain.yml
290
+ - lib/strata/cli/generators/templates/security.yml
289
291
  - lib/strata/cli/generators/templates/strata.yml
290
292
  - lib/strata/cli/generators/templates/table.table_name.yml
291
293
  - lib/strata/cli/generators/templates/test.yml