foobara 0.4.1 → 0.4.3

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.
Files changed (37) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +15 -0
  3. data/README.md +3 -1
  4. data/projects/command/src/command_pattern_implementation/concerns/transactions.rb +1 -0
  5. data/projects/command_connectors/Guardfile +1 -1
  6. data/projects/command_connectors/spec/auth_mappers_spec.rb +201 -0
  7. data/projects/command_connectors/spec/command_connector_spec.rb +2 -1
  8. data/projects/command_connectors/spec/pre_commit_transformers/load_atoms_pre_commit_transformer_spec.rb +5 -2
  9. data/projects/command_connectors/spec/request_spec.rb +23 -0
  10. data/projects/command_connectors/spec/spec_helper.rb +8 -3
  11. data/projects/command_connectors/src/authenticator.rb +27 -16
  12. data/projects/command_connectors/src/command_connector/request.rb +40 -5
  13. data/projects/command_connectors/src/command_connector.rb +98 -4
  14. data/projects/command_connectors/src/transformed_command.rb +12 -2
  15. data/projects/entities/spec/spec_helper.rb +4 -2
  16. data/projects/entities_plumbing/Guardfile +6 -0
  17. data/projects/entities_plumbing/lib/foobara/entities_plumbing.rb +12 -0
  18. data/projects/entities_plumbing/spec/auth_mappers_spec.rb +159 -0
  19. data/projects/entities_plumbing/spec/spec_helper.rb +54 -0
  20. data/projects/entities_plumbing/spec/support/rubyprof.rb +32 -0
  21. data/projects/entities_plumbing/spec/support/term_trap.rb +6 -0
  22. data/projects/entities_plumbing/src/command_connectors_extension.rb +17 -0
  23. data/projects/entities_plumbing/src/extensions/authenticator.rb +13 -0
  24. data/projects/manifest/spec/spec_helper.rb +6 -3
  25. data/projects/typesystem/projects/builtin_types/src/builtin_types.rb +6 -6
  26. data/projects/typesystem/projects/domain/src/domain.rb +0 -15
  27. data/projects/typesystem/projects/domain/src/domain_module_extension.rb +0 -153
  28. data/projects/typesystem/projects/domain/src/module_extension.rb +0 -58
  29. data/projects/typesystem/projects/namespace/src/is_namespace.rb +8 -0
  30. data/projects/typesystem/projects/type_declarations/lib/foobara/type_declarations.rb +0 -15
  31. data/projects/typesystem/projects/type_declarations/src/type_declaration.rb +15 -12
  32. data/projects/typesystem/projects/type_declarations/src/typed_transformer.rb +20 -2
  33. data/projects/typesystem/spec/common/data_path_spec.rb +6 -8
  34. data/projects/typesystem/spec/spec_helper.rb +5 -3
  35. data/version.rb +1 -1
  36. metadata +11 -2
  37. data/projects/entities/projects/model/src/extensions/domain/module_extension.rb +0 -20
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: cd07a1cd0a343e918e6686086afa86cb0996b04a120a67381d4582bfdfae7171
4
- data.tar.gz: cbfd94dde3fbee1846451afabac7ce3bb1b4b2f0ae7c49d2844df17486c18df4
3
+ metadata.gz: db631f3175aa83feaee1a634560a7086c24e3c46b5c7df0b0a958252b531a377
4
+ data.tar.gz: 479a5728d5c86fdb689841d8cb422c564330d55a0502acbe3a32c27891573694
5
5
  SHA512:
