textus 0.55.2 → 0.56.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +175 -2768
- data/README.md +8 -9
- data/lib/textus/artifact.rb +8 -0
- data/lib/textus/errors.rb +0 -39
- data/lib/textus/human.rb +7 -0
- data/lib/textus/infra/adapters/concurrency_adapter.rb +15 -0
- data/lib/textus/infra/clock.rb +7 -0
- data/lib/textus/infra/database.rb +127 -0
- data/lib/textus/infra/file_system.rb +54 -0
- data/lib/textus/infra/locks.rb +50 -0
- data/lib/textus/infra/port/clock.rb +18 -0
- data/lib/textus/infra/port/publisher.rb +71 -0
- data/lib/textus/infra/port/sentinel.rb +25 -0
- data/lib/textus/infra/port/sentinel_store.rb +93 -0
- data/lib/textus/infra/store/audit_log.rb +42 -0
- data/lib/textus/infra/store/base.rb +25 -0
- data/lib/textus/infra/store/cursor_store.rb +27 -0
- data/lib/textus/infra/store/entry_index.rb +31 -0
- data/lib/textus/infra/store/envelope/meta.rb +63 -0
- data/lib/textus/infra/store/file_store.rb +23 -0
- data/lib/textus/infra/store/index/builder.rb +28 -0
- data/lib/textus/infra/store/index/lookup.rb +23 -0
- data/lib/textus/infra/store/link_store.rb +43 -0
- data/lib/textus/infra/store/workflow_queue.rb +54 -0
- data/lib/textus/infra/store.rb +29 -0
- data/lib/textus/ingest.rb +8 -0
- data/lib/textus/lanes/artifact/cache.rb +35 -0
- data/lib/textus/lanes/artifact/handlers.rb +29 -0
- data/lib/textus/lanes/artifact/lifecycle.rb +23 -0
- data/lib/textus/lanes/artifact/verbs.rb +18 -0
- data/lib/textus/lanes/ingest/dedup.rb +17 -0
- data/lib/textus/lanes/ingest/entry_types/asset.rb +45 -0
- data/lib/textus/lanes/ingest/entry_types/link.rb +16 -0
- data/lib/textus/lanes/ingest/entry_types/text.rb +17 -0
- data/lib/textus/lanes/ingest/entry_types.rb +25 -0
- data/lib/textus/lanes/ingest/handlers.rb +54 -0
- data/lib/textus/lanes/ingest/index_rebuilder.rb +13 -0
- data/lib/textus/lanes/ingest/key_builder.rb +33 -0
- data/lib/textus/lanes/ingest/resolver.rb +21 -0
- data/lib/textus/lanes/knowledge/freeform.rb +15 -0
- data/lib/textus/lanes/knowledge/handlers.rb +53 -0
- data/lib/textus/lanes/knowledge/runner.rb +45 -0
- data/lib/textus/lanes/knowledge/verbs.rb +16 -0
- data/lib/textus/lanes/scratchpad/handlers.rb +164 -0
- data/lib/textus/lanes/scratchpad/verbs.rb +73 -0
- data/lib/textus/{dispatch → protocol}/binder.rb +1 -7
- data/lib/textus/protocol/boot/catalog.rb +34 -0
- data/lib/textus/protocol/boot/context.rb +124 -0
- data/lib/textus/protocol/boot/protocol.rb +33 -0
- data/lib/textus/protocol/boot.rb +42 -0
- data/lib/textus/protocol/builder/domain.rb +17 -0
- data/lib/textus/protocol/builder/infrastructure.rb +17 -0
- data/lib/textus/protocol/builder/runtime.rb +46 -0
- data/lib/textus/protocol/builder.rb +11 -0
- data/lib/textus/protocol/container.rb +22 -0
- data/lib/textus/protocol/diff.rb +170 -0
- data/lib/textus/protocol/entry_constraint.rb +54 -0
- data/lib/textus/protocol/envelope.rb +70 -0
- data/lib/textus/protocol/format/base.rb +80 -0
- data/lib/textus/protocol/format/bash.rb +10 -0
- data/lib/textus/protocol/format/javascript.rb +10 -0
- data/lib/textus/protocol/format/json.rb +82 -0
- data/lib/textus/protocol/format/markdown.rb +53 -0
- data/lib/textus/protocol/format/python.rb +10 -0
- data/lib/textus/protocol/format/ruby.rb +10 -0
- data/lib/textus/protocol/format/script.rb +55 -0
- data/lib/textus/protocol/format/text.rb +48 -0
- data/lib/textus/protocol/format/yaml.rb +105 -0
- data/lib/textus/protocol/format.rb +73 -0
- data/lib/textus/{store → protocol}/freshness/ttl_evaluator.rb +7 -3
- data/lib/textus/protocol/gate.rb +61 -0
- data/lib/textus/protocol/handlers/command_context.rb +19 -0
- data/lib/textus/protocol/handlers/concern.rb +15 -0
- data/lib/textus/protocol/handlers/ops/audit_filter.rb +37 -0
- data/lib/textus/protocol/handlers/ops/bulk_planner.rb +38 -0
- data/lib/textus/protocol/handlers/ops/data_mv.rb +49 -0
- data/lib/textus/protocol/handlers/ops/git_blame.rb +65 -0
- data/lib/textus/protocol/handlers/ops/pulse_coordinator.rb +37 -0
- data/lib/textus/protocol/handlers/ops/source_expander.rb +41 -0
- data/lib/textus/protocol/handlers/query_context.rb +41 -0
- data/lib/textus/protocol/handlers/read.rb +67 -0
- data/lib/textus/protocol/handlers/system.rb +93 -0
- data/lib/textus/protocol/handlers/write.rb +57 -0
- data/lib/textus/protocol/init.rb +143 -0
- data/lib/textus/protocol/key/grammar.rb +35 -0
- data/lib/textus/protocol/key/matching.rb +23 -0
- data/lib/textus/protocol/key/path.rb +23 -0
- data/lib/textus/protocol/key.rb +25 -0
- data/lib/textus/protocol/lane.rb +12 -0
- data/lib/textus/{store → protocol}/layout.rb +16 -22
- data/lib/textus/protocol/links/resolver.rb +33 -0
- data/lib/textus/protocol/links/uri_rewriter.rb +30 -0
- data/lib/textus/protocol/links.rb +10 -0
- data/lib/textus/protocol/manifest/data.rb +90 -0
- data/lib/textus/protocol/manifest/entry/base.rb +95 -0
- data/lib/textus/protocol/manifest/entry/leaf.rb +19 -0
- data/lib/textus/protocol/manifest/entry/nested.rb +19 -0
- data/lib/textus/protocol/manifest/entry/parser.rb +71 -0
- data/lib/textus/protocol/manifest/entry/publish.rb +209 -0
- data/lib/textus/protocol/manifest/entry.rb +10 -0
- data/lib/textus/protocol/manifest/policy/publish_target.rb +36 -0
- data/lib/textus/protocol/manifest/policy.rb +69 -0
- data/lib/textus/protocol/manifest/resolver.rb +156 -0
- data/lib/textus/protocol/manifest/schema/contract.rb +53 -0
- data/lib/textus/protocol/manifest/schema/validator.rb +52 -0
- data/lib/textus/protocol/manifest/schema.rb +40 -0
- data/lib/textus/protocol/manifest.rb +62 -0
- data/lib/textus/protocol/produce/render/context.rb +43 -0
- data/lib/textus/protocol/produce/render.rb +44 -0
- data/lib/textus/protocol/schema/registry.rb +44 -0
- data/lib/textus/protocol/schema/tools.rb +104 -0
- data/lib/textus/protocol/schema.rb +117 -0
- data/lib/textus/protocol/session/bridge.rb +11 -0
- data/lib/textus/protocol/session/context.rb +5 -0
- data/lib/textus/protocol/session.rb +87 -0
- data/lib/textus/protocol/store_engine/delete.rb +37 -0
- data/lib/textus/protocol/store_engine/move.rb +109 -0
- data/lib/textus/protocol/store_engine/pipeline.rb +61 -0
- data/lib/textus/protocol/store_engine/put.rb +104 -0
- data/lib/textus/protocol/store_engine.rb +86 -0
- data/lib/textus/protocol/v4.rb +25 -0
- data/lib/textus/protocol/verb_registry/arg_spec.rb +19 -0
- data/lib/textus/protocol/verb_registry/core_verbs.rb +164 -0
- data/lib/textus/protocol/verb_registry/verb_spec.rb +42 -0
- data/lib/textus/protocol/verb_registry.rb +42 -0
- data/lib/textus/surface/cli/group/{rule.rb → web.rb} +2 -2
- data/lib/textus/surface/cli/runner.rb +22 -21
- data/lib/textus/surface/cli/sources.rb +1 -3
- data/lib/textus/surface/cli/verb/get.rb +2 -7
- data/lib/textus/surface/cli/verb/init.rb +1 -1
- data/lib/textus/surface/cli/verb/put.rb +7 -7
- data/lib/textus/surface/cli/verb/web_serve.rb +22 -0
- data/lib/textus/surface/cli.rb +25 -6
- data/lib/textus/surface/dispatch.rb +17 -0
- data/lib/textus/surface/mcp/adapters/mcp_adapter.rb +28 -0
- data/lib/textus/surface/mcp/catalog.rb +12 -12
- data/lib/textus/surface/mcp/cursor_manager.rb +22 -0
- data/lib/textus/surface/mcp/drift_annotation.rb +19 -0
- data/lib/textus/surface/mcp/server.rb +35 -24
- data/lib/textus/surface/watcher.rb +18 -12
- data/lib/textus/surface/web.rb +50 -0
- data/lib/textus/value/call.rb +1 -1
- data/lib/textus/value/etag.rb +7 -7
- data/lib/textus/version.rb +1 -1
- data/lib/textus/workflow/consumer.rb +39 -0
- data/lib/textus/workflow/dsl/lifecycle.rb +32 -0
- data/lib/textus/workflow/dsl/pattern_matcher.rb +20 -0
- data/lib/textus/workflow/dsl/step_builder.rb +56 -0
- data/lib/textus/workflow/dsl.rb +111 -21
- data/lib/textus/workflow/event_emitter.rb +21 -0
- data/lib/textus/workflow/publisher.rb +59 -0
- data/lib/textus/workflow/registry.rb +31 -8
- data/lib/textus/workflow/runner.rb +25 -53
- data/lib/textus/workflow/scheduler.rb +45 -0
- data/lib/textus/workflow/step_executor.rb +78 -0
- data/lib/textus/workflow/step_helpers.rb +102 -0
- data/lib/textus/workflow.rb +2 -5
- data/lib/textus.rb +16 -16
- metadata +169 -185
- data/SPEC.md +0 -909
- data/docs/architecture/README.md +0 -273
- data/docs/reference/conventions.md +0 -141
- data/lib/textus/boot.rb +0 -181
- data/lib/textus/dispatch/contracts.rb +0 -63
- data/lib/textus/dispatch/handler_registry.rb +0 -21
- data/lib/textus/dispatch/middleware/audit_index.rb +0 -51
- data/lib/textus/dispatch/middleware/auth.rb +0 -40
- data/lib/textus/dispatch/middleware/base.rb +0 -26
- data/lib/textus/dispatch/middleware/binder.rb +0 -20
- data/lib/textus/dispatch/middleware/cascade.rb +0 -53
- data/lib/textus/dispatch/pipeline.rb +0 -35
- data/lib/textus/doctor/check/audit_log.rb +0 -34
- data/lib/textus/doctor/check/generator_drift.rb +0 -29
- data/lib/textus/doctor/check/illegal_keys.rb +0 -60
- data/lib/textus/doctor/check/manifest_files.rb +0 -25
- data/lib/textus/doctor/check/orphaned_publish_targets.rb +0 -35
- data/lib/textus/doctor/check/proposal_targets.rb +0 -45
- data/lib/textus/doctor/check/protocol_version.rb +0 -47
- data/lib/textus/doctor/check/publish_tree_index_overlap.rb +0 -48
- data/lib/textus/doctor/check/raw_asset_paths.rb +0 -50
- data/lib/textus/doctor/check/rule_ambiguity.rb +0 -49
- data/lib/textus/doctor/check/schema_parse_error.rb +0 -28
- data/lib/textus/doctor/check/schema_violations.rb +0 -28
- data/lib/textus/doctor/check/schemas.rb +0 -26
- data/lib/textus/doctor/check/scratchpad_sources.rb +0 -55
- data/lib/textus/doctor/check/sentinels.rb +0 -60
- data/lib/textus/doctor/check/stale_reviewed_stamp.rb +0 -54
- data/lib/textus/doctor/check/templates.rb +0 -29
- data/lib/textus/doctor/check/unowned_schema_fields.rb +0 -40
- data/lib/textus/doctor/check.rb +0 -43
- data/lib/textus/doctor/validator.rb +0 -93
- data/lib/textus/doctor.rb +0 -54
- data/lib/textus/format/base.rb +0 -77
- data/lib/textus/format/json.rb +0 -81
- data/lib/textus/format/markdown.rb +0 -59
- data/lib/textus/format/text.rb +0 -55
- data/lib/textus/format/yaml.rb +0 -104
- data/lib/textus/format.rb +0 -61
- data/lib/textus/handlers/maintenance/boot_store.rb +0 -15
- data/lib/textus/handlers/maintenance/doctor_store.rb +0 -15
- data/lib/textus/handlers/maintenance/drain_store.rb +0 -21
- data/lib/textus/handlers/maintenance/ingest_entry.rb +0 -159
- data/lib/textus/handlers/maintenance/jobs_action.rb +0 -21
- data/lib/textus/handlers/maintenance/published_entries.rb +0 -17
- data/lib/textus/handlers/maintenance/rule_explain.rb +0 -77
- data/lib/textus/handlers/maintenance/rule_lint.rb +0 -54
- data/lib/textus/handlers/maintenance/rule_list.rb +0 -32
- data/lib/textus/handlers/maintenance/schema_envelope.rb +0 -19
- data/lib/textus/handlers/read/audit_entries.rb +0 -48
- data/lib/textus/handlers/read/blame_entry.rb +0 -71
- data/lib/textus/handlers/read/deps_entry.rb +0 -17
- data/lib/textus/handlers/read/get_entry.rb +0 -68
- data/lib/textus/handlers/read/list_keys.rb +0 -36
- data/lib/textus/handlers/read/pulse_entries.rb +0 -66
- data/lib/textus/handlers/read/rdeps_entry.rb +0 -21
- data/lib/textus/handlers/read/uid_entry.rb +0 -18
- data/lib/textus/handlers/read/where_entry.rb +0 -18
- data/lib/textus/handlers/write/accept_proposal.rb +0 -39
- data/lib/textus/handlers/write/data_mv.rb +0 -55
- data/lib/textus/handlers/write/delete_key.rb +0 -17
- data/lib/textus/handlers/write/enqueue_job.rb +0 -27
- data/lib/textus/handlers/write/key_delete_prefix.rb +0 -32
- data/lib/textus/handlers/write/key_mv_prefix.rb +0 -45
- data/lib/textus/handlers/write/move_key.rb +0 -80
- data/lib/textus/handlers/write/propose_entry.rb +0 -29
- data/lib/textus/handlers/write/put_entry.rb +0 -29
- data/lib/textus/handlers/write/reject_proposal.rb +0 -29
- data/lib/textus/init.rb +0 -157
- data/lib/textus/jobs.rb +0 -9
- data/lib/textus/key/distance.rb +0 -55
- data/lib/textus/key/grammar.rb +0 -33
- data/lib/textus/key/matching.rb +0 -24
- data/lib/textus/key/path.rb +0 -28
- data/lib/textus/manifest/capabilities.rb +0 -29
- data/lib/textus/manifest/data.rb +0 -102
- data/lib/textus/manifest/entry/base.rb +0 -105
- data/lib/textus/manifest/entry/ignore_matcher.rb +0 -46
- data/lib/textus/manifest/entry/leaf.rb +0 -17
- data/lib/textus/manifest/entry/nested.rb +0 -37
- data/lib/textus/manifest/entry/parser.rb +0 -99
- data/lib/textus/manifest/entry/produced.rb +0 -36
- data/lib/textus/manifest/entry/publish/mode.rb +0 -45
- data/lib/textus/manifest/entry/publish/none.rb +0 -14
- data/lib/textus/manifest/entry/publish/subtree_mirror.rb +0 -73
- data/lib/textus/manifest/entry/publish/template.rb +0 -16
- data/lib/textus/manifest/entry/publish/to_paths.rb +0 -76
- data/lib/textus/manifest/entry/publish/tree.rb +0 -50
- data/lib/textus/manifest/entry/publish.rb +0 -45
- data/lib/textus/manifest/entry/validators/format_matrix.rb +0 -21
- data/lib/textus/manifest/entry/validators/ignore.rb +0 -28
- data/lib/textus/manifest/entry/validators/publish.rb +0 -30
- data/lib/textus/manifest/entry/validators.rb +0 -18
- data/lib/textus/manifest/entry.rb +0 -8
- data/lib/textus/manifest/policy/matcher.rb +0 -51
- data/lib/textus/manifest/policy/predicates/author_held.rb +0 -22
- data/lib/textus/manifest/policy/predicates/etag_match.rb +0 -18
- data/lib/textus/manifest/policy/predicates/fresh_within.rb +0 -13
- data/lib/textus/manifest/policy/predicates/lane_deletable_by.rb +0 -31
- data/lib/textus/manifest/policy/predicates/lane_writable_by.rb +0 -23
- data/lib/textus/manifest/policy/predicates/raw_lane_ingest_only.rb +0 -25
- data/lib/textus/manifest/policy/predicates/raw_write_once.rb +0 -24
- data/lib/textus/manifest/policy/predicates/schema_valid.rb +0 -41
- data/lib/textus/manifest/policy/predicates/target_is_canon.rb +0 -20
- data/lib/textus/manifest/policy/predicates.rb +0 -54
- data/lib/textus/manifest/policy/publish_target.rb +0 -34
- data/lib/textus/manifest/policy/react.rb +0 -30
- data/lib/textus/manifest/policy/retention.rb +0 -29
- data/lib/textus/manifest/policy/source.rb +0 -30
- data/lib/textus/manifest/policy.rb +0 -95
- data/lib/textus/manifest/resolver.rb +0 -118
- data/lib/textus/manifest/rules.rb +0 -86
- data/lib/textus/manifest/schema/contract.rb +0 -61
- data/lib/textus/manifest/schema/keys.rb +0 -90
- data/lib/textus/manifest/schema/semantics/cross_field.rb +0 -53
- data/lib/textus/manifest/schema/semantics/invariants.rb +0 -125
- data/lib/textus/manifest/schema/semantics/migration.rb +0 -83
- data/lib/textus/manifest/schema/semantics.rb +0 -27
- data/lib/textus/manifest/schema/validator.rb +0 -48
- data/lib/textus/manifest/schema/vocabulary.rb +0 -25
- data/lib/textus/manifest/schema.rb +0 -30
- data/lib/textus/manifest.rb +0 -65
- data/lib/textus/orchestration.rb +0 -55
- data/lib/textus/port/audit_log.rb +0 -268
- data/lib/textus/port/build_lock.rb +0 -64
- data/lib/textus/port/clock.rb +0 -10
- data/lib/textus/port/publisher.rb +0 -60
- data/lib/textus/port/sentinel.rb +0 -25
- data/lib/textus/port/sentinel_store.rb +0 -83
- data/lib/textus/port/storage/file_stat.rb +0 -19
- data/lib/textus/port/storage/file_store.rb +0 -49
- data/lib/textus/port/storage/interface.rb +0 -17
- data/lib/textus/port/store.rb +0 -149
- data/lib/textus/port/watcher_lock.rb +0 -48
- data/lib/textus/produce/engine.rb +0 -40
- data/lib/textus/produce/publisher.rb +0 -21
- data/lib/textus/produce/render.rb +0 -20
- data/lib/textus/schema/registry.rb +0 -42
- data/lib/textus/schema/tools.rb +0 -108
- data/lib/textus/schema.rb +0 -100
- data/lib/textus/store/container.rb +0 -173
- data/lib/textus/store/cursor.rb +0 -26
- data/lib/textus/store/entry/reader.rb +0 -47
- data/lib/textus/store/entry/writer.rb +0 -219
- data/lib/textus/store/envelope/meta.rb +0 -61
- data/lib/textus/store/freshness/drift_detector.rb +0 -93
- data/lib/textus/store/freshness/evaluator.rb +0 -20
- data/lib/textus/store/freshness/verdict.rb +0 -24
- data/lib/textus/store/freshness.rb +0 -8
- data/lib/textus/store/index/builder.rb +0 -76
- data/lib/textus/store/index/lookup.rb +0 -60
- data/lib/textus/store/jobs/base.rb +0 -13
- data/lib/textus/store/jobs/index.rb +0 -15
- data/lib/textus/store/jobs/materialize.rb +0 -15
- data/lib/textus/store/jobs/plan.rb +0 -11
- data/lib/textus/store/jobs/planner.rb +0 -124
- data/lib/textus/store/jobs/queue.rb +0 -162
- data/lib/textus/store/jobs/registry.rb +0 -19
- data/lib/textus/store/jobs/retention/base.rb +0 -52
- data/lib/textus/store/jobs/retention/sweep.rb +0 -55
- data/lib/textus/store/jobs/retention.rb +0 -8
- data/lib/textus/store/jobs/sweep.rb +0 -21
- data/lib/textus/store/jobs/worker.rb +0 -64
- data/lib/textus/store.rb +0 -122
- data/lib/textus/surface/cli/verb/doctor.rb +0 -20
- data/lib/textus/surface/cli/verb/schema_diff.rb +0 -17
- data/lib/textus/surface/cli/verb/schema_init.rb +0 -21
- data/lib/textus/surface/cli/verb/schema_migrate.rb +0 -21
- data/lib/textus/surface/mcp/projector.rb +0 -20
- data/lib/textus/surface/mcp.rb +0 -8
- data/lib/textus/value/command.rb +0 -16
- data/lib/textus/value/envelope.rb +0 -90
- data/lib/textus/value/result.rb +0 -46
- data/lib/textus/verb_registry.rb +0 -417
- data/lib/textus/workflow/collector.rb +0 -27
- data/lib/textus/workflow/context.rb +0 -5
- data/lib/textus/workflow/loader.rb +0 -17
- data/lib/textus/workflow/pattern.rb +0 -18
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
module Textus
|
|
2
|
-
module Handlers
|
|
3
|
-
module Maintenance
|
|
4
|
-
class RuleList
|
|
5
|
-
def initialize(manifest:)
|
|
6
|
-
@manifest = manifest
|
|
7
|
-
end
|
|
8
|
-
|
|
9
|
-
def call(_command, _call)
|
|
10
|
-
Value::Result.success(@manifest.rules.blocks.map do |block|
|
|
11
|
-
row = { "match" => block.match }
|
|
12
|
-
LIST_FIELDS.each do |field|
|
|
13
|
-
value = block.public_send(field)
|
|
14
|
-
row[field.to_s] = serialize(field, value) unless value.nil?
|
|
15
|
-
end
|
|
16
|
-
row
|
|
17
|
-
end)
|
|
18
|
-
end
|
|
19
|
-
|
|
20
|
-
LIST_FIELDS = Textus::Manifest::Schema::FIELD_REGISTRY.select { |_, m| m[:in_rule_list] }.keys.freeze
|
|
21
|
-
|
|
22
|
-
def serialize(field, value)
|
|
23
|
-
case field
|
|
24
|
-
when :retention then { "ttl_seconds" => value.ttl_seconds, "action" => value.action.to_s }
|
|
25
|
-
when :react then value.to_h
|
|
26
|
-
else value
|
|
27
|
-
end
|
|
28
|
-
end
|
|
29
|
-
end
|
|
30
|
-
end
|
|
31
|
-
end
|
|
32
|
-
end
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
module Textus
|
|
2
|
-
module Handlers
|
|
3
|
-
module Maintenance
|
|
4
|
-
class SchemaEnvelope
|
|
5
|
-
def initialize(manifest:, schemas:)
|
|
6
|
-
@manifest = manifest
|
|
7
|
-
@schemas = schemas
|
|
8
|
-
end
|
|
9
|
-
|
|
10
|
-
def call(command, _call)
|
|
11
|
-
mentry = @manifest.resolver.resolve(command.key).entry
|
|
12
|
-
schema = @schemas.fetch_or_nil(mentry.schema)
|
|
13
|
-
Value::Result.success("protocol" => Textus::PROTOCOL, "key" => command.key,
|
|
14
|
-
"schema_ref" => mentry.schema, "schema" => schema&.to_h)
|
|
15
|
-
end
|
|
16
|
-
end
|
|
17
|
-
end
|
|
18
|
-
end
|
|
19
|
-
end
|
|
@@ -1,48 +0,0 @@
|
|
|
1
|
-
module Textus
|
|
2
|
-
module Handlers
|
|
3
|
-
module Read
|
|
4
|
-
class AuditEntries
|
|
5
|
-
def initialize(manifest:, audit_log:)
|
|
6
|
-
@manifest = manifest
|
|
7
|
-
@audit_log = audit_log
|
|
8
|
-
end
|
|
9
|
-
|
|
10
|
-
def call(command, _call)
|
|
11
|
-
cursor_check = check_cursor_expiry(command.seq_since)
|
|
12
|
-
return cursor_check if cursor_check
|
|
13
|
-
|
|
14
|
-
rows = @audit_log.scan(
|
|
15
|
-
seq_since: command.seq_since,
|
|
16
|
-
key: command.key, role: command.role, verb: command.verb,
|
|
17
|
-
correlation_id: command.correlation_id, limit: command.limit
|
|
18
|
-
).select do |row|
|
|
19
|
-
next false if command.lane && !key_in_lane?(row["key"], command.lane)
|
|
20
|
-
next false if command.since && (row["ts"].nil? || Time.parse(row["ts"]) < command.since)
|
|
21
|
-
|
|
22
|
-
true
|
|
23
|
-
end
|
|
24
|
-
Value::Result.success(rows)
|
|
25
|
-
end
|
|
26
|
-
|
|
27
|
-
private
|
|
28
|
-
|
|
29
|
-
def check_cursor_expiry(seq_since)
|
|
30
|
-
return unless seq_since
|
|
31
|
-
|
|
32
|
-
min = @audit_log.min_available_seq
|
|
33
|
-
return unless min && seq_since < min - 1
|
|
34
|
-
|
|
35
|
-
Value::Result.failure(:cursor_expired, "requested seq #{seq_since} is below minimum available #{min}",
|
|
36
|
-
details: { requested: seq_since, min_available: min })
|
|
37
|
-
end
|
|
38
|
-
|
|
39
|
-
def key_in_lane?(key, lane)
|
|
40
|
-
mentry = @manifest.resolver.resolve(key).entry
|
|
41
|
-
mentry && mentry.lane == lane
|
|
42
|
-
rescue Textus::Error
|
|
43
|
-
false
|
|
44
|
-
end
|
|
45
|
-
end
|
|
46
|
-
end
|
|
47
|
-
end
|
|
48
|
-
end
|
|
@@ -1,71 +0,0 @@
|
|
|
1
|
-
module Textus
|
|
2
|
-
module Handlers
|
|
3
|
-
module Read
|
|
4
|
-
class BlameEntry
|
|
5
|
-
def initialize(manifest:, orchestration:)
|
|
6
|
-
@manifest = manifest
|
|
7
|
-
@orchestration = orchestration
|
|
8
|
-
end
|
|
9
|
-
|
|
10
|
-
def call(command, call)
|
|
11
|
-
root = @manifest.data.root
|
|
12
|
-
audit = @orchestration.audit_entries(key: command.key, limit: command.limit, call: call)
|
|
13
|
-
return audit if audit.failure?
|
|
14
|
-
|
|
15
|
-
audit_rows = audit.value.fetch("rows")
|
|
16
|
-
|
|
17
|
-
path = resolve_path(command.key)
|
|
18
|
-
return Value::Result.success(audit_rows.map { |row| row.merge("git" => nil) }) unless git_tracked?(path, root: root)
|
|
19
|
-
|
|
20
|
-
Value::Result.success(audit_rows.map { |row| row.merge("git" => git_commit_at(path, timestamp: row["ts"], root: root)) })
|
|
21
|
-
end
|
|
22
|
-
|
|
23
|
-
private
|
|
24
|
-
|
|
25
|
-
def resolve_path(key)
|
|
26
|
-
res = @manifest.resolver.resolve(key)
|
|
27
|
-
path = res.path
|
|
28
|
-
path || Textus::Key::Path.resolve(@manifest.data, res.entry)
|
|
29
|
-
rescue Textus::Error
|
|
30
|
-
nil
|
|
31
|
-
end
|
|
32
|
-
|
|
33
|
-
def git_tracked?(path, root:)
|
|
34
|
-
return false if path.nil? || !File.exist?(path) || !git_repo?(root)
|
|
35
|
-
|
|
36
|
-
_out, _err, status = Open3.capture3("git", "ls-files", "--error-unmatch", path, chdir: root)
|
|
37
|
-
status.success?
|
|
38
|
-
rescue Errno::ENOENT
|
|
39
|
-
false
|
|
40
|
-
end
|
|
41
|
-
|
|
42
|
-
def git_repo?(root)
|
|
43
|
-
dir = root
|
|
44
|
-
loop do
|
|
45
|
-
return true if File.directory?(File.join(dir, ".git"))
|
|
46
|
-
|
|
47
|
-
parent = File.dirname(dir)
|
|
48
|
-
return false if parent == dir
|
|
49
|
-
|
|
50
|
-
dir = parent
|
|
51
|
-
end
|
|
52
|
-
end
|
|
53
|
-
|
|
54
|
-
def git_commit_at(path, timestamp:, root:)
|
|
55
|
-
args = ["git", "log", "-1"]
|
|
56
|
-
args << "--before=#{timestamp}" if timestamp
|
|
57
|
-
args += ["--format=%H%x09%an%x09%aI%x09%s", "--", path]
|
|
58
|
-
out, _err, status = Open3.capture3(*args, chdir: root)
|
|
59
|
-
return nil unless status.success?
|
|
60
|
-
|
|
61
|
-
sha, author, date, subject = out.strip.split("\t", 4)
|
|
62
|
-
return nil if sha.nil? || sha.empty?
|
|
63
|
-
|
|
64
|
-
{ "sha" => sha, "author" => author, "date" => date, "subject" => subject }
|
|
65
|
-
rescue Errno::ENOENT
|
|
66
|
-
nil
|
|
67
|
-
end
|
|
68
|
-
end
|
|
69
|
-
end
|
|
70
|
-
end
|
|
71
|
-
end
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
module Textus
|
|
2
|
-
module Handlers
|
|
3
|
-
module Read
|
|
4
|
-
class DepsEntry
|
|
5
|
-
def initialize(manifest:)
|
|
6
|
-
@manifest = manifest
|
|
7
|
-
end
|
|
8
|
-
|
|
9
|
-
def call(command, _call)
|
|
10
|
-
entry = @manifest.data.entries.find { |e| e.key == command.key }
|
|
11
|
-
deps = entry&.external? ? Array(entry.source&.sources).compact : []
|
|
12
|
-
Value::Result.success("key" => command.key, "deps" => deps.uniq)
|
|
13
|
-
end
|
|
14
|
-
end
|
|
15
|
-
end
|
|
16
|
-
end
|
|
17
|
-
end
|
|
@@ -1,68 +0,0 @@
|
|
|
1
|
-
module Textus
|
|
2
|
-
module Handlers
|
|
3
|
-
module Read
|
|
4
|
-
class GetEntry
|
|
5
|
-
def initialize(container:, freshness_evaluator:)
|
|
6
|
-
@container = container
|
|
7
|
-
@freshness_evaluator = freshness_evaluator
|
|
8
|
-
end
|
|
9
|
-
|
|
10
|
-
def call(command, _call)
|
|
11
|
-
envelope = Store::Entry::Reader.from(container: @container).read(command.key)
|
|
12
|
-
return Value::Result.failure(:not_found, "no entry at #{command.key}") unless envelope
|
|
13
|
-
|
|
14
|
-
envelope = expand_sources(envelope, depth: 0)
|
|
15
|
-
Value::Result.success(envelope.with(freshness: @freshness_evaluator.verdict(resolve_entry(command.key))))
|
|
16
|
-
end
|
|
17
|
-
|
|
18
|
-
MAX_SOURCE_DEPTH = 5
|
|
19
|
-
|
|
20
|
-
private
|
|
21
|
-
|
|
22
|
-
def expand_sources(envelope, depth:)
|
|
23
|
-
return envelope if depth >= MAX_SOURCE_DEPTH
|
|
24
|
-
|
|
25
|
-
raw_sources = Array(envelope.meta["sources"])
|
|
26
|
-
return envelope if raw_sources.empty?
|
|
27
|
-
|
|
28
|
-
expanded = raw_sources.map { |src| expand_one_source(src, depth: depth) }
|
|
29
|
-
envelope.with(sources: expanded)
|
|
30
|
-
end
|
|
31
|
-
|
|
32
|
-
def expand_one_source(src, depth:)
|
|
33
|
-
src = { "key" => src } if src.is_a?(String)
|
|
34
|
-
return src unless src.is_a?(Hash) && src["key"].is_a?(String)
|
|
35
|
-
|
|
36
|
-
key = src["key"]
|
|
37
|
-
stored_etag = src["etag"]
|
|
38
|
-
current_etag = resolve_current_etag(key)
|
|
39
|
-
suspended = stored_etag && current_etag ? stored_etag != current_etag : false
|
|
40
|
-
|
|
41
|
-
result = src.merge("suspended" => suspended)
|
|
42
|
-
|
|
43
|
-
child_env = @container.reader.read(key)
|
|
44
|
-
if child_env
|
|
45
|
-
child_expanded = expand_sources(child_env, depth: depth + 1)
|
|
46
|
-
child_sources = Array(child_expanded.sources)
|
|
47
|
-
result = result.merge("sources" => child_sources) unless child_sources.empty?
|
|
48
|
-
end
|
|
49
|
-
|
|
50
|
-
result
|
|
51
|
-
end
|
|
52
|
-
|
|
53
|
-
def resolve_current_etag(key)
|
|
54
|
-
path = @container.manifest.resolver.resolve(key).path
|
|
55
|
-
return nil unless @container.file_store.exists?(path)
|
|
56
|
-
|
|
57
|
-
@container.file_store.etag(path)
|
|
58
|
-
rescue Textus::Error
|
|
59
|
-
nil
|
|
60
|
-
end
|
|
61
|
-
|
|
62
|
-
def resolve_entry(key)
|
|
63
|
-
@container.manifest.resolver.resolve(key).entry
|
|
64
|
-
end
|
|
65
|
-
end
|
|
66
|
-
end
|
|
67
|
-
end
|
|
68
|
-
end
|
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
module Textus
|
|
2
|
-
module Handlers
|
|
3
|
-
module Read
|
|
4
|
-
class ListKeys
|
|
5
|
-
def initialize(manifest:, job_store: nil)
|
|
6
|
-
@manifest = manifest
|
|
7
|
-
@job_store = job_store
|
|
8
|
-
end
|
|
9
|
-
|
|
10
|
-
def call(command, _call)
|
|
11
|
-
q = command.respond_to?(:q) ? command.q : nil
|
|
12
|
-
schema = command.respond_to?(:schema) ? command.schema : nil
|
|
13
|
-
|
|
14
|
-
return sqlite_list(q: q, schema: schema, lane: command.lane, prefix: command.prefix) if @job_store && (q || schema)
|
|
15
|
-
|
|
16
|
-
manifest_list(prefix: command.prefix, lane: command.lane)
|
|
17
|
-
end
|
|
18
|
-
|
|
19
|
-
private
|
|
20
|
-
|
|
21
|
-
def sqlite_list(q:, schema:, lane:, prefix:) # rubocop:disable Naming/MethodParameterName
|
|
22
|
-
rows = @job_store.search_entries(q: q, schema: schema, lane: lane, prefix: prefix)
|
|
23
|
-
Value::Result.success((rows || []).map { |r| { "key" => r["key"], "lane" => r["lane"] } })
|
|
24
|
-
end
|
|
25
|
-
|
|
26
|
-
def manifest_list(prefix:, lane:)
|
|
27
|
-
rows = @manifest.resolver.enumerate(prefix: prefix)
|
|
28
|
-
rows = rows.select { |row| row[:manifest_entry].lane == lane } if lane
|
|
29
|
-
Value::Result.success(rows.map do |row|
|
|
30
|
-
{ "key" => row[:key], "lane" => row[:manifest_entry].lane, "path" => row[:path] }
|
|
31
|
-
end)
|
|
32
|
-
end
|
|
33
|
-
end
|
|
34
|
-
end
|
|
35
|
-
end
|
|
36
|
-
end
|
|
@@ -1,66 +0,0 @@
|
|
|
1
|
-
module Textus
|
|
2
|
-
module Handlers
|
|
3
|
-
module Read
|
|
4
|
-
class PulseEntries
|
|
5
|
-
def initialize(manifest:, audit_log:, file_store:, orchestration:, job_store: nil)
|
|
6
|
-
@manifest = manifest
|
|
7
|
-
@audit_log = audit_log
|
|
8
|
-
@file_store = file_store
|
|
9
|
-
@orchestration = orchestration
|
|
10
|
-
@job_store = job_store
|
|
11
|
-
end
|
|
12
|
-
|
|
13
|
-
def call(command, call)
|
|
14
|
-
root = @manifest.data.root
|
|
15
|
-
since = command.since || Textus::Store::Cursor.new(root: root, role: call.role).read
|
|
16
|
-
|
|
17
|
-
changed = changed_since(since, call)
|
|
18
|
-
|
|
19
|
-
result = {
|
|
20
|
-
"cursor" => @audit_log.latest_seq,
|
|
21
|
-
"changed" => changed,
|
|
22
|
-
"pending_review" => review_keys(call),
|
|
23
|
-
"contract_etag" => Textus::Value::Etag.for_contract(root),
|
|
24
|
-
"index_etag" => index_etag,
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
Textus::Store::Cursor.new(root: root, role: call.role).write(result["cursor"])
|
|
28
|
-
Value::Result.success(result)
|
|
29
|
-
end
|
|
30
|
-
|
|
31
|
-
private
|
|
32
|
-
|
|
33
|
-
def changed_since(since, call)
|
|
34
|
-
if @job_store
|
|
35
|
-
sqlite_rows = @job_store.audit_events_since(seq: since)
|
|
36
|
-
return sqlite_rows.map { |r| { "key" => r["key"], "verb" => r["verb"], "seq" => r["seq"] } } if sqlite_rows.any?
|
|
37
|
-
end
|
|
38
|
-
|
|
39
|
-
# Fall back to flat-log scan when SQLite index is empty for this window
|
|
40
|
-
# (writes that bypassed dispatch, fresh stores, or pre-migration entries).
|
|
41
|
-
audit = @orchestration.audit_entries(seq_since: since, call: call)
|
|
42
|
-
return [] if audit.failure?
|
|
43
|
-
|
|
44
|
-
audit.value.fetch("rows") || []
|
|
45
|
-
end
|
|
46
|
-
|
|
47
|
-
def review_keys(call)
|
|
48
|
-
queue = @manifest.policy.queue_lane
|
|
49
|
-
return [] unless queue
|
|
50
|
-
|
|
51
|
-
result = @orchestration.list_keys(prefix: nil, lane: queue, call: call)
|
|
52
|
-
return [] unless result.success?
|
|
53
|
-
|
|
54
|
-
result.value.fetch("rows").map { |r| r["key"] }
|
|
55
|
-
end
|
|
56
|
-
|
|
57
|
-
def index_etag
|
|
58
|
-
path = @manifest.resolver.resolve("artifacts.system.index").path
|
|
59
|
-
File.exist?(path) ? @file_store.etag(path) : nil
|
|
60
|
-
rescue Textus::Error
|
|
61
|
-
nil
|
|
62
|
-
end
|
|
63
|
-
end
|
|
64
|
-
end
|
|
65
|
-
end
|
|
66
|
-
end
|
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
module Textus
|
|
2
|
-
module Handlers
|
|
3
|
-
module Read
|
|
4
|
-
class RdepsEntry
|
|
5
|
-
def initialize(manifest:)
|
|
6
|
-
@manifest = manifest
|
|
7
|
-
end
|
|
8
|
-
|
|
9
|
-
def call(command, _call)
|
|
10
|
-
rdeps = @manifest.data.entries.each_with_object([]) do |entry, acc|
|
|
11
|
-
next unless entry.external?
|
|
12
|
-
|
|
13
|
-
sources = Array(entry.source&.sources).compact
|
|
14
|
-
acc << entry.key if sources.any? { |source| source == command.key || command.key.start_with?("#{source}.") }
|
|
15
|
-
end
|
|
16
|
-
Value::Result.success("key" => command.key, "rdeps" => rdeps)
|
|
17
|
-
end
|
|
18
|
-
end
|
|
19
|
-
end
|
|
20
|
-
end
|
|
21
|
-
end
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
module Textus
|
|
2
|
-
module Handlers
|
|
3
|
-
module Read
|
|
4
|
-
class UidEntry
|
|
5
|
-
def initialize(container:)
|
|
6
|
-
@container = container
|
|
7
|
-
end
|
|
8
|
-
|
|
9
|
-
def call(command, _call)
|
|
10
|
-
envelope = Store::Entry::Reader.from(container: @container).read(command.key)
|
|
11
|
-
return Value::Result.failure(:not_found, "no entry at #{command.key}") unless envelope
|
|
12
|
-
|
|
13
|
-
Value::Result.success(envelope.uid)
|
|
14
|
-
end
|
|
15
|
-
end
|
|
16
|
-
end
|
|
17
|
-
end
|
|
18
|
-
end
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
module Textus
|
|
2
|
-
module Handlers
|
|
3
|
-
module Read
|
|
4
|
-
class WhereEntry
|
|
5
|
-
def initialize(manifest:)
|
|
6
|
-
@manifest = manifest
|
|
7
|
-
end
|
|
8
|
-
|
|
9
|
-
def call(command, _call)
|
|
10
|
-
res = @manifest.resolver.resolve(command.key)
|
|
11
|
-
mentry = res.entry
|
|
12
|
-
Value::Result.success("protocol" => Textus::PROTOCOL, "key" => command.key,
|
|
13
|
-
"lane" => mentry.lane, "owner" => mentry.owner, "path" => res.path)
|
|
14
|
-
end
|
|
15
|
-
end
|
|
16
|
-
end
|
|
17
|
-
end
|
|
18
|
-
end
|
|
@@ -1,39 +0,0 @@
|
|
|
1
|
-
module Textus
|
|
2
|
-
module Handlers
|
|
3
|
-
module Write
|
|
4
|
-
class AcceptProposal
|
|
5
|
-
def initialize(container:)
|
|
6
|
-
@container = container
|
|
7
|
-
end
|
|
8
|
-
|
|
9
|
-
def call(command, call)
|
|
10
|
-
reader = Store::Entry::Reader.from(container: @container)
|
|
11
|
-
env = reader.read(command.pending_key)
|
|
12
|
-
proposal = env&.meta&.dig("proposal") or
|
|
13
|
-
return Value::Result.failure(:proposal_error, "entry has no proposal block: #{command.pending_key}")
|
|
14
|
-
target = proposal["target_key"] or
|
|
15
|
-
return Value::Result.failure(:proposal_error, "proposal missing target_key")
|
|
16
|
-
action = proposal["action"] || "put"
|
|
17
|
-
|
|
18
|
-
writer = Store::Entry::Writer.from(container: @container, call: call)
|
|
19
|
-
case action
|
|
20
|
-
when "put"
|
|
21
|
-
mentry = @container.manifest.resolver.resolve(target).entry
|
|
22
|
-
writer.put(
|
|
23
|
-
target, mentry: mentry,
|
|
24
|
-
payload: Textus::Value::Payload.new(meta: env.meta["_meta"] || {}, body: env.body, content: nil)
|
|
25
|
-
)
|
|
26
|
-
when "delete"
|
|
27
|
-
writer.delete(target)
|
|
28
|
-
else
|
|
29
|
-
return Value::Result.failure(:proposal_error, "unknown action: #{action}")
|
|
30
|
-
end
|
|
31
|
-
|
|
32
|
-
writer.delete(command.pending_key)
|
|
33
|
-
Value::Result.success("protocol" => Textus::PROTOCOL, "accepted" => command.pending_key,
|
|
34
|
-
"target_key" => target, "action" => action, "cascade_key" => target)
|
|
35
|
-
end
|
|
36
|
-
end
|
|
37
|
-
end
|
|
38
|
-
end
|
|
39
|
-
end
|
|
@@ -1,55 +0,0 @@
|
|
|
1
|
-
require "yaml"
|
|
2
|
-
|
|
3
|
-
module Textus
|
|
4
|
-
module Handlers
|
|
5
|
-
module Write
|
|
6
|
-
class DataMv
|
|
7
|
-
def initialize(container:)
|
|
8
|
-
@container = container
|
|
9
|
-
end
|
|
10
|
-
|
|
11
|
-
def call(command, _call)
|
|
12
|
-
manifest = @container.manifest
|
|
13
|
-
geom = @container.layout
|
|
14
|
-
|
|
15
|
-
return Value::Result.failure(:usage_error, "from and to required") if command.from.nil? || command.to.nil?
|
|
16
|
-
unless manifest.data.declared_lane_kinds.key?(command.from)
|
|
17
|
-
return Value::Result.failure(:usage_error,
|
|
18
|
-
"data lane '#{command.from}' not declared")
|
|
19
|
-
end
|
|
20
|
-
|
|
21
|
-
dest_dir = geom.lane_path(command.to)
|
|
22
|
-
return Value::Result.failure(:usage_error, "destination 'data/#{command.to}' already exists") if File.exist?(dest_dir)
|
|
23
|
-
|
|
24
|
-
affected_keys = manifest.data.entries.select { |entry| entry.lane == command.from }.map(&:key)
|
|
25
|
-
|
|
26
|
-
steps = [{ "op" => "rename_zone", "from" => command.from, "to" => command.to }]
|
|
27
|
-
steps += affected_keys.map do |key|
|
|
28
|
-
{ "op" => "mv", "from" => key, "to" => "#{command.to}#{key[command.from.length..]}" }
|
|
29
|
-
end
|
|
30
|
-
|
|
31
|
-
plan = Textus::Store::Jobs::Plan.new(steps: steps, warnings: [])
|
|
32
|
-
return Value::Result.success(plan) if command.dry_run
|
|
33
|
-
|
|
34
|
-
rewrite_manifest!(geom, from: command.from, to: command.to)
|
|
35
|
-
FileUtils.mv(geom.lane_path(command.from), dest_dir)
|
|
36
|
-
Value::Result.success(plan)
|
|
37
|
-
end
|
|
38
|
-
|
|
39
|
-
private
|
|
40
|
-
|
|
41
|
-
def rewrite_manifest!(geom, from:, to:)
|
|
42
|
-
path = geom.manifest_path
|
|
43
|
-
raw = YAML.safe_load_file(path, permitted_classes: [Symbol], aliases: false)
|
|
44
|
-
raw["lanes"].each { |lane| lane["name"] = to if lane["name"] == from }
|
|
45
|
-
raw["entries"].each do |entry|
|
|
46
|
-
entry["lane"] = to if entry["lane"] == from
|
|
47
|
-
entry["key"] = entry["key"].sub(/\A#{Regexp.escape(from)}(\.|\z)/, "#{to}\\1")
|
|
48
|
-
entry["path"] = entry["path"].sub(%r{\A(data/)?#{Regexp.escape(from)}(/|\z)}, "\\1#{to}\\2")
|
|
49
|
-
end
|
|
50
|
-
File.write(path, YAML.dump(raw))
|
|
51
|
-
end
|
|
52
|
-
end
|
|
53
|
-
end
|
|
54
|
-
end
|
|
55
|
-
end
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
module Textus
|
|
2
|
-
module Handlers
|
|
3
|
-
module Write
|
|
4
|
-
class DeleteKey
|
|
5
|
-
def initialize(container:)
|
|
6
|
-
@container = container
|
|
7
|
-
end
|
|
8
|
-
|
|
9
|
-
def call(command, call)
|
|
10
|
-
writer = Store::Entry::Writer.from(container: @container, call: call)
|
|
11
|
-
writer.delete(command.key, if_etag: command.if_etag)
|
|
12
|
-
Value::Result.success("protocol" => Textus::PROTOCOL, "ok" => true, "key" => command.key, "deleted" => true)
|
|
13
|
-
end
|
|
14
|
-
end
|
|
15
|
-
end
|
|
16
|
-
end
|
|
17
|
-
end
|
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
module Textus
|
|
2
|
-
module Handlers
|
|
3
|
-
module Write
|
|
4
|
-
class EnqueueJob
|
|
5
|
-
def initialize(job_store:)
|
|
6
|
-
@job_store = job_store
|
|
7
|
-
end
|
|
8
|
-
|
|
9
|
-
def call(command, call)
|
|
10
|
-
action_class = Textus::Jobs.fetch(command.type.to_s)
|
|
11
|
-
|
|
12
|
-
if action_class.const_defined?(:REQUIRED_ROLE) && call.role != action_class::REQUIRED_ROLE
|
|
13
|
-
return Value::Result.failure(:forbidden,
|
|
14
|
-
"role '#{call.role}' is not authorized to enqueue this job type",
|
|
15
|
-
details: { "role" => call.role, "required_role" => action_class::REQUIRED_ROLE })
|
|
16
|
-
end
|
|
17
|
-
|
|
18
|
-
job = Textus::Store::Jobs::Queue::Job.new(type: command.type, args: command.args, role: call.role, max_attempts: 3)
|
|
19
|
-
Textus::Store::Jobs::Queue.new(store: @job_store).enqueue(job)
|
|
20
|
-
Value::Result.success("protocol" => Textus::PROTOCOL, "ok" => true, "id" => job.id)
|
|
21
|
-
rescue Textus::UsageError
|
|
22
|
-
Value::Result.failure(:usage_error, "unregistered job type '#{command.type}'")
|
|
23
|
-
end
|
|
24
|
-
end
|
|
25
|
-
end
|
|
26
|
-
end
|
|
27
|
-
end
|
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
module Textus
|
|
2
|
-
module Handlers
|
|
3
|
-
module Write
|
|
4
|
-
class KeyDeletePrefix
|
|
5
|
-
def initialize(orchestration:)
|
|
6
|
-
@orchestration = orchestration
|
|
7
|
-
end
|
|
8
|
-
|
|
9
|
-
def call(command, call)
|
|
10
|
-
return Value::Result.failure(:usage_error, "prefix required") if command.prefix.nil? || command.prefix.empty?
|
|
11
|
-
|
|
12
|
-
list = @orchestration.list_keys(prefix: command.prefix, lane: nil, call: call)
|
|
13
|
-
return list if list.failure?
|
|
14
|
-
|
|
15
|
-
leaves = list.value.fetch("rows")
|
|
16
|
-
|
|
17
|
-
warnings = leaves.empty? ? ["no keys under #{command.prefix}"] : []
|
|
18
|
-
steps = leaves.map { |row| { "op" => "delete", "key" => row["key"] } }
|
|
19
|
-
|
|
20
|
-
plan = Textus::Store::Jobs::Plan.new(steps: steps, warnings: warnings)
|
|
21
|
-
return Value::Result.success(plan) if command.dry_run
|
|
22
|
-
|
|
23
|
-
steps.each do |step|
|
|
24
|
-
delete = @orchestration.delete_key(key: step["key"], call: call)
|
|
25
|
-
return delete if delete.failure?
|
|
26
|
-
end
|
|
27
|
-
Value::Result.success(plan)
|
|
28
|
-
end
|
|
29
|
-
end
|
|
30
|
-
end
|
|
31
|
-
end
|
|
32
|
-
end
|
|
@@ -1,45 +0,0 @@
|
|
|
1
|
-
module Textus
|
|
2
|
-
module Handlers
|
|
3
|
-
module Write
|
|
4
|
-
class KeyMvPrefix
|
|
5
|
-
def initialize(orchestration:)
|
|
6
|
-
@orchestration = orchestration
|
|
7
|
-
end
|
|
8
|
-
|
|
9
|
-
def call(command, call)
|
|
10
|
-
if command.from_prefix.nil? || command.to_prefix.nil?
|
|
11
|
-
return Value::Result.failure(:usage_error,
|
|
12
|
-
"from_prefix and to_prefix required")
|
|
13
|
-
end
|
|
14
|
-
|
|
15
|
-
list = @orchestration.list_keys(prefix: command.from_prefix, lane: nil, call: call)
|
|
16
|
-
return list if list.failure?
|
|
17
|
-
|
|
18
|
-
leaves = list.value.fetch("rows")
|
|
19
|
-
|
|
20
|
-
if leaves.any? { |r| r["key"] == command.from_prefix }
|
|
21
|
-
return Value::Result.failure(:usage_error,
|
|
22
|
-
"from_prefix '#{command.from_prefix}' is itself a leaf — use `mv` to rename a single key")
|
|
23
|
-
end
|
|
24
|
-
|
|
25
|
-
warnings = leaves.empty? ? ["no keys under #{command.from_prefix}"] : []
|
|
26
|
-
steps = leaves.map do |row|
|
|
27
|
-
old_key = row["key"]
|
|
28
|
-
tail = old_key.delete_prefix("#{command.from_prefix}.")
|
|
29
|
-
new_key = "#{command.to_prefix}.#{tail}"
|
|
30
|
-
{ "op" => "mv", "from" => old_key, "to" => new_key }
|
|
31
|
-
end
|
|
32
|
-
|
|
33
|
-
plan = Textus::Store::Jobs::Plan.new(steps: steps, warnings: warnings)
|
|
34
|
-
return Value::Result.success(plan) if command.dry_run
|
|
35
|
-
|
|
36
|
-
steps.each do |step|
|
|
37
|
-
move = @orchestration.move_key(old_key: step["from"], new_key: step["to"], call: call)
|
|
38
|
-
return move if move.failure?
|
|
39
|
-
end
|
|
40
|
-
Value::Result.success(plan)
|
|
41
|
-
end
|
|
42
|
-
end
|
|
43
|
-
end
|
|
44
|
-
end
|
|
45
|
-
end
|