foobara 0.0.115 → 0.0.117
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 +19 -0
- data/projects/builtin_types/src/date/casters/hash.rb +1 -1
- data/projects/callback/src/block.rb +1 -1
- data/projects/command/src/command_pattern_implementation/concerns/callbacks.rb +2 -2
- data/projects/command/src/command_pattern_implementation/concerns/runtime.rb +7 -7
- data/projects/command/src/command_pattern_implementation/concerns/transactions.rb +10 -69
- data/projects/command/src/state_machine.rb +22 -22
- data/projects/command_connectors/src/authenticator.rb +7 -1
- data/projects/command_connectors/src/authenticator_selector.rb +8 -0
- data/projects/command_connectors/src/command_connector/command_connector_error.rb +1 -1
- data/projects/command_connectors/src/command_connector/request.rb +56 -11
- data/projects/command_connectors/src/command_connector/response.rb +4 -0
- data/projects/command_connectors/src/command_connector.rb +92 -97
- data/projects/command_connectors/src/command_registry.rb +1 -1
- data/projects/command_connectors/src/serializers/entities_to_primary_keys_serializer.rb +8 -1
- data/projects/{command → command_connectors}/src/transformed_command.rb +16 -7
- data/projects/detached_entity/src/concerns/attributes.rb +28 -0
- data/projects/detached_entity/src/concerns/initialization.rb +57 -0
- data/projects/detached_entity/src/concerns/persistence.rb +14 -0
- data/projects/detached_entity/src/detached_entity.rb +3 -0
- data/projects/detached_entity/src/extensions/builtin_types/detached_entity/casters/primary_key.rb +43 -0
- data/projects/entity/src/concerns/attributes.rb +4 -0
- data/projects/entity/src/concerns/callbacks.rb +16 -16
- data/projects/entity/src/concerns/initialization.rb +3 -0
- data/projects/entity/src/concerns/persistence.rb +1 -5
- data/projects/entity/src/extensions/builtin_types/entity/casters/primary_key.rb +3 -29
- data/projects/entity/src/sensitive_type_removers/entity.rb +15 -0
- data/projects/entity/src/sensitive_value_removers/entity.rb +14 -14
- data/projects/enumerated/src/values.rb +1 -1
- data/projects/foobara/lib/foobara/all.rb +2 -1
- data/projects/manifest/src/foobara/manifest/base_manifest.rb +2 -2
- data/projects/manifest/src/foobara/manifest/root_manifest.rb +1 -1
- data/projects/model/src/concerns/types.rb +3 -2
- data/projects/model/src/extensions/builtin_types/model/validators/model_instance_is_valid.rb +2 -2
- data/projects/model/src/extensions/type_declarations/handlers/extend_model_type_declaration/delegates_desugarizer.rb +2 -2
- data/projects/model/src/extensions/type_declarations/handlers/extend_model_type_declaration/delegates_validator.rb +1 -1
- data/projects/model/src/extensions/type_declarations/handlers/extend_model_type_declaration/model_class_desugarizer.rb +3 -1
- data/projects/model/src/model.rb +8 -5
- data/projects/model_attribute_helpers/src/attribute_helper_aliases.rb +11 -11
- data/projects/monorepo/lib/foobara/monorepo/project.rb +4 -1
- data/projects/nested_transactionable/lib/foobara/nested_transactionable.rb +95 -0
- data/projects/persistence/src/entity_base/transaction_table/concerns/record_tracking.rb +5 -5
- data/projects/type_declarations/lib/foobara/type_declarations.rb +4 -4
- data/projects/type_declarations/src/handlers/extend_array_type_declaration/type_set_to_array_desugarizer.rb +1 -1
- data/projects/type_declarations/src/handlers/extend_registered_type_declaration/to_type_transformer.rb +1 -1
- data/projects/type_declarations/src/type_declarations.rb +2 -2
- data/projects/value/src/caster.rb +2 -2
- data/projects/value/src/processor.rb +2 -2
- data/projects/value/src/transformer.rb +2 -2
- metadata +9 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3baedff1b00683e94d063e4a3c3453e89f9d8383aac79fd63a7b80590b7ac2ab
|
4
|
+
data.tar.gz: db340140596337684becb59db82ef8c9f64970195eace6d2cb0032d6e209eb88
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a9cfde792ff8303519b9b98ca46062a7e0dc4209799e00bd64bf3d5674112e685eb8cce621eb50cda6680c54f76084076007d3c18125baefe14eb052566f9038
|
7
|
+
data.tar.gz: db3398f6804081f01a44ac188a0597831db3b3ffa0dfa81ffb99de75c01685e33d21d4d91dbefae6e7d601bae29676e60c778e7be59cb3120bfde7dd7ba1b19a
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,22 @@
|
|
1
|
+
# [0.0.117] - 2025-05-05
|
2
|
+
|
3
|
+
- Make sure we do not apply an authenticator unless it is applicable
|
4
|
+
- Move loaded/unloaded concept up into DetachedEntity from Entity
|
5
|
+
- This allows us to deal with serialization of detached entities as their primary keys
|
6
|
+
- Fix bugs preventing connecting/importing types with delegated attributes
|
7
|
+
|
8
|
+
# [0.0.116] - 2025-05-03
|
9
|
+
|
10
|
+
- Add automatic transaction support to requests, cover in/out mutators/transformers
|
11
|
+
- This makes it so that authenticators and allowed rules can more cleanly be
|
12
|
+
expressed when they have the same entity bases needed by the transformed command
|
13
|
+
- Move #authenticated_user from TransformedCommand to Request
|
14
|
+
- Add a Request#authenticated_credential
|
15
|
+
- Provide a way to skip validations for models
|
16
|
+
- A type without sensitive types derived from an entity type will now be registered as a
|
17
|
+
detached entity
|
18
|
+
- Fix model type re-registering bug
|
19
|
+
|
1
20
|
# [0.0.115] - 2025-05-01
|
2
21
|
|
3
22
|
- Make sure Authenticator#authenticate hits #applicable?
|
@@ -4,7 +4,7 @@ module Foobara
|
|
4
4
|
module Casters
|
5
5
|
class Hash < Value::Caster
|
6
6
|
def applicable?(hash)
|
7
|
-
hash.is_a?(::Hash) && hash.keys.size == 3 && (hash.keys.map(&:to_s).sort ==
|
7
|
+
hash.is_a?(::Hash) && hash.keys.size == 3 && (hash.keys.map(&:to_s).sort == ["day", "month", "year"])
|
8
8
|
end
|
9
9
|
|
10
10
|
def applies_message
|
@@ -56,7 +56,7 @@ module Foobara
|
|
56
56
|
end
|
57
57
|
|
58
58
|
def has_keyword_args?
|
59
|
-
@has_keyword_args ||= param_types.any? { |type|
|
59
|
+
@has_keyword_args ||= param_types.any? { |type| [:keyreq, :keyrest].include?(type) }
|
60
60
|
end
|
61
61
|
|
62
62
|
def has_positional_args?
|
@@ -28,7 +28,7 @@ module Foobara
|
|
28
28
|
self.subclass_defined_callbacks ||= Foobara::Callback::Registry::SingleAction.new
|
29
29
|
|
30
30
|
[self, singleton_class].each do |target|
|
31
|
-
|
31
|
+
[:before, :after].each do |type|
|
32
32
|
target.define_method "#{type}_any_transition" do |&block|
|
33
33
|
callback_state_machine_target.register_transition_callback(type) do |state_machine:, **args|
|
34
34
|
block.call(command: state_machine.owner, **args)
|
@@ -61,7 +61,7 @@ module Foobara
|
|
61
61
|
|
62
62
|
Foobara::Command::StateMachine.transitions.each do |transition|
|
63
63
|
[self, singleton_class].each do |target|
|
64
|
-
|
64
|
+
[:before, :after].each do |type|
|
65
65
|
target.define_method "#{type}_#{transition}" do |&block|
|
66
66
|
callback_state_machine_target.register_transition_callback(
|
67
67
|
type, transition:
|
@@ -27,13 +27,13 @@ module Foobara
|
|
27
27
|
Foobara::Namespace.use self.class do
|
28
28
|
invoke_with_callbacks_and_transition(:open_transaction)
|
29
29
|
|
30
|
-
invoke_with_callbacks_and_transition_in_transaction(
|
31
|
-
cast_and_validate_inputs
|
32
|
-
load_records
|
33
|
-
validate_records
|
34
|
-
validate
|
35
|
-
run_execute
|
36
|
-
commit_transaction
|
30
|
+
invoke_with_callbacks_and_transition_in_transaction([
|
31
|
+
:cast_and_validate_inputs,
|
32
|
+
:load_records,
|
33
|
+
:validate_records,
|
34
|
+
:validate,
|
35
|
+
:run_execute,
|
36
|
+
:commit_transaction
|
37
37
|
])
|
38
38
|
|
39
39
|
invoke_with_callbacks_and_transition(:succeed)
|
@@ -3,81 +3,22 @@ module Foobara
|
|
3
3
|
module Concerns
|
4
4
|
module Transactions
|
5
5
|
include Concern
|
6
|
-
|
7
|
-
def transactions
|
8
|
-
@transactions ||= []
|
9
|
-
end
|
10
|
-
|
11
|
-
def opened_transactions
|
12
|
-
@opened_transactions ||= []
|
13
|
-
end
|
14
|
-
|
15
|
-
def auto_detect_current_transactions
|
16
|
-
bases = relevant_entity_classes.map(&:entity_base).uniq
|
17
|
-
|
18
|
-
bases.each do |base|
|
19
|
-
tx = base.current_transaction
|
20
|
-
transactions << tx if tx
|
21
|
-
end
|
22
|
-
end
|
6
|
+
include NestedTransactionable
|
23
7
|
|
24
8
|
def relevant_entity_classes
|
25
|
-
@relevant_entity_classes
|
26
|
-
entity_classes = if inputs_type
|
27
|
-
Entity.construct_associations(
|
28
|
-
inputs_type
|
29
|
-
).values.uniq.map(&:target_class)
|
30
|
-
else
|
31
|
-
[]
|
32
|
-
end
|
33
|
-
|
34
|
-
if result_type
|
35
|
-
entity_classes += Entity.construct_associations(
|
36
|
-
result_type
|
37
|
-
).values.uniq.map(&:target_class)
|
38
|
-
|
39
|
-
if result_type.extends?(BuiltinTypes[:entity])
|
40
|
-
entity_classes << result_type.target_class
|
41
|
-
end
|
42
|
-
end
|
9
|
+
return @relevant_entity_classes if defined?(@relevant_entity_classes)
|
43
10
|
|
44
|
-
|
45
|
-
|
46
|
-
|
11
|
+
entity_classes = if inputs_type
|
12
|
+
relevant_entity_classes_for_type(inputs_type)
|
13
|
+
else
|
14
|
+
[]
|
15
|
+
end
|
47
16
|
|
48
|
-
|
17
|
+
if result_type
|
18
|
+
entity_classes += relevant_entity_classes_for_type(result_type)
|
49
19
|
end
|
50
|
-
end
|
51
|
-
|
52
|
-
def open_transaction
|
53
|
-
auto_detect_current_transactions
|
54
|
-
|
55
|
-
bases_not_needing_transaction = transactions.map(&:entity_base)
|
56
|
-
|
57
|
-
bases_needing_transaction = relevant_entity_classes.map(&:entity_base).uniq - bases_not_needing_transaction
|
58
|
-
|
59
|
-
bases_needing_transaction.each do |entity_base|
|
60
|
-
transaction = entity_base.transaction
|
61
|
-
transaction.open!
|
62
|
-
opened_transactions << transaction
|
63
|
-
transactions << transaction
|
64
|
-
end
|
65
|
-
end
|
66
|
-
|
67
|
-
def rollback_transaction
|
68
|
-
opened_transactions.reverse.each do |transaction|
|
69
|
-
if transaction.currently_open?
|
70
|
-
# Hard to test this because halting and other exceptions rollback the transactions via
|
71
|
-
# block form but to be safe keeping this
|
72
|
-
# :nocov:
|
73
|
-
transaction.rollback!
|
74
|
-
# :nocov:
|
75
|
-
end
|
76
|
-
end
|
77
|
-
end
|
78
20
|
|
79
|
-
|
80
|
-
opened_transactions.reverse.each(&:commit!)
|
21
|
+
@relevant_entity_classes = [*entity_classes, *self.class.depends_on_entities].uniq
|
81
22
|
end
|
82
23
|
end
|
83
24
|
end
|
@@ -1,31 +1,31 @@
|
|
1
1
|
module Foobara
|
2
2
|
class Command
|
3
3
|
class StateMachine < Foobara::StateMachine
|
4
|
-
transitions =
|
5
|
-
open_transaction
|
6
|
-
cast_and_validate_inputs
|
7
|
-
load_records
|
8
|
-
validate_records
|
9
|
-
validate
|
10
|
-
run_execute
|
11
|
-
commit_transaction
|
12
|
-
succeed
|
13
|
-
error
|
14
|
-
fail
|
15
|
-
reset
|
4
|
+
transitions = [
|
5
|
+
:open_transaction,
|
6
|
+
:cast_and_validate_inputs,
|
7
|
+
:load_records,
|
8
|
+
:validate_records,
|
9
|
+
:validate,
|
10
|
+
:run_execute,
|
11
|
+
:commit_transaction,
|
12
|
+
:succeed,
|
13
|
+
:error,
|
14
|
+
:fail,
|
15
|
+
:reset
|
16
16
|
]
|
17
17
|
|
18
|
-
terminal_states =
|
18
|
+
terminal_states = [:succeeded, :errored, :failed]
|
19
19
|
|
20
|
-
states =
|
21
|
-
initialized
|
22
|
-
transaction_opened
|
23
|
-
inputs_casted_and_validated
|
24
|
-
loaded_records
|
25
|
-
validated_records
|
26
|
-
validated_execution
|
27
|
-
executing
|
28
|
-
transaction_committed
|
20
|
+
states = [
|
21
|
+
:initialized,
|
22
|
+
:transaction_opened,
|
23
|
+
:inputs_casted_and_validated,
|
24
|
+
:loaded_records,
|
25
|
+
:validated_records,
|
26
|
+
:validated_execution,
|
27
|
+
:executing,
|
28
|
+
:transaction_committed
|
29
29
|
] + terminal_states
|
30
30
|
|
31
31
|
can_fail_states = states - terminal_states
|
@@ -13,6 +13,10 @@ module Foobara
|
|
13
13
|
@block = block
|
14
14
|
end
|
15
15
|
|
16
|
+
def relevant_entity_classes(_request)
|
17
|
+
nil
|
18
|
+
end
|
19
|
+
|
16
20
|
def symbol
|
17
21
|
declaration_data[:symbol]
|
18
22
|
end
|
@@ -26,7 +30,9 @@ module Foobara
|
|
26
30
|
end
|
27
31
|
|
28
32
|
def authenticate(request)
|
29
|
-
|
33
|
+
if applicable?(request)
|
34
|
+
process_value!(request)
|
35
|
+
end
|
30
36
|
end
|
31
37
|
|
32
38
|
def to_proc
|
@@ -24,6 +24,14 @@ module Foobara
|
|
24
24
|
def authenticate(request)
|
25
25
|
selector.process_value!(request)
|
26
26
|
end
|
27
|
+
|
28
|
+
def relevant_entity_classes(request)
|
29
|
+
outcome = selector.processor_for(request)
|
30
|
+
|
31
|
+
if outcome.success?
|
32
|
+
outcome.result&.relevant_entity_classes(request)
|
33
|
+
end
|
34
|
+
end
|
27
35
|
end
|
28
36
|
end
|
29
37
|
end
|
@@ -2,9 +2,11 @@ module Foobara
|
|
2
2
|
class CommandConnector
|
3
3
|
class Request
|
4
4
|
include TruncatedInspect
|
5
|
+
include NestedTransactionable
|
5
6
|
|
6
7
|
# TODO: this feels like a smell of some sort...
|
7
8
|
attr_accessor :command,
|
9
|
+
:command_class,
|
8
10
|
:error,
|
9
11
|
:command_connector,
|
10
12
|
# Why aren't there serializers on the response?
|
@@ -12,12 +14,12 @@ module Foobara
|
|
12
14
|
:inputs,
|
13
15
|
:full_command_name,
|
14
16
|
:action,
|
15
|
-
:response
|
16
|
-
|
17
|
-
|
17
|
+
:response,
|
18
|
+
:authenticated_user,
|
19
|
+
:authenticated_credential
|
18
20
|
|
19
21
|
def initialize(**opts)
|
20
|
-
valid_keys =
|
22
|
+
valid_keys = [:inputs, :full_command_name, :action, :serializers]
|
21
23
|
|
22
24
|
invalid_keys = opts.keys - valid_keys
|
23
25
|
|
@@ -33,8 +35,8 @@ module Foobara
|
|
33
35
|
self.serializers = Util.array(opts[:serializers]) if opts.key?(:serializers)
|
34
36
|
end
|
35
37
|
|
36
|
-
def
|
37
|
-
|
38
|
+
def mutate_request
|
39
|
+
return if error
|
38
40
|
|
39
41
|
# TODO: we really need to revisit these interfaces. Something is wrong.
|
40
42
|
if command_class.respond_to?(:mutate_request)
|
@@ -42,6 +44,25 @@ module Foobara
|
|
42
44
|
end
|
43
45
|
end
|
44
46
|
|
47
|
+
def authenticate
|
48
|
+
return if error
|
49
|
+
return unless command_class.respond_to?(:requires_authentication) && command_class.requires_authentication
|
50
|
+
|
51
|
+
authenticated_user, authenticated_credential = authenticator.authenticate(self)
|
52
|
+
|
53
|
+
# TODO: why are these on the command instead of the request??
|
54
|
+
self.authenticated_user = authenticated_user
|
55
|
+
self.authenticated_credential = authenticated_credential
|
56
|
+
|
57
|
+
unless authenticated_user
|
58
|
+
self.error = CommandConnector::UnauthenticatedError.new
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def authenticator
|
63
|
+
command_class.authenticator
|
64
|
+
end
|
65
|
+
|
45
66
|
def serializer
|
46
67
|
return @serializer if defined?(@serializer)
|
47
68
|
|
@@ -77,12 +98,10 @@ module Foobara
|
|
77
98
|
end
|
78
99
|
|
79
100
|
def outcome
|
80
|
-
|
81
|
-
|
82
|
-
if outcome
|
83
|
-
outcome
|
84
|
-
elsif error
|
101
|
+
if error
|
85
102
|
Outcome.error(error)
|
103
|
+
else
|
104
|
+
command&.outcome
|
86
105
|
end
|
87
106
|
end
|
88
107
|
|
@@ -94,8 +113,34 @@ module Foobara
|
|
94
113
|
outcome.error_collection
|
95
114
|
end
|
96
115
|
|
116
|
+
def relevant_entity_classes
|
117
|
+
if command_class.is_a?(::Class) && command_class < TransformedCommand
|
118
|
+
entity_classes = authenticator&.relevant_entity_classes(self)
|
119
|
+
[*entity_classes, *relevant_entity_classes_from_inputs_transformer]
|
120
|
+
end || []
|
121
|
+
end
|
122
|
+
|
97
123
|
private
|
98
124
|
|
125
|
+
def relevant_entity_classes_from_inputs_transformer(
|
126
|
+
object = [*command_class.inputs_transformer, *command_class.result_transformer]
|
127
|
+
)
|
128
|
+
case object
|
129
|
+
when TypeDeclarations::TypedTransformer
|
130
|
+
relevant_entity_classes_from_inputs_transformer([*object.from_type, *object.to_type])
|
131
|
+
when Types::Type
|
132
|
+
relevant_entity_classes_for_type(object)
|
133
|
+
when ::Array
|
134
|
+
object.map do |o|
|
135
|
+
relevant_entity_classes_from_inputs_transformer(o)
|
136
|
+
end.flatten
|
137
|
+
when Value::Processor::Pipeline
|
138
|
+
relevant_entity_classes_from_inputs_transformer(object.processors)
|
139
|
+
else
|
140
|
+
[]
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
99
144
|
def objects_to_serializers(objects)
|
100
145
|
objects.map do |object|
|
101
146
|
case object
|