6
- metadata.gz: db7af13da7e1b0faff58350ea7953487f856cff45f92beb5edf8249dbeb041dffd1f7ef98cf405b5fd71c1cb6b4d46014256c48dd9432f84ea5b5688fa8cbfbe
7
- data.tar.gz: 54563dc01bca8752b202ac61e50909d92794ea80357d4090c4741e27c6db705d498508d8cabee7c6dc40c28c51731cbee6449c8484f732dbb49adf3749c5aebd
6
+ metadata.gz: de898a650ff1066c7eb4ac304050a77c3a08bbf075bca85e7cf22c448a1ea3a1f128dd8cec36356a0cf1cae7a2590b3a98f547090f94b43674808b62c4178009
7
+ data.tar.gz: 6d900e6391ebd970950e43c42b106cbc0595480e14c7c594f90ed7be564234235f242b1824117cdc41975056e55763b854acfa4f6e412f02e6371e2b779a44d6
data/CHANGELOG.md CHANGED
@@ -1,3 +1,18 @@
1
+ # [0.4.3] - 2026-01-19
2
+
3
+ - Allow a request to handle auth mapper methods
4
+
5
+ # [0.4.2] - 2026-01-15
6
+
7
+ - Fix relaxed lookup bug for a symbol starting with a prefix
8
+ - Allow registered types to be looked up by string and in relaxed mode
9
+ - Create entities_plumbing project and implement auth mapping for entities
10
+ - Implement auth mappers concept
11
+ - Change authenticator to be a TypedTransformer
12
+ - Allow passing a block to a connector to simplify connecting stuff a bit
13
+ - Remove likely-unused automatic creation of Types module constants
14
+ - Support official ruby debug
15
+
1
16
  # [0.4.1] - 2025-12-19
2
17
 
3
18
  - Add a model_plumbing project and use it to fix up commands when models are reregistered
data/README.md CHANGED
@@ -1578,7 +1578,9 @@ module FoobaraDemo
1578
1578
 
1579
1579
  alias animal from
1580
1580
 
1581
- foobara_delegate :first_name, :last_name, :birthday, to: :animal # Deprecated
1581
+ def first_name = animal.first_name
1582
+ def last_name = animal.last_name
1583
+ def birthday = animal.birthday
1582
1584
 
1583
1585
  def birthday_to_age
1584
1586
  today = Date.today
@@ -7,6 +7,7 @@ module Foobara
7
7
  include Concern
8
8
  include NestedTransactionable
9
9
 
10
+ # TODO: move this out of here!
10
11
  def relevant_entity_classes
11
12
  return @relevant_entity_classes if defined?(@relevant_entity_classes)
12
13
 
@@ -1,4 +1,4 @@
1
- guard :rspec, all_after_pass: true, all_on_start: true, cmd: "bundle exec rspec", failed_mode: :focus do
1
+ guard :rspec, all_after_pass: false, all_on_start: true, cmd: "bundle exec rspec", failed_mode: :focus do
2
2
  watch(%r{^spec/(.+)_spec\.rb$})
3
3
  watch(%r{^src/(.+)\.rb$}) { "spec/" }
4
4
  watch(%r{^lib/(.+)\.rb$}) { "spec/" }
