mammoth 0.6.0 → 0.7.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 +27 -1
- data/README.md +16 -0
- data/config/mammoth.example.yml +17 -0
- data/config/mammoth.schema.json +56 -1
- data/lib/mammoth/application.rb +58 -23
- data/lib/mammoth/capabilities.rb +59 -0
- data/lib/mammoth/cdc_source.rb +1 -1
- data/lib/mammoth/cli.rb +16 -24
- data/lib/mammoth/commands/bootstrap_command.rb +26 -0
- data/lib/mammoth/commands/dead_letters_command.rb +22 -0
- data/lib/mammoth/commands/deliver_sample_command.rb +35 -0
- data/lib/mammoth/commands/start_command.rb +28 -0
- data/lib/mammoth/commands/status_command.rb +28 -0
- data/lib/mammoth/commands/validate_command.rb +24 -0
- data/lib/mammoth/configuration.rb +66 -7
- data/lib/mammoth/dead_letter_commands.rb +17 -9
- data/lib/mammoth/dead_letter_store.rb +4 -0
- data/lib/mammoth/delivered_envelope_store.rb +3 -0
- data/lib/mammoth/destinations/adapter.rb +20 -0
- data/lib/mammoth/destinations/registry.rb +46 -0
- data/lib/mammoth/destinations/webhook_adapter.rb +29 -0
- data/lib/mammoth/lifecycle_hooks.rb +56 -0
- data/lib/mammoth/node_identity.rb +54 -0
- data/lib/mammoth/observability_server.rb +2 -0
- data/lib/mammoth/operational_state/adapter.rb +37 -0
- data/lib/mammoth/operational_state/registry.rb +46 -0
- data/lib/mammoth/operational_state/sqlite_adapter.rb +44 -0
- data/lib/mammoth/registry.rb +46 -0
- data/lib/mammoth/runtimes/adapter.rb +23 -0
- data/lib/mammoth/runtimes/concurrent_adapter.rb +31 -0
- data/lib/mammoth/runtimes/inline_adapter.rb +40 -0
- data/lib/mammoth/runtimes/registry.rb +46 -0
- data/lib/mammoth/status.rb +55 -13
- data/lib/mammoth/version.rb +1 -1
- data/lib/mammoth.rb +28 -3
- metadata +21 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: d7437cc30ed1a50ca4b414c37bc75ba374569ef3cdfafc7b88f0631d5f462790
|
|
4
|
+
data.tar.gz: d75b6cf2476c892915aa24ceb5169f69889a794cb923e5d1748a7c5a726129e3
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: b38367dbafad260559c1f80558d2aeb6f452688fc1404b5652dbd8310625fbe0058eb2ac45ab0dea9dc226b19e0447efca79ff8a70556f3b10f24ba1ebd2e8c9
|
|
7
|
+
data.tar.gz: f7950310f899a5291889853f95b096937b51332899c8adabe7209e6bef3b5c186863cc37bc0cb331e06a51c3ff5abc15b5049995a0822f07ff51c3076b5b79b5
|
data/CHANGELOG.md
CHANGED
|
@@ -1,6 +1,32 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
-
## Unreleased
|
|
3
|
+
## 0.7.1 Unreleased
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
|
|
7
|
+
- Added local lifecycle hooks for start, shutdown, and dead-letter replay extension points.
|
|
8
|
+
- Added file and hash configuration providers for CLI, tests, and future control-plane integration.
|
|
9
|
+
- Added reusable command objects for validate, bootstrap, start, sample delivery, and dead-letter commands.
|
|
10
|
+
|
|
11
|
+
### Changed
|
|
12
|
+
|
|
13
|
+
- Bumped Mammoth version to `0.7.1`.
|
|
14
|
+
|
|
15
|
+
## 0.7.0
|
|
16
|
+
|
|
17
|
+
### Added
|
|
18
|
+
|
|
19
|
+
- Added explicit adapter registries for operational state, destinations, and delivery runtimes.
|
|
20
|
+
- Added the built-in SQLite operational state adapter.
|
|
21
|
+
- Added the built-in webhook destination adapter registration.
|
|
22
|
+
- Added inline and concurrent runtime adapter registration.
|
|
23
|
+
- Added optional node identity configuration for future control-plane integration.
|
|
24
|
+
- Added local capability reporting for state, destination, runtime, and relay features.
|
|
25
|
+
- Added a reusable status command object behind the CLI status command.
|
|
26
|
+
|
|
27
|
+
### Changed
|
|
28
|
+
|
|
29
|
+
- Bumped Mammoth version to `0.7.0`.
|
|
4
30
|
|
|
5
31
|
## 0.6.0
|
|
6
32
|
|
data/README.md
CHANGED
|
@@ -57,6 +57,9 @@ https://kanutocd.github.io/mammoth/Mammoth.html
|
|
|
57
57
|
- public Helm chart support
|
|
58
58
|
- unit and e2e test tasks
|
|
59
59
|
- health and metrics endpoints
|
|
60
|
+
- explicit extension registries for state, destination, and runtime adapters
|
|
61
|
+
- node identity and local capability reporting
|
|
62
|
+
- lifecycle hooks, configuration providers, and reusable local command objects
|
|
60
63
|
|
|
61
64
|
## Boundary
|
|
62
65
|
|
|
@@ -66,6 +69,19 @@ Mammoth does not own pgoutput protocol parsing, value decoding, source
|
|
|
66
69
|
normalization, ordering policy, or runtime execution. Those belong to the
|
|
67
70
|
upstream CDC Ecosystem components.
|
|
68
71
|
|
|
72
|
+
## Extensions
|
|
73
|
+
|
|
74
|
+
Mammoth OSS exposes small adapter registries for future extensions:
|
|
75
|
+
|
|
76
|
+
- operational state adapters
|
|
77
|
+
- destination adapters
|
|
78
|
+
- runtime adapters
|
|
79
|
+
- lifecycle hooks
|
|
80
|
+
- configuration providers
|
|
81
|
+
- local command objects
|
|
82
|
+
|
|
83
|
+
See [`docs/EXTENSIONS.md`](docs/EXTENSIONS.md).
|
|
84
|
+
|
|
69
85
|
## Configuration
|
|
70
86
|
|
|
71
87
|
Mammoth configuration is YAML-backed and IDE-friendly.
|
data/config/mammoth.example.yml
CHANGED
|
@@ -34,6 +34,19 @@ mammoth:
|
|
|
34
34
|
# production_mammoth
|
|
35
35
|
name: local_mammoth
|
|
36
36
|
|
|
37
|
+
node:
|
|
38
|
+
# Optional local node identity for status output and future control-plane
|
|
39
|
+
# agents. Omit this section to default node_id to the host name and node_name
|
|
40
|
+
# to mammoth.name.
|
|
41
|
+
node_id: local-mammoth-1
|
|
42
|
+
node_name: local-mammoth-dev
|
|
43
|
+
fleet_id: local-dev
|
|
44
|
+
environment: development
|
|
45
|
+
labels:
|
|
46
|
+
region: local
|
|
47
|
+
metadata:
|
|
48
|
+
owner: platform
|
|
49
|
+
|
|
37
50
|
postgres:
|
|
38
51
|
# PostgreSQL host reachable from the Mammoth process or pod.
|
|
39
52
|
#
|
|
@@ -331,6 +344,10 @@ sqlite:
|
|
|
331
344
|
# records here. In Kubernetes, back this path with a PersistentVolumeClaim.
|
|
332
345
|
path: data/mammoth.db
|
|
333
346
|
|
|
347
|
+
operational_state:
|
|
348
|
+
# Mammoth OSS 0.7.x ships the sqlite operational state adapter.
|
|
349
|
+
adapter: sqlite
|
|
350
|
+
|
|
334
351
|
observability:
|
|
335
352
|
# Bind host for the optional health, readiness, and metrics server.
|
|
336
353
|
host: 0.0.0.0
|
data/config/mammoth.schema.json
CHANGED
|
@@ -40,6 +40,47 @@
|
|
|
40
40
|
},
|
|
41
41
|
"description": "Mammoth instance identity and operator-facing metadata."
|
|
42
42
|
},
|
|
43
|
+
"node": {
|
|
44
|
+
"type": "object",
|
|
45
|
+
"additionalProperties": false,
|
|
46
|
+
"properties": {
|
|
47
|
+
"node_id": {
|
|
48
|
+
"type": "string",
|
|
49
|
+
"minLength": 1,
|
|
50
|
+
"description": "Stable node identifier used by status output and future control-plane agents."
|
|
51
|
+
},
|
|
52
|
+
"node_name": {
|
|
53
|
+
"type": "string",
|
|
54
|
+
"minLength": 1,
|
|
55
|
+
"description": "Human-readable node name."
|
|
56
|
+
},
|
|
57
|
+
"fleet_id": {
|
|
58
|
+
"type": "string",
|
|
59
|
+
"minLength": 1,
|
|
60
|
+
"description": "Optional fleet identifier for grouping Mammoth nodes."
|
|
61
|
+
},
|
|
62
|
+
"environment": {
|
|
63
|
+
"type": "string",
|
|
64
|
+
"minLength": 1,
|
|
65
|
+
"description": "Optional deployment environment such as development, staging, or production."
|
|
66
|
+
},
|
|
67
|
+
"labels": {
|
|
68
|
+
"type": "object",
|
|
69
|
+
"additionalProperties": {
|
|
70
|
+
"type": "string"
|
|
71
|
+
},
|
|
72
|
+
"description": "Operator-defined node labels."
|
|
73
|
+
},
|
|
74
|
+
"metadata": {
|
|
75
|
+
"type": "object",
|
|
76
|
+
"additionalProperties": {
|
|
77
|
+
"type": "string"
|
|
78
|
+
},
|
|
79
|
+
"description": "Operator-defined node metadata."
|
|
80
|
+
}
|
|
81
|
+
},
|
|
82
|
+
"description": "Optional local node identity. Mammoth OSS uses this for status and future extension surfaces only."
|
|
83
|
+
},
|
|
43
84
|
"postgres": {
|
|
44
85
|
"type": "object",
|
|
45
86
|
"additionalProperties": false,
|
|
@@ -223,7 +264,7 @@
|
|
|
223
264
|
"enum": [
|
|
224
265
|
"webhook"
|
|
225
266
|
],
|
|
226
|
-
"description": "Destination adapter type. Mammoth OSS 0.
|
|
267
|
+
"description": "Destination adapter type. Mammoth OSS 0.7.x supports webhook destinations."
|
|
227
268
|
},
|
|
228
269
|
"enabled": {
|
|
229
270
|
"type": "boolean",
|
|
@@ -385,6 +426,20 @@
|
|
|
385
426
|
},
|
|
386
427
|
"description": "SQLite operational store settings for checkpoints, attempts, and dead-letter records."
|
|
387
428
|
},
|
|
429
|
+
"operational_state": {
|
|
430
|
+
"type": "object",
|
|
431
|
+
"additionalProperties": false,
|
|
432
|
+
"properties": {
|
|
433
|
+
"adapter": {
|
|
434
|
+
"type": "string",
|
|
435
|
+
"enum": [
|
|
436
|
+
"sqlite"
|
|
437
|
+
],
|
|
438
|
+
"description": "Operational state adapter. Mammoth OSS 0.7.x ships the sqlite adapter."
|
|
439
|
+
}
|
|
440
|
+
},
|
|
441
|
+
"description": "Operational state adapter selection. SQLite remains the default OSS adapter."
|
|
442
|
+
},
|
|
388
443
|
"logging": {
|
|
389
444
|
"type": "object",
|
|
390
445
|
"additionalProperties": false,
|
data/lib/mammoth/application.rb
CHANGED
|
@@ -10,25 +10,62 @@ module Mammoth
|
|
|
10
10
|
# injected CDC work source rather than owning upstream CDC source-adapter
|
|
11
11
|
# lifecycle decisions.
|
|
12
12
|
class Application
|
|
13
|
-
attr_reader :config, :
|
|
13
|
+
attr_reader :config, :state_adapter, :consumer, :delivery_worker, :checkpoint_store, :lifecycle_hooks
|
|
14
14
|
|
|
15
15
|
# @param config [Mammoth::Configuration] loaded configuration
|
|
16
16
|
# @param source [#each, nil] injectable event source for tests and demos
|
|
17
17
|
# @param sink [#deliver, nil] optional destination sink
|
|
18
18
|
# @param sleeper [#call] retry sleep strategy
|
|
19
|
-
|
|
19
|
+
# @param lifecycle_hooks [Mammoth::LifecycleHooks, Hash] local lifecycle callbacks
|
|
20
|
+
def initialize(config, source: nil, sink: nil, sleeper: Kernel.method(:sleep), lifecycle_hooks: LifecycleHooks.new)
|
|
20
21
|
@config = config
|
|
21
|
-
@
|
|
22
|
-
@
|
|
22
|
+
@lifecycle_hooks = build_lifecycle_hooks(lifecycle_hooks)
|
|
23
|
+
@state_adapter = build_state_adapter
|
|
24
|
+
@checkpoint_store = state_adapter.checkpoint_store
|
|
23
25
|
@consumer = ReplicationConsumer.new(source: source || build_source, delivery_unit: delivery_unit)
|
|
24
26
|
@delivery_worker = sink ? build_delivery_worker(sink: sink, sleeper: sleeper) : build_configured_delivery_worker(sleeper:)
|
|
25
27
|
end
|
|
26
28
|
|
|
29
|
+
# @return [Mammoth::SQLiteStore] underlying SQLite store for compatibility
|
|
30
|
+
def sqlite_store
|
|
31
|
+
state_adapter.respond_to?(:sqlite_store) ? state_adapter.sqlite_store : nil
|
|
32
|
+
end
|
|
33
|
+
|
|
27
34
|
# Start the application runtime and deliver consumed CDC work.
|
|
28
35
|
#
|
|
29
36
|
# @return [Integer] number of processed work units
|
|
30
37
|
def start
|
|
31
38
|
runtime = build_runtime
|
|
39
|
+
processed = nil
|
|
40
|
+
|
|
41
|
+
lifecycle_hooks.call(:before_start, application_context(runtime: runtime))
|
|
42
|
+
processed = process_consumer(runtime)
|
|
43
|
+
lifecycle_hooks.call(:after_start, application_context(runtime: runtime, processed: processed))
|
|
44
|
+
processed
|
|
45
|
+
ensure
|
|
46
|
+
lifecycle_hooks.call(:before_shutdown, application_context(runtime: runtime, processed: processed))
|
|
47
|
+
runtime.shutdown if runtime.respond_to?(:shutdown)
|
|
48
|
+
lifecycle_hooks.call(:after_shutdown, application_context(runtime: runtime, processed: processed))
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
private
|
|
52
|
+
|
|
53
|
+
def build_lifecycle_hooks(hooks)
|
|
54
|
+
return hooks if hooks.is_a?(LifecycleHooks)
|
|
55
|
+
|
|
56
|
+
LifecycleHooks.new(hooks)
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def application_context(extra = {})
|
|
60
|
+
{
|
|
61
|
+
config: config,
|
|
62
|
+
state_adapter: state_adapter,
|
|
63
|
+
checkpoint_store: checkpoint_store,
|
|
64
|
+
delivery_worker: delivery_worker
|
|
65
|
+
}.merge(extra)
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def process_consumer(runtime)
|
|
32
69
|
processed = 0
|
|
33
70
|
batch = [nil].compact
|
|
34
71
|
|
|
@@ -45,22 +82,17 @@ module Mammoth
|
|
|
45
82
|
end
|
|
46
83
|
end
|
|
47
84
|
|
|
48
|
-
processed
|
|
49
|
-
processed
|
|
50
|
-
ensure
|
|
51
|
-
runtime.shutdown if runtime.respond_to?(:shutdown)
|
|
85
|
+
processed + flush_batch(runtime, batch)
|
|
52
86
|
end
|
|
53
87
|
|
|
54
|
-
|
|
88
|
+
def flush_batch(runtime, batch)
|
|
89
|
+
return 0 unless runtime_batching?(runtime) && batch.any?
|
|
90
|
+
|
|
91
|
+
process_batch(runtime, batch)
|
|
92
|
+
end
|
|
55
93
|
|
|
56
94
|
def process_work(runtime, work)
|
|
57
|
-
|
|
58
|
-
runtime.process_many([work])
|
|
59
|
-
elsif transaction_delivery?
|
|
60
|
-
delivery_worker.deliver_transaction(work)
|
|
61
|
-
else
|
|
62
|
-
delivery_worker.deliver(work)
|
|
63
|
-
end
|
|
95
|
+
runtime.process_many([work])
|
|
64
96
|
end
|
|
65
97
|
|
|
66
98
|
def process_batch(runtime, batch)
|
|
@@ -73,9 +105,8 @@ module Mammoth
|
|
|
73
105
|
end
|
|
74
106
|
|
|
75
107
|
def build_runtime
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
ConcurrentDeliveryRuntime.new(
|
|
108
|
+
Runtimes::Registry.build(
|
|
109
|
+
runtime_adapter,
|
|
79
110
|
processor: DeliveryProcessor.new(delivery_worker:, delivery_unit: delivery_unit),
|
|
80
111
|
concurrency: runtime_concurrency,
|
|
81
112
|
timeout: runtime_timeout,
|
|
@@ -92,7 +123,7 @@ module Mammoth
|
|
|
92
123
|
config,
|
|
93
124
|
sink: sink,
|
|
94
125
|
checkpoint_store: checkpoint_store,
|
|
95
|
-
dead_letter_store:
|
|
126
|
+
dead_letter_store: state_adapter.dead_letter_store,
|
|
96
127
|
sleeper: sleeper,
|
|
97
128
|
delivery_policy: delivery_policy
|
|
98
129
|
)
|
|
@@ -116,7 +147,7 @@ module Mammoth
|
|
|
116
147
|
|
|
117
148
|
destinations.map.with_index(1) do |destination, index|
|
|
118
149
|
{
|
|
119
|
-
sink:
|
|
150
|
+
sink: Destinations::Registry.build(destination, label: "destinations[#{index - 1}]"),
|
|
120
151
|
delivery_policy: destination_delivery_policy(destination)
|
|
121
152
|
}
|
|
122
153
|
end
|
|
@@ -132,8 +163,12 @@ module Mammoth
|
|
|
132
163
|
}
|
|
133
164
|
end
|
|
134
165
|
|
|
135
|
-
def
|
|
136
|
-
|
|
166
|
+
def build_state_adapter
|
|
167
|
+
OperationalState::Registry.build(operational_state_adapter, config)
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
def operational_state_adapter
|
|
171
|
+
config.dig("operational_state", "adapter") || "sqlite"
|
|
137
172
|
end
|
|
138
173
|
|
|
139
174
|
def delivery_unit
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Mammoth
|
|
4
|
+
# Reports local Mammoth capabilities without inspecting private internals.
|
|
5
|
+
class Capabilities
|
|
6
|
+
# Built-in OSS data-plane feature capabilities.
|
|
7
|
+
FEATURES = %w[
|
|
8
|
+
checkpointing
|
|
9
|
+
delivery_ledger
|
|
10
|
+
dead_letters
|
|
11
|
+
replay
|
|
12
|
+
health
|
|
13
|
+
metrics
|
|
14
|
+
routing
|
|
15
|
+
fanout
|
|
16
|
+
].freeze
|
|
17
|
+
|
|
18
|
+
attr_reader :config
|
|
19
|
+
|
|
20
|
+
# @param config [Mammoth::Configuration] loaded configuration
|
|
21
|
+
def initialize(config)
|
|
22
|
+
@config = config
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
# @param config [Mammoth::Configuration] loaded configuration
|
|
26
|
+
# @return [Hash] JSON-friendly capabilities
|
|
27
|
+
def self.call(config)
|
|
28
|
+
new(config).to_h
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
# @return [Hash] JSON-friendly capabilities
|
|
32
|
+
def to_h
|
|
33
|
+
{
|
|
34
|
+
operational_state: operational_state_adapter,
|
|
35
|
+
destinations: destination_types,
|
|
36
|
+
runtimes: Runtimes::Registry.names,
|
|
37
|
+
runtime: runtime_adapter,
|
|
38
|
+
features: FEATURES
|
|
39
|
+
}
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
private
|
|
43
|
+
|
|
44
|
+
def operational_state_adapter
|
|
45
|
+
config.dig("operational_state", "adapter") || "sqlite"
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def runtime_adapter
|
|
49
|
+
config.dig("runtime", "adapter") || "inline"
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def destination_types
|
|
53
|
+
destinations = config.data["destinations"]
|
|
54
|
+
return [config.dig("webhook", "type") || "webhook"] unless destinations
|
|
55
|
+
|
|
56
|
+
destinations.map { |destination| destination.fetch("type", "webhook") }.uniq.sort
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|
data/lib/mammoth/cdc_source.rb
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
module Mammoth
|
|
4
4
|
# Backwards-compatible internal alias for the PostgreSQL CDC source.
|
|
5
5
|
#
|
|
6
|
-
# New code should use {Mammoth::Sources::Postgres}. Mammoth
|
|
6
|
+
# New code should use {Mammoth::Sources::Postgres}. Mammoth keeps this
|
|
7
7
|
# constant so older tests or examples that referenced the transitional
|
|
8
8
|
# CdcSource name continue to work while the product-facing source name moves
|
|
9
9
|
# to PostgreSQL.
|
data/lib/mammoth/cli.rb
CHANGED
|
@@ -85,53 +85,37 @@ module Mammoth
|
|
|
85
85
|
end
|
|
86
86
|
|
|
87
87
|
def validate
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
0
|
|
88
|
+
require_config_path!
|
|
89
|
+
Commands::ValidateCommand.new(config_provider).call
|
|
91
90
|
end
|
|
92
91
|
|
|
93
92
|
def bootstrap
|
|
94
|
-
|
|
95
|
-
store = SQLiteStore.connect(config.dig("sqlite", "path")).bootstrap!
|
|
96
|
-
puts "SQLite database initialized"
|
|
97
|
-
puts "Path: #{store.path}"
|
|
98
|
-
puts "Tables: #{store.tables.join(", ")}"
|
|
99
|
-
0
|
|
93
|
+
Commands::BootstrapCommand.new(load_config).call
|
|
100
94
|
end
|
|
101
95
|
|
|
102
96
|
def status
|
|
103
97
|
config = load_config
|
|
104
98
|
store = SQLiteStore.connect(config.dig("sqlite", "path"))
|
|
105
|
-
|
|
106
|
-
0
|
|
99
|
+
Commands::StatusCommand.new(config, sqlite_store: store).call
|
|
107
100
|
end
|
|
108
101
|
|
|
109
102
|
def start
|
|
110
|
-
|
|
111
|
-
processed = Application.new(config).start
|
|
112
|
-
puts "Processed events: #{processed}"
|
|
113
|
-
0
|
|
103
|
+
Commands::StartCommand.new(load_config).call
|
|
114
104
|
end
|
|
115
105
|
|
|
116
106
|
def deliver_sample
|
|
117
107
|
config = load_config
|
|
118
108
|
event_path = argv.fetch(2, nil)
|
|
119
109
|
raise ConfigurationError, "event JSON path required\n#{USAGE}" unless event_path
|
|
120
|
-
raise ConfigurationError, "event JSON file not found: #{event_path}" unless File.file?(event_path)
|
|
121
110
|
|
|
122
|
-
|
|
123
|
-
processed = Application.new(config, source: [event]).start
|
|
124
|
-
puts "Processed sample events: #{processed}"
|
|
125
|
-
0
|
|
126
|
-
rescue JSON::ParserError => e
|
|
127
|
-
raise ConfigurationError, "invalid event JSON in #{event_path}: #{e.message}"
|
|
111
|
+
Commands::DeliverSampleCommand.new(config, event_path: event_path).call
|
|
128
112
|
end
|
|
129
113
|
|
|
130
114
|
# Dispatch the nested dead-letter command group.
|
|
131
115
|
#
|
|
132
116
|
# @return [Integer] process status code
|
|
133
117
|
def dead_letters
|
|
134
|
-
|
|
118
|
+
Commands::DeadLettersCommand.new(argv).call
|
|
135
119
|
end
|
|
136
120
|
|
|
137
121
|
def observability
|
|
@@ -143,9 +127,17 @@ module Mammoth
|
|
|
143
127
|
end
|
|
144
128
|
|
|
145
129
|
def load_config
|
|
130
|
+
require_config_path!
|
|
131
|
+
|
|
132
|
+
config_provider.load
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
def require_config_path!
|
|
146
136
|
raise ConfigurationError, "configuration path required\n#{USAGE}" unless config_path
|
|
137
|
+
end
|
|
147
138
|
|
|
148
|
-
|
|
139
|
+
def config_provider
|
|
140
|
+
Configuration::Providers::FileProvider.new(config_path)
|
|
149
141
|
end
|
|
150
142
|
end
|
|
151
143
|
end
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Mammoth
|
|
4
|
+
module Commands
|
|
5
|
+
# Initializes the configured operational SQLite database.
|
|
6
|
+
class BootstrapCommand
|
|
7
|
+
attr_reader :config, :output
|
|
8
|
+
|
|
9
|
+
# @param config [Mammoth::Configuration] loaded configuration
|
|
10
|
+
# @param output [#puts] output stream
|
|
11
|
+
def initialize(config, output: $stdout)
|
|
12
|
+
@config = config
|
|
13
|
+
@output = output
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
# @return [Integer] process-style status code
|
|
17
|
+
def call
|
|
18
|
+
store = SQLiteStore.connect(config.dig("sqlite", "path")).bootstrap!
|
|
19
|
+
output.puts "SQLite database initialized"
|
|
20
|
+
output.puts "Path: #{store.path}"
|
|
21
|
+
output.puts "Tables: #{store.tables.join(", ")}"
|
|
22
|
+
0
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Mammoth
|
|
4
|
+
module Commands
|
|
5
|
+
# Reusable local command object for dead-letter inspection and replay.
|
|
6
|
+
class DeadLettersCommand
|
|
7
|
+
attr_reader :argv, :lifecycle_hooks
|
|
8
|
+
|
|
9
|
+
# @param argv [Array<String>] dead-letters CLI-style arguments
|
|
10
|
+
# @param lifecycle_hooks [Mammoth::LifecycleHooks, Hash] local lifecycle callbacks
|
|
11
|
+
def initialize(argv, lifecycle_hooks: LifecycleHooks.new)
|
|
12
|
+
@argv = argv
|
|
13
|
+
@lifecycle_hooks = lifecycle_hooks
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
# @return [Integer] process-style status code
|
|
17
|
+
def call
|
|
18
|
+
DeadLetterCommands.call(argv, lifecycle_hooks: lifecycle_hooks)
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "json"
|
|
4
|
+
|
|
5
|
+
module Mammoth
|
|
6
|
+
module Commands
|
|
7
|
+
# Delivers one JSON event file through the configured local application.
|
|
8
|
+
class DeliverSampleCommand
|
|
9
|
+
attr_reader :config, :event_path, :output, :lifecycle_hooks
|
|
10
|
+
|
|
11
|
+
# @param config [Mammoth::Configuration] loaded configuration
|
|
12
|
+
# @param event_path [String] event JSON path
|
|
13
|
+
# @param output [#puts] output stream
|
|
14
|
+
# @param lifecycle_hooks [Mammoth::LifecycleHooks, Hash] local lifecycle callbacks
|
|
15
|
+
def initialize(config, event_path:, output: $stdout, lifecycle_hooks: LifecycleHooks.new)
|
|
16
|
+
@config = config
|
|
17
|
+
@event_path = event_path
|
|
18
|
+
@output = output
|
|
19
|
+
@lifecycle_hooks = lifecycle_hooks
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
# @return [Integer] process-style status code
|
|
23
|
+
def call
|
|
24
|
+
raise ConfigurationError, "event JSON file not found: #{event_path}" unless File.file?(event_path)
|
|
25
|
+
|
|
26
|
+
event = JSON.parse(File.read(event_path))
|
|
27
|
+
processed = Application.new(config, source: [event], lifecycle_hooks: lifecycle_hooks).start
|
|
28
|
+
output.puts "Processed sample events: #{processed}"
|
|
29
|
+
0
|
|
30
|
+
rescue JSON::ParserError => e
|
|
31
|
+
raise ConfigurationError, "invalid event JSON in #{event_path}: #{e.message}"
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Mammoth
|
|
4
|
+
module Commands
|
|
5
|
+
# Starts a Mammoth application and prints the processed work count.
|
|
6
|
+
class StartCommand
|
|
7
|
+
attr_reader :config, :source, :output, :lifecycle_hooks
|
|
8
|
+
|
|
9
|
+
# @param config [Mammoth::Configuration] loaded configuration
|
|
10
|
+
# @param source [#each, nil] optional injected source
|
|
11
|
+
# @param output [#puts] output stream
|
|
12
|
+
# @param lifecycle_hooks [Mammoth::LifecycleHooks, Hash] local lifecycle callbacks
|
|
13
|
+
def initialize(config, source: nil, output: $stdout, lifecycle_hooks: LifecycleHooks.new)
|
|
14
|
+
@config = config
|
|
15
|
+
@source = source
|
|
16
|
+
@output = output
|
|
17
|
+
@lifecycle_hooks = lifecycle_hooks
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
# @return [Integer] process-style status code
|
|
21
|
+
def call
|
|
22
|
+
processed = Application.new(config, source: source, lifecycle_hooks: lifecycle_hooks).start
|
|
23
|
+
output.puts "Processed events: #{processed}"
|
|
24
|
+
0
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Mammoth
|
|
4
|
+
# Local command objects shared by CLI and future control-plane agents.
|
|
5
|
+
module Commands
|
|
6
|
+
# Reusable local command object for status inspection.
|
|
7
|
+
class StatusCommand
|
|
8
|
+
attr_reader :config, :sqlite_store, :output
|
|
9
|
+
|
|
10
|
+
# @param config [Mammoth::Configuration] loaded configuration
|
|
11
|
+
# @param sqlite_store [Mammoth::SQLiteStore, nil] optional operational store
|
|
12
|
+
# @param output [#puts] output stream
|
|
13
|
+
def initialize(config, sqlite_store: nil, output: $stdout)
|
|
14
|
+
@config = config
|
|
15
|
+
@sqlite_store = sqlite_store
|
|
16
|
+
@output = output
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
# Execute the status command.
|
|
20
|
+
#
|
|
21
|
+
# @return [Integer] process-style status code
|
|
22
|
+
def call
|
|
23
|
+
Status.call(config, sqlite_store: sqlite_store, output: output)
|
|
24
|
+
0
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Mammoth
|
|
4
|
+
module Commands
|
|
5
|
+
# Validates a Mammoth configuration through a provider.
|
|
6
|
+
class ValidateCommand
|
|
7
|
+
attr_reader :provider, :output
|
|
8
|
+
|
|
9
|
+
# @param provider [#load] configuration provider
|
|
10
|
+
# @param output [#puts] output stream
|
|
11
|
+
def initialize(provider, output: $stdout)
|
|
12
|
+
@provider = provider
|
|
13
|
+
@output = output
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
# @return [Integer] process-style status code
|
|
17
|
+
def call
|
|
18
|
+
config = provider.load
|
|
19
|
+
output.puts "Configuration OK: #{config.path}"
|
|
20
|
+
0
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|