textus 0.10.5 → 0.12.1
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 +4 -4
- data/CHANGELOG.md +104 -3
- data/README.md +39 -26
- data/SPEC.md +222 -144
- data/lib/textus/application/reads/freshness.rb +2 -2
- data/lib/textus/application/reads/get.rb +1 -1
- data/lib/textus/application/reads/policy_explain.rb +2 -2
- data/lib/textus/application/refresh/orchestrator.rb +1 -1
- data/lib/textus/application/refresh/worker.rb +5 -5
- data/lib/textus/application/writes/accept.rb +19 -1
- data/lib/textus/application/writes/build.rb +5 -5
- data/lib/textus/application/writes/delete.rb +1 -1
- data/lib/textus/application/writes/publish.rb +1 -1
- data/lib/textus/application/writes/put.rb +1 -1
- data/lib/textus/builder/pipeline.rb +1 -1
- data/lib/textus/builder/renderer/json.rb +1 -1
- data/lib/textus/builder/renderer/yaml.rb +1 -1
- data/lib/textus/cli/group/key.rb +1 -1
- data/lib/textus/cli/group/refresh.rb +21 -0
- data/lib/textus/cli/group/rule.rb +11 -0
- data/lib/textus/cli/verb/build.rb +1 -1
- data/lib/textus/cli/verb/hook_run.rb +3 -2
- data/lib/textus/cli/verb/hooks.rb +1 -1
- data/lib/textus/cli/verb/{migrate_keys.rb → key_normalize.rb} +1 -1
- data/lib/textus/cli/verb/put.rb +1 -1
- data/lib/textus/cli/verb/{policy_explain.rb → rule_explain.rb} +1 -1
- data/lib/textus/cli/verb/{policy_list.rb → rule_list.rb} +3 -3
- data/lib/textus/cli/verb.rb +3 -2
- data/lib/textus/cli.rb +6 -6
- data/lib/textus/doctor/check/handler_allowlist.rb +1 -1
- data/lib/textus/doctor/check/illegal_keys.rb +39 -16
- data/lib/textus/doctor/check/intake_registration.rb +4 -4
- data/lib/textus/doctor/check/protocol_version.rb +47 -0
- data/lib/textus/doctor/check/{policy_ambiguity.rb → rule_ambiguity.rb} +6 -6
- data/lib/textus/doctor.rb +5 -4
- data/lib/textus/domain/permission.rb +4 -4
- data/lib/textus/domain/policy/predicates/human_accept.rb +31 -0
- data/lib/textus/domain/policy/predicates/schema_valid.rb +50 -0
- data/lib/textus/domain/policy/promotion.rb +45 -0
- data/lib/textus/errors.rb +24 -5
- data/lib/textus/hooks/builtin.rb +5 -5
- data/lib/textus/hooks/dispatcher.rb +1 -1
- data/lib/textus/hooks/dsl.rb +3 -10
- data/lib/textus/hooks/loader.rb +1 -2
- data/lib/textus/hooks/registry.rb +22 -21
- data/lib/textus/infra/refresh/detached.rb +1 -1
- data/lib/textus/init.rb +25 -34
- data/lib/textus/intro.rb +9 -9
- data/lib/textus/manifest/entry.rb +33 -6
- data/lib/textus/manifest/{policies.rb → rules.rb} +12 -10
- data/lib/textus/manifest/schema.rb +49 -0
- data/lib/textus/manifest.rb +45 -9
- data/lib/textus/migrate_keys.rb +1 -1
- data/lib/textus/projection.rb +4 -4
- data/lib/textus/refresh.rb +1 -1
- data/lib/textus/store/mover.rb +1 -1
- data/lib/textus/store/staleness/intake_check.rb +1 -1
- data/lib/textus/store/writer.rb +1 -1
- data/lib/textus/store.rb +1 -1
- data/lib/textus/version.rb +2 -2
- data/lib/textus.rb +1 -0
- metadata +13 -7
- data/lib/textus/cli/group/policy.rb +0 -11
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
module Textus
|
|
2
|
+
class Manifest
|
|
3
|
+
module Schema
|
|
4
|
+
ROOT_KEYS = %w[version zones entries rules].freeze
|
|
5
|
+
ZONE_KEYS = %w[name write_policy read_policy].freeze
|
|
6
|
+
ENTRY_KEYS = %w[
|
|
7
|
+
key path zone schema owner nested format
|
|
8
|
+
compute template publish_to publish_each
|
|
9
|
+
intake events inject_intro index_filename
|
|
10
|
+
].freeze
|
|
11
|
+
COMPUTE_KEYS = %w[kind select pluck sort_by limit transform command sources].freeze
|
|
12
|
+
INTAKE_KEYS = %w[handler config].freeze
|
|
13
|
+
RULE_KEYS = %w[match refresh intake_handler_allowlist promotion retention].freeze
|
|
14
|
+
REFRESH_KEYS = %w[ttl on_stale sync_budget_ms].freeze
|
|
15
|
+
PROMOTION_KEYS = %w[requires].freeze
|
|
16
|
+
|
|
17
|
+
def self.validate!(raw)
|
|
18
|
+
raise BadManifest.new("manifest must be a hash") unless raw.is_a?(Hash)
|
|
19
|
+
|
|
20
|
+
walk(raw, ROOT_KEYS, "$")
|
|
21
|
+
Array(raw["zones"]).each_with_index do |z, i|
|
|
22
|
+
walk(z, ZONE_KEYS, "$.zones[#{i}]")
|
|
23
|
+
end
|
|
24
|
+
Array(raw["entries"]).each_with_index do |e, i|
|
|
25
|
+
path = "$.entries[#{i}]"
|
|
26
|
+
walk(e, ENTRY_KEYS, path)
|
|
27
|
+
walk(e["compute"], COMPUTE_KEYS, "#{path}.compute") if e["compute"].is_a?(Hash)
|
|
28
|
+
walk(e["intake"], INTAKE_KEYS, "#{path}.intake") if e["intake"].is_a?(Hash)
|
|
29
|
+
end
|
|
30
|
+
Array(raw["rules"]).each_with_index do |r, i|
|
|
31
|
+
path = "$.rules[#{i}]"
|
|
32
|
+
walk(r, RULE_KEYS, path)
|
|
33
|
+
walk(r["refresh"], REFRESH_KEYS, "#{path}.refresh") if r["refresh"].is_a?(Hash)
|
|
34
|
+
walk(r["promotion"], PROMOTION_KEYS, "#{path}.promotion") if r["promotion"].is_a?(Hash)
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def self.walk(hash, allowed, path)
|
|
39
|
+
return unless hash.is_a?(Hash)
|
|
40
|
+
|
|
41
|
+
hash.each_key do |k|
|
|
42
|
+
next if allowed.include?(k)
|
|
43
|
+
|
|
44
|
+
raise BadManifest.new("unknown key '#{k}' at '#{path}'")
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
data/lib/textus/manifest.rb
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
require "yaml"
|
|
2
|
+
require_relative "manifest/schema"
|
|
2
3
|
|
|
3
4
|
module Textus
|
|
4
5
|
class Manifest
|
|
@@ -10,10 +11,26 @@ module Textus
|
|
|
10
11
|
".txt" => "text",
|
|
11
12
|
}.freeze
|
|
12
13
|
|
|
14
|
+
TEXTUS_2_HINT = "Install textus 0.11.x to run the migrator, then upgrade to this version. " \
|
|
15
|
+
"See https://github.com/patrick204nqh/textus/blob/main/CHANGELOG.md#0110".freeze
|
|
16
|
+
|
|
17
|
+
def self.version_hint_for(version)
|
|
18
|
+
version == "textus/2" ? TEXTUS_2_HINT : nil
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
private_class_method :version_hint_for
|
|
22
|
+
|
|
13
23
|
attr_reader :root, :entries, :raw
|
|
14
24
|
|
|
15
25
|
def zones
|
|
16
|
-
@zones ||= Array(@raw["zones"]).to_h { |z| [z["name"], Array(z["
|
|
26
|
+
@zones ||= Array(@raw["zones"]).to_h { |z| [z["name"], Array(z["write_policy"])] }
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def zone_readers
|
|
30
|
+
@zone_readers ||= Array(@raw["zones"]).to_h do |z|
|
|
31
|
+
rp = z["read_policy"]
|
|
32
|
+
[z["name"], rp.nil? ? :all : Array(rp)]
|
|
33
|
+
end
|
|
17
34
|
end
|
|
18
35
|
|
|
19
36
|
def zone_writers(zone_name)
|
|
@@ -23,18 +40,35 @@ module Textus
|
|
|
23
40
|
def permission_for(zone_name)
|
|
24
41
|
Textus::Domain::Permission.new(
|
|
25
42
|
zone: zone_name,
|
|
26
|
-
|
|
27
|
-
|
|
43
|
+
write_policy: zone_writers(zone_name),
|
|
44
|
+
read_policy: zone_readers[zone_name] || :all,
|
|
28
45
|
)
|
|
29
46
|
end
|
|
30
47
|
|
|
48
|
+
def self.parse(yaml_text, root: ".")
|
|
49
|
+
raw = YAML.safe_load(yaml_text, aliases: false)
|
|
50
|
+
unless raw["version"] == PROTOCOL
|
|
51
|
+
raise BadFrontmatter.new(
|
|
52
|
+
"<string>",
|
|
53
|
+
"unsupported manifest version #{raw["version"].inspect}; expected #{PROTOCOL.inspect}",
|
|
54
|
+
hint: version_hint_for(raw["version"]),
|
|
55
|
+
)
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
new(root, raw)
|
|
59
|
+
end
|
|
60
|
+
|
|
31
61
|
def self.load(root)
|
|
32
62
|
manifest_path = File.join(root, "manifest.yaml")
|
|
33
63
|
raise IoError.new("manifest not found: #{manifest_path}") unless File.exist?(manifest_path)
|
|
34
64
|
|
|
35
65
|
raw = YAML.safe_load_file(manifest_path, aliases: false)
|
|
36
66
|
unless raw["version"] == PROTOCOL
|
|
37
|
-
raise BadFrontmatter.new(
|
|
67
|
+
raise BadFrontmatter.new(
|
|
68
|
+
manifest_path,
|
|
69
|
+
"unsupported manifest version #{raw["version"].inspect}; expected #{PROTOCOL.inspect}",
|
|
70
|
+
hint: version_hint_for(raw["version"]),
|
|
71
|
+
)
|
|
38
72
|
end
|
|
39
73
|
|
|
40
74
|
new(root, raw)
|
|
@@ -45,16 +79,18 @@ module Textus
|
|
|
45
79
|
@raw = raw
|
|
46
80
|
raise BadFrontmatter.new(File.join(root, "manifest.yaml"), "manifest must declare zones:") if Array(raw["zones"]).empty?
|
|
47
81
|
|
|
82
|
+
Schema.validate!(raw)
|
|
83
|
+
|
|
48
84
|
@entries = Array(raw["entries"]).map { |e| Manifest::Entry.new(self, e) }
|
|
49
85
|
validate_declared_keys!
|
|
50
86
|
end
|
|
51
87
|
|
|
52
|
-
def
|
|
53
|
-
@
|
|
88
|
+
def rules
|
|
89
|
+
@rules ||= Textus::Manifest::Rules.parse(@raw["rules"] || [])
|
|
54
90
|
end
|
|
55
91
|
|
|
56
|
-
def
|
|
57
|
-
|
|
92
|
+
def rules_for(key)
|
|
93
|
+
rules.for(key)
|
|
58
94
|
end
|
|
59
95
|
|
|
60
96
|
# Returns [Manifest::Entry, resolved_path, remaining_segments]
|
|
@@ -135,7 +171,7 @@ module Textus
|
|
|
135
171
|
|
|
136
172
|
illegal = segs.find { |s| !valid_segment?(s) }
|
|
137
173
|
if illegal
|
|
138
|
-
warn("textus: skipping illegal key segment '#{illegal}' at #{path} — run 'textus key
|
|
174
|
+
warn("textus: skipping illegal key segment '#{illegal}' at #{path} — run 'textus key normalize --dry-run'")
|
|
139
175
|
return nil
|
|
140
176
|
end
|
|
141
177
|
|
data/lib/textus/migrate_keys.rb
CHANGED
data/lib/textus/projection.rb
CHANGED
|
@@ -38,14 +38,14 @@ module Textus
|
|
|
38
38
|
private
|
|
39
39
|
|
|
40
40
|
def apply_reducer(rows)
|
|
41
|
-
name = @spec["
|
|
42
|
-
callable = @store.registry.rpc_callable(:
|
|
41
|
+
name = @spec["transform"] or return rows
|
|
42
|
+
callable = @store.registry.rpc_callable(:transform_rows, name)
|
|
43
43
|
view = Application::Context.new(store: @store, role: "human")
|
|
44
44
|
Timeout.timeout(REDUCER_TIMEOUT_SECONDS) do
|
|
45
|
-
callable.call(store: view, rows: rows, config: @spec["
|
|
45
|
+
callable.call(store: view, rows: rows, config: @spec["transform_config"] || {})
|
|
46
46
|
end
|
|
47
47
|
rescue Timeout::Error
|
|
48
|
-
raise UsageError.new("
|
|
48
|
+
raise UsageError.new("transform_rows '#{name}' exceeded #{REDUCER_TIMEOUT_SECONDS}s timeout")
|
|
49
49
|
end
|
|
50
50
|
|
|
51
51
|
def collect_keys
|
data/lib/textus/refresh.rb
CHANGED
|
@@ -5,7 +5,7 @@ module Textus
|
|
|
5
5
|
Textus::Composition.refresh_worker(ctx).run(key)
|
|
6
6
|
end
|
|
7
7
|
|
|
8
|
-
def self.refresh_stale(store, prefix: nil, zone: nil, as: "
|
|
8
|
+
def self.refresh_stale(store, prefix: nil, zone: nil, as: "runner")
|
|
9
9
|
ctx = Textus::Composition.context(store, role: as)
|
|
10
10
|
Textus::Application::Refresh::All.call(ctx, prefix: prefix, zone: zone)
|
|
11
11
|
end
|
data/lib/textus/store/mover.rb
CHANGED
|
@@ -13,7 +13,7 @@ module Textus
|
|
|
13
13
|
def rows_for(mentry)
|
|
14
14
|
return [] unless mentry.intake_handler
|
|
15
15
|
|
|
16
|
-
ttl = @manifest.
|
|
16
|
+
ttl = @manifest.rules_for(mentry.key).refresh&.ttl_seconds
|
|
17
17
|
return [] unless ttl
|
|
18
18
|
|
|
19
19
|
path = Textus::Key::Path.resolve(@manifest, mentry)
|
data/lib/textus/store/writer.rb
CHANGED
|
@@ -163,7 +163,7 @@ module Textus
|
|
|
163
163
|
target_key = proposal["target_key"] or raise ProposalError.new("proposal missing target_key")
|
|
164
164
|
|
|
165
165
|
delete(pending_key, as: as)
|
|
166
|
-
@store.fire_event(:
|
|
166
|
+
@store.fire_event(:proposal_rejected, key: pending_key, target_key: target_key)
|
|
167
167
|
{ "protocol" => PROTOCOL, "rejected" => pending_key, "target_key" => target_key }
|
|
168
168
|
end
|
|
169
169
|
end
|
data/lib/textus/store.rb
CHANGED
data/lib/textus/version.rb
CHANGED
data/lib/textus.rb
CHANGED
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: textus
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.12.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Patrick
|
|
@@ -133,7 +133,8 @@ files:
|
|
|
133
133
|
- lib/textus/cli/group.rb
|
|
134
134
|
- lib/textus/cli/group/hook.rb
|
|
135
135
|
- lib/textus/cli/group/key.rb
|
|
136
|
-
- lib/textus/cli/group/
|
|
136
|
+
- lib/textus/cli/group/refresh.rb
|
|
137
|
+
- lib/textus/cli/group/rule.rb
|
|
137
138
|
- lib/textus/cli/group/schema.rb
|
|
138
139
|
- lib/textus/cli/verb.rb
|
|
139
140
|
- lib/textus/cli/verb/accept.rb
|
|
@@ -149,17 +150,17 @@ files:
|
|
|
149
150
|
- lib/textus/cli/verb/hooks.rb
|
|
150
151
|
- lib/textus/cli/verb/init.rb
|
|
151
152
|
- lib/textus/cli/verb/intro.rb
|
|
153
|
+
- lib/textus/cli/verb/key_normalize.rb
|
|
152
154
|
- lib/textus/cli/verb/list.rb
|
|
153
|
-
- lib/textus/cli/verb/migrate_keys.rb
|
|
154
155
|
- lib/textus/cli/verb/mv.rb
|
|
155
|
-
- lib/textus/cli/verb/policy_explain.rb
|
|
156
|
-
- lib/textus/cli/verb/policy_list.rb
|
|
157
156
|
- lib/textus/cli/verb/published.rb
|
|
158
157
|
- lib/textus/cli/verb/put.rb
|
|
159
158
|
- lib/textus/cli/verb/rdeps.rb
|
|
160
159
|
- lib/textus/cli/verb/refresh.rb
|
|
161
160
|
- lib/textus/cli/verb/refresh_stale.rb
|
|
162
161
|
- lib/textus/cli/verb/reject.rb
|
|
162
|
+
- lib/textus/cli/verb/rule_explain.rb
|
|
163
|
+
- lib/textus/cli/verb/rule_list.rb
|
|
163
164
|
- lib/textus/cli/verb/schema.rb
|
|
164
165
|
- lib/textus/cli/verb/schema_diff.rb
|
|
165
166
|
- lib/textus/cli/verb/schema_init.rb
|
|
@@ -176,7 +177,8 @@ files:
|
|
|
176
177
|
- lib/textus/doctor/check/illegal_keys.rb
|
|
177
178
|
- lib/textus/doctor/check/intake_registration.rb
|
|
178
179
|
- lib/textus/doctor/check/manifest_files.rb
|
|
179
|
-
- lib/textus/doctor/check/
|
|
180
|
+
- lib/textus/doctor/check/protocol_version.rb
|
|
181
|
+
- lib/textus/doctor/check/rule_ambiguity.rb
|
|
180
182
|
- lib/textus/doctor/check/schema_parse_error.rb
|
|
181
183
|
- lib/textus/doctor/check/schema_violations.rb
|
|
182
184
|
- lib/textus/doctor/check/schemas.rb
|
|
@@ -192,7 +194,10 @@ files:
|
|
|
192
194
|
- lib/textus/domain/policy.rb
|
|
193
195
|
- lib/textus/domain/policy/handler_allowlist.rb
|
|
194
196
|
- lib/textus/domain/policy/matcher.rb
|
|
197
|
+
- lib/textus/domain/policy/predicates/human_accept.rb
|
|
198
|
+
- lib/textus/domain/policy/predicates/schema_valid.rb
|
|
195
199
|
- lib/textus/domain/policy/promote.rb
|
|
200
|
+
- lib/textus/domain/policy/promotion.rb
|
|
196
201
|
- lib/textus/domain/policy/refresh.rb
|
|
197
202
|
- lib/textus/entry.rb
|
|
198
203
|
- lib/textus/entry/base.rb
|
|
@@ -220,7 +225,8 @@ files:
|
|
|
220
225
|
- lib/textus/key/path.rb
|
|
221
226
|
- lib/textus/manifest.rb
|
|
222
227
|
- lib/textus/manifest/entry.rb
|
|
223
|
-
- lib/textus/manifest/
|
|
228
|
+
- lib/textus/manifest/rules.rb
|
|
229
|
+
- lib/textus/manifest/schema.rb
|
|
224
230
|
- lib/textus/migrate_keys.rb
|
|
225
231
|
- lib/textus/mustache.rb
|
|
226
232
|
- lib/textus/projection.rb
|