@@ -0,0 +1,201 @@
1
+ RSpec.describe Foobara::CommandConnector do
2
+ after do
3
+ Foobara.reset_alls
4
+ end
5
+
6
+ let(:command_class) do
7
+ stub_class(:ComputeExponent, Foobara::Command) do
8
+ inputs exponent: :integer, base: :integer
9
+ result :integer
10
+
11
+ def execute = base ** exponent
12
+ end
13
+ end
14
+
15
+ let(:command_connector) do
16
+ described_class.new(authenticator:, current_user: current_user_mapper)
17
+ end
18
+
19
+ let(:authenticator) do
20
+ proc { "some_user" }
21
+ end
22
+
23
+ let(:base) { 2 }
24
+ let(:exponent) { 3 }
25
+
26
+ let(:response) { command_connector.run(full_command_name:, action:, inputs:) }
27
+ let(:errors_hash) { response.body.errors_hash }
28
+
29
+ let(:action) { "run" }
30
+ let(:full_command_name) { "ComputeExponent" }
31
+ let(:inputs) do
32
+ { base:, exponent: }
33
+ end
34
+
35
+ describe "#run_command" do
36
+ class << self
37
+ def with_current_user_allowed_rule
38
+ let(:allowed_rule) do
39
+ -> { current_user == "current_user is some_user" }
40
+ end
41
+ end
42
+
43
+ def with_current_user_not_allowed_rule
44
+ let(:allowed_rule) do
45
+ -> { current_user == "asfdasdf" }
46
+ end
47
+ end
48
+
49
+ def with_authenticated_user_allowed_rule
50
+ let(:allowed_rule) do
51
+ -> { authenticated_user == "some_user" }
52
+ end
53
+ end
54
+
55
+ def with_authenticated_user_not_allowed_rule
56
+ let(:allowed_rule) do
57
+ -> { authenticated_user == "asfdasdf" }
58
+ end
59
+ end
60
+ end
61
+
62
+ shared_examples "a connector with auth mappers" do
63
+ before do
64
+ command_connector.connect(command_class, allowed_rule:)
65
+ end
66
+
67
+ context "when using current_user in the allowed rule" do
68
+ context "when allowed" do
69
+ with_current_user_allowed_rule
70
+
71
+ it "runs the command" do
72
+ expect(response.status).to be(0)
73
+ expect(response.body).to eq(8)
74
+ end
75
+ end
76
+
77
+ context "when not allowed" do
78
+ with_current_user_not_allowed_rule
79
+
80
+ it "fails with a relevant error" do
81
+ expect(response.status).to be(1)
82
+ expect(errors_hash.key?("runtime.not_allowed")).to be true
83
+ end
84
+ end
85
+
86
+ context "when calling current_user multiple times" do
87
+ let(:allowed_rule) do
88
+ -> { current_user && current_user == "current_user is some_user" }
89
+ end
90
+
91
+ it "runs the command" do
92
+ expect(response.status).to be(0)
93
+ expect(response.body).to eq(8)
94
+ end
95
+ end
96
+ end
97
+
98
+ context "when using authenticated_user in the allowed rule" do
99
+ context "when allowed" do
100
+ with_authenticated_user_allowed_rule
101
+
102
+ it "runs the command" do
103
+ expect(response.status).to be(0)
104
+ expect(response.body).to eq(8)
105
+ end
106
+ end
107
+
108
+ context "when not allowed" do
109
+ with_authenticated_user_not_allowed_rule
110
+
111
+ it "fails with a relevant error" do
112
+ expect(response.status).to be(1)
113
+ expect(errors_hash.key?("runtime.not_allowed")).to be true
114
+ end
115
+ end
116
+ end
117
+ end
118
+
119
+ context "when auth user mapper is a hash" do
120
+ let(:current_user_mapper) do
121
+ {
122
+ to: :string,
123
+ map: ->(authenticated_user) { "current_user is #{authenticated_user}" }
124
+ }
125
+ end
126
+
127
+ it_behaves_like "a connector with auth mappers"
128
+ end
129
+
130
+ context "when auth user mapper is an array" do
131
+ let(:current_user_mapper) { [:string, ->(authenticated_user) { "current_user is #{authenticated_user}" }] }
132
+
133
+ it_behaves_like "a connector with auth mappers"
134
+ end
135
+
136
+ context "when auth user mapper is a typed transformer class" do
137
+ let(:current_user_mapper) do
138
+ stub_class("SomeTypedTransformer", Foobara::TypeDeclarations::TypedTransformer) do
139
+ to :string
140
+
141
+ def transform(authenticated_user)
142
+ "current_user is #{authenticated_user}"
143
+ end
144
+ end
145
+ end
146
+
147
+ it_behaves_like "a connector with auth mappers"
148
+
149
+ context "when it is an instance" do
150
+ let(:current_user_mapper) { super().instance }
151
+
152
+ it_behaves_like "a connector with auth mappers"
153
+ end
154
+ end
155
+
156
+ context "when auth user mapper is a domain mapper" do
157
+ let(:current_user_mapper) do
158
+ stub_class("SomeDomainMapper", Foobara::DomainMapper) do
159
+ from :string
160
+ to :string
161
+
162
+ def map = "current_user is #{from}"
163
+ end
164
+ end
165
+
166
+ it_behaves_like "a connector with auth mappers"
167
+ end
168
+
169
+ context "when auth user mapper is a command" do
170
+ context "with only one input" do
171
+ let(:current_user_mapper) do
172
+ stub_class("SomeCommand", Foobara::Command) do
173
+ inputs auth_user: :string
174
+ result :string
175
+
176
+ def execute = "current_user is #{auth_user}"
177
+ end
178
+ end
179
+
180
+ it_behaves_like "a connector with auth mappers"
181
+ end
182
+
183
+ context "with multiple inputs but only one required input" do
184
+ let(:current_user_mapper) do
185
+ stub_class("SomeCommand", Foobara::Command) do
186
+ inputs do
187
+ foo :string
188
+ bar :string
189
+ auth_user :string, :required
190
+ end
191
+ result :string
192
+
193
+ def execute = "current_user is #{auth_user}"
194
+ end
195
+ end
196
+
197
+ it_behaves_like "a connector with auth mappers"
198
+ end
199
+ end
200
+ end
201
+ end
@@ -2201,7 +2201,8 @@ RSpec.describe Foobara::CommandConnector do
2201
2201
  command_connector_b.connect(command_class, :auth)
