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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +15 -0
- data/README.md +3 -1
- data/projects/command/src/command_pattern_implementation/concerns/transactions.rb +1 -0
- data/projects/command_connectors/Guardfile +1 -1
- data/projects/command_connectors/spec/auth_mappers_spec.rb +201 -0
- data/projects/command_connectors/spec/command_connector_spec.rb +2 -1
- data/projects/command_connectors/spec/pre_commit_transformers/load_atoms_pre_commit_transformer_spec.rb +5 -2
- data/projects/command_connectors/spec/request_spec.rb +23 -0
- data/projects/command_connectors/spec/spec_helper.rb +8 -3
- data/projects/command_connectors/src/authenticator.rb +27 -16
- data/projects/command_connectors/src/command_connector/request.rb +40 -5
- data/projects/command_connectors/src/command_connector.rb +98 -4
- data/projects/command_connectors/src/transformed_command.rb +12 -2
- data/projects/entities/spec/spec_helper.rb +4 -2
- data/projects/entities_plumbing/Guardfile +6 -0
- data/projects/entities_plumbing/lib/foobara/entities_plumbing.rb +12 -0
- data/projects/entities_plumbing/spec/auth_mappers_spec.rb +159 -0
- data/projects/entities_plumbing/spec/spec_helper.rb +54 -0
- data/projects/entities_plumbing/spec/support/rubyprof.rb +32 -0
- data/projects/entities_plumbing/spec/support/term_trap.rb +6 -0
- data/projects/entities_plumbing/src/command_connectors_extension.rb +17 -0
- data/projects/entities_plumbing/src/extensions/authenticator.rb +13 -0
- data/projects/manifest/spec/spec_helper.rb +6 -3
- data/projects/typesystem/projects/builtin_types/src/builtin_types.rb +6 -6
- data/projects/typesystem/projects/domain/src/domain.rb +0 -15
- data/projects/typesystem/projects/domain/src/domain_module_extension.rb +0 -153
- data/projects/typesystem/projects/domain/src/module_extension.rb +0 -58
- data/projects/typesystem/projects/namespace/src/is_namespace.rb +8 -0
- data/projects/typesystem/projects/type_declarations/lib/foobara/type_declarations.rb +0 -15
- data/projects/typesystem/projects/type_declarations/src/type_declaration.rb +15 -12
- data/projects/typesystem/projects/type_declarations/src/typed_transformer.rb +20 -2
- data/projects/typesystem/spec/common/data_path_spec.rb +6 -8
- data/projects/typesystem/spec/spec_helper.rb +5 -3
- data/version.rb +1 -1
- metadata +11 -2
- 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:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: db631f3175aa83feaee1a634560a7086c24e3c46b5c7df0b0a958252b531a377
|
|
4
|
+
data.tar.gz: 479a5728d5c86fdb689841d8cb422c564330d55a0502acbe3a32c27891573694
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
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
|
-
|
|
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
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
guard :rspec, all_after_pass:
|
|
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,
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
4
|
-
|
|
5
|
-
|
|
3
|
+
class Authenticator < TypeDeclarations::TypedTransformer
|
|
4
|
+
class << self
|
|
5
|
+
attr_writer :default_symbol
|
|
6
|
+
attr_accessor :default_explanation, :default_block
|
|
6
7
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
explanation ||= symbol
|
|
8
|
+
def subclass(to: nil, symbol: nil, explanation: nil, &default_block)
|
|
9
|
+
klass = super(to:, from: Request, &nil)
|
|
10
10
|
|
|
11
|
-
|
|
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
|
-
|
|
14
|
-
|
|
15
|
+
klass
|
|
16
|
+
end
|
|
15
17
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
18
|
+
def default_symbol
|
|
19
|
+
@default_symbol || Util.non_full_name_underscore(self)&.to_sym
|
|
20
|
+
end
|
|
19
21
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
+
def instance
|
|
23
|
+
@instance ||= new
|
|
24
|
+
end
|
|
22
25
|
end
|
|
23
26
|
|
|
24
|
-
|
|
25
|
-
|
|
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
|
|
63
|
-
|
|
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.
|
|
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
|
|
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
|
-
|
|
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"
|