2202
2202
  command_connector_c.connect(command_class, requires_authentication: true)
2203
2203
  command_connector_d.connect(command_class, requires_authentication: true)
2204
- command_connector_e.connect(command_class, requires_authentication: true, authenticator: [authenticator_e])
2204
+ command_connector_e.connect(command_class, requires_authentication: true,
2205
+ authenticator: [authenticator_e])
2205
2206
 
2206
2207
  response = command_connector_a.run(full_command_name:, action:, inputs:)
2207
2208
  expect(response.status).to be(0)
@@ -76,14 +76,17 @@ RSpec.describe Foobara::CommandConnectors::Transformers::LoadAtomsPreCommitTrans
76
76
  end
77
77
 
78
78
  let(:command_connector) do
79
- Foobara::CommandConnector.new(default_pre_commit_transformers: described_class)
79
+ cc = command_class
80
+ Foobara::CommandConnector.new(default_pre_commit_transformers: described_class) do
81
+ command cc
82
+ end
80
83
  end
81
84
 
82
85
  let(:transformer) { described_class.new(to: type) }
83
86
 
84
87
  describe "#transform" do
85
88
  it "gives what was passed in because we expect this data to be cast where needed" do
86
- command_connector.connect(command_class)
89
+ command_connector
87
90
  response = command_connector.run(
88
91
  full_command_name: command_class.full_command_name,
89
92
  inputs: value,
@@ -92,4 +92,27 @@ RSpec.describe Foobara::CommandConnector::Request do
92
92
  end
93
93
  end
94
94
  end
95
+
96
+ context "with an auth-mapped method" do
97
+ let(:command_class) do
98
+ stub_class("SomeCommand", Foobara::Command) do
99
+ def self.requires_authentication = true
100
+ end
101
+ end
102
+
103
+ let(:request) do
104
+ foo_mapper = Foobara::TypeDeclarations::TypedTransformer.subclass(to: :symbol) { :bar }.instance
105
+ # TODO: make a block form of this
106
+ authenticator = Foobara::CommandConnector.to_authenticator(-> { :some_user })
107
+
108
+ described_class.new(authenticator:, auth_mappers: { foo: foo_mapper }).tap do |request|
109
+ request.command_class = command_class
110
+ request.authenticate
111
+ end
112
+ end
113
+
114
+ it "can access the auth-mapped method" do
115
+ expect(request.foo).to eq(:bar)
116
+ end
117
+ end
95
118
  end
@@ -2,9 +2,11 @@ ENV["FOOBARA_ENV"] = "test"
2
2
 
3
3
  require "bundler/setup"
4
4
 
5
- unless ENV["SKIP_PRY"] == "true" || ENV["CI"] == "true"
5
+ if ENV["RUBY_DEBUG"] == "true"
6
+ require "debug"
7
+ elsif ENV["SKIP_PRY"] != "true" && ENV["CI"] != "true"
6
8
  require "pry"
7
- require "pry-byebug"
9
+ require "pry-byebug" unless ENV["SKIP_BYEBUG"] == "true"
8
10
  end
9
11
 
10
12
  require "rspec/its"
@@ -22,13 +24,16 @@ end
22
24
 
23
25
  require "foobara/all"
24
26
  require "foobara/command_connectors"
27
+ # TODO: eliminate this require! We should not depend on entities in this project!
28
+ require "foobara/entities_plumbing"
25
29
 
26
30
  RSpec.configure do |config|
27
31
  # Need to do :all instead of :each because for specs that use .around,
28
32
  # .after(:each) do |example| here is called after example.run but before any threads created in
29
33
  # .around might have been cleaned up.
30
34
  config.after(:suite) do
31
- expect(Thread.list.size).to eq(1)
35
+ threads = Thread.list.reject { |t| t.name == "DEBUGGER__::SESSION@server" }
36
+ expect(threads.size).to eq(1)
32
37
  end
33
38
  config.filter_run_when_matching :focus
34
39
 
@@ -1,28 +1,39 @@
1
1
  module Foobara
2
2
  class CommandConnector
3
- # TODO: should switch to a processor and give errors if the authenticator header is malformed
4
- class Authenticator < Value::Transformer
5
- attr_reader :block
3
+ class Authenticator < TypeDeclarations::TypedTransformer
4
+ class << self
5
+ attr_writer :default_symbol
6
+ attr_accessor :default_explanation, :default_block
6
7
 
7
- def initialize(symbol: nil, explanation: nil, &block)
8
- symbol ||= Util.non_full_name_underscore(self.class).to_sym
9
- explanation ||= symbol
8
+ def subclass(to: nil, symbol: nil, explanation: nil, &default_block)
9
+ klass = super(to:, from: Request, &nil)
10
10
 
11
- super(symbol:, explanation:)
11
+ klass.default_symbol = symbol if symbol
12
+ klass.default_explanation = explanation if explanation
13
+ klass.default_block = default_block if default_block
12
14
 
13
- @block = block
14
- end
15
+ klass
16
+ end
15
17
 
16
- def relevant_entity_classes(_request)
17
- nil
18
- end
18
+ def default_symbol
19
+ @default_symbol || Util.non_full_name_underscore(self)&.to_sym
20
+ end
19
21
 
20
- def symbol
21
- declaration_data[:symbol]
22
+ def instance
23
+ @instance ||= new
24
+ end
22
25
  end
23
26
 
24
- def explanation
25
- declaration_data[:explanation]
27
+ attr_accessor :symbol
28
+ attr_reader :block, :explanation
29
+
30
+ def initialize(symbol: nil, explanation: nil, &block)
31
+ self.symbol = symbol || self.class.default_symbol
32
+ @explanation = explanation || self.class.default_explanation || self.symbol
33
+
34
+ super()
35
+
36
+ @block = block || self.class.default_block
26
37
  end
27
38
 
28
39
  def transform(request)
@@ -16,10 +16,12 @@ module Foobara
16
16
  :action,
17
17
  :response,
18
18
  :authenticated_user,
19
- :authenticated_credential
19
+ :authenticated_credential,
20
+ :auth_mappers,
21
+ :authenticator
20
22
 
21
23
  def initialize(**opts)
22
- valid_keys = [:inputs, :full_command_name, :action, :serializers]
24
+ valid_keys = [:inputs, :full_command_name, :action, :serializers, :authenticator, :auth_mappers]
23
25
 
24
26
  invalid_keys = opts.keys - valid_keys
25
27
 
@@ -31,6 +33,8 @@ module Foobara
31
33
 
32
34
  self.inputs = opts[:inputs] if opts.key?(:inputs)
33
35
  self.action = opts[:action] if opts.key?(:action)
36
+ self.auth_mappers = opts[:auth_mappers] if opts.key?(:auth_mappers)
37
+ self.authenticator = opts[:authenticator] if opts.key?(:authenticator)
34
38
  self.full_command_name = opts[:full_command_name] if opts.key?(:full_command_name)
35
39
  self.serializers = Util.array(opts[:serializers]) if opts.key?(:serializers)
36
40
  end
@@ -59,8 +63,38 @@ module Foobara
59
63
  end
60
64
  end
61
65
 
62
- def authenticator
63
- command_class.authenticator
66
+ def auth_mapped_method?(method_name)
67
+ auth_mappers&.key?(method_name)
68
+ end
69
+
70
+ def auth_mapper_for(name)
71
+ auth_mappers&.[](name.to_sym)
72
+ end
73
+
74
+ def auth_mapped_value_for(name)
75
+ return unless authenticated_user
76
+
77
+ @auth_mapper_cache ||= {}
78
+
79
+ if @auth_mapper_cache.key?(name)
80
+ @auth_mapper_cache[name]
81
+ else
82
+ @auth_mapper_cache[name] = auth_mapper_for(name)&.process_value!(authenticated_user)
83
+ end
84
+ end
85
+
86
+ def method_missing(method_name, ...)
87
+ if auth_mapped_method?(method_name)
88
+ auth_mapped_value_for(method_name)
89
+ else
90
+ # :nocov:
91
+ super
92
+ # :nocov:
93
+ end
94
+ end
95
+
96
+ def respond_to_missing?(method_name, private = false)
97
+ auth_mapped_method?(method_name) || super
64
98
  end
65
99
 
66
100
  def serializer
@@ -113,11 +147,12 @@ module Foobara
113
147
  outcome.error_collection
114
148
  end
115
149
 
150
+ # TODO: move this to entity_plumbing
116
151
  def relevant_entity_classes
117
152
  if command_class.is_a?(::Class) && command_class < TransformedCommand
118
153
  entity_classes = authenticator&.relevant_entity_classes(self)
119
154
  [*entity_classes, *relevant_entity_classes_from_inputs_transformer]
120
- end || []
155
+ end || EMPTY_ARRAY
121
156
  end
122
157
 
123
158
  private
@@ -63,7 +63,7 @@ module Foobara
63
63
  case object
64
64
  when Class
65
65
  if object < Authenticator
66
- object.new
66
+ object.instance
67
67
  else
68
68
  # :nocov:
69
69
  raise ArgumentError, "Expected a class that inherits from Authenticator"
@@ -116,19 +116,97 @@ module Foobara
116
116
  end
117
117
  end
118
118
  end
119
+
120
+ # don't call this auth_user ?
121
+ def to_auth_user_mapper(object)
122
+ case object
123
+ when TypeDeclarations::TypedTransformer
124
+ object
125
+ when ::Class
126
+ if object < TypeDeclarations::TypedTransformer
127
+ object.instance
128
+ elsif object < Foobara::DomainMapper
129
+ build_auth_mapper(object.to_type) { |authenticated_user| object.map!(authenticated_user) }
130
+ elsif object < Foobara::Command
131
+ inputs_type = object.inputs_type
132
+ element_types = inputs_type.element_types
133
+ size = element_types.size
134
+
135
+ first_required_input = if size == 1
136
+ element_types.keys.first
137
+ elsif size > 1
138
+ declaration = inputs_type&.declaration_data
139
+ required_attribute_names = declaration&.[](:required) || EMPTY_ARRAY
140
+
141
+ if required_attribute_names.size == 1
142
+ required_attribute_names.first
143
+ else
144
+ # :nocov:
145
+ raise ArgumentError,
146
+ "Ambiguous inputs when trying to use #{object} as a mapper. " \
147
+ "Should have either only 1 input or only 1 required input."
148
+ # :nocov:
149
+ end
150
+ else
151
+ # :nocov:
152
+ raise ArgumentError, "To use a command as a mapper it must take an input to map"
153
+ # :nocov:
154
+ end
155
+
156
+ build_auth_mapper(object.result_type) do |authenticated_user|
157
+ object.run!(first_required_input => authenticated_user)
158
+ end
159
+ else
160
+ # :nocov:
161
+ raise ArgumentError, "not sure how to convert a #{object} to an auth mapper"
162
+ # :nocov:
163
+ end
164
+ when ::Hash
165
+ object => { to:, map: }
166
+ build_auth_mapper(to, &map)
167
+ when ::Array
168
+ object => [to, map]
169
+ build_auth_mapper(to, &map)
170
+ else
171
+ # :nocov:
172
+ raise ArgumentError, "Not sure how to convert #{object} to an auth mapper"
173
+ # :nocov:
174
+ end
175
+ end
176
+
177
+ # TODO: make private
178
+ def build_auth_mapper(to_type, &)
179
+ TypeDeclarations::TypedTransformer.subclass(to: to_type, &).instance
180
+ end
119
181
  end
120
182
 
121
- attr_accessor :command_registry, :authenticator, :capture_unknown_error, :name
183
+ attr_accessor :command_registry, :authenticator, :capture_unknown_error, :name,
184
+ :auth_map
122
185
 
123
186
  def initialize(name: nil,
124
187
  authenticator: nil,
125
188
  capture_unknown_error: nil,
126
189
  default_serializers: nil,
127
- default_pre_commit_transformers: nil)
190
+ default_pre_commit_transformers: nil,
191
+ auth_map: nil,
192
+ current_user: nil,
193
+ &block)
128
194
  authenticator = self.class.to_authenticator(authenticator)
129
195
 
196
+ if current_user
197
+ auth_map ||= {}
198
+ auth_map[:current_user] = current_user
199
+ end
200
+
201
+ if auth_map
202
+ self.auth_map = auth_map.transform_values do |mapper|
203
+ self.class.to_auth_user_mapper(mapper)
204
+ end
205
+ end
206
+
130
207
  self.authenticator = authenticator
131
208
  self.command_registry = CommandRegistry.new(authenticator:)
209
+
132
210
  self.capture_unknown_error = capture_unknown_error
133
211
  self.name = name
134
212
 
@@ -143,8 +221,15 @@ module Foobara
143
221
  self.class.allowed_rules_to_register.each do |ruleish_args|
144
222
  command_registry.allowed_rule(*ruleish_args)
145
223
  end
224
+
225
+ if block
226
+ instance_eval(&block)
227
+ end
146
228
  end
147
229
 
230
+ # TODO: should this be the official way to connect a command instead of #connect ?
231
+ def command(...) = connect(...)
232
+
148
233
  def connect(*args, **opts)
149
234
  args, opts = desugarize_connect_args(args, opts)
150
235
 
@@ -472,15 +557,24 @@ module Foobara
472
557
 
473
558
  def build_request(...)
474
559
  self.class::Request.new(...).tap do |request|
475
- # TODO: feels like a smell
560
+ # TODO: feels like a smell? Can we deprecate this somehow? Request no longer calls command_connector it seems...
476
561
  request.command_connector = self
477
562
  end
478
563
  end
479
564
 
565
+ # TODO: get all this persistence stuff out of here and into entities plumbing somehow
480
566
  def run_request(request)
481
567
  command_class = determine_command_class(request)
482
568
  request.command_class = command_class
483
569
 
570
+ if command_class.respond_to?(:requires_authentication) && command_class.requires_authentication
571
+ request.authenticator ||= command_class.authenticator || authenticator
572
+
573
+ if auth_map
574
+ request.auth_mappers ||= auth_map
575
+ end
576
+ end
577
+
484
578
  return build_response(request) unless command_class
485
579
 
486
580
  begin
@@ -782,7 +782,9 @@ module Foobara
782
782
  end
783
783
 
784
784
  def method_missing(method_name, ...)
785
- if command.respond_to?(method_name)
785
+ if auth_mapped_method?(method_name)
786
+ auth_mapped_value_for(method_name)
787
+ elsif command.respond_to?(method_name)
786
788
  command.send(method_name, ...)
787
789
  else
788
790
  # :nocov:
@@ -791,8 +793,16 @@ module Foobara
791
793
  end
792
794
  end
793
795
 
796
+ def auth_mapped_value_for(name)
797
+ request.auth_mapped_value_for(name)
798
+ end
799
+
800
+ def auth_mapped_method?(method_name)
801
+ request&.auth_mapped_method?(method_name)
802
+ end
803
+
794
804
  def respond_to_missing?(method_name, private = false)
795
- command.respond_to?(method_name, private) || super
805
+ command.respond_to?(method_name, private) || auth_mapped_method?(method_name) || super
796
806
  end
797
807
 
798
808
  private
@@ -2,9 +2,11 @@ ENV["FOOBARA_ENV"] = "test"
2
2
 
3
3
  require "bundler/setup"
4
4
 
5
- unless ENV["SKIP_PRY"] == "true" || ENV["CI"] == "true"
5
+ if ENV["RUBY_DEBUG"] == "true"
6
+ require "debug"
7
+ elsif ENV["SKIP_PRY"] != "true" && ENV["CI"] != "true"
6
8
  require "pry"
7
- require "pry-byebug"
9
+ require "pry-byebug" unless ENV["SKIP_BYEBUG"] == "true"
8
10
  end
9
11
 
10
12
  require "rspec/its"