yes-command-api 1.3.0 → 2.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 3f926af4f6824aeb10379876f0eff6f037a6a1991fb1a39d43a18145a3c02bd2
4
- data.tar.gz: abbd434a020d8c47c6d6aadb52e75fe5bb22afd44fa0f3451686e15dd9cf979f
3
+ metadata.gz: 161adcb38c52851d967b1f5b5d18d9b7cd815ae9c434b61ea05569792c3186e1
4
+ data.tar.gz: f59882bc2561b1b16bc4a52acdae8d86f4736e07ecb5cefc5eec8daf4b71089d
5
5
  SHA512:
6
- metadata.gz: 86e7a885bb6345c80a5f3ccd3cceb08f228983a8edb91723670672770e00a9b7305deca5b395fb6db0e0eb188e18fa779247967a045c1d2050a3e175aeeace33
7
- data.tar.gz: 80a578e95673a58ba2d8798175920dc4f5667d6553d2fff9694769f94e58435163bfcd3bd6dfd60d77424eab479a580aaeb95940b46c38cd5dda825ce7cad4d7
6
+ metadata.gz: 956401e81a5ed46e22908f05c8b8f5130a07752c75bf44cc04c5dc2db323d586fd1cf97561d766c4dd7967294ce74c2cc8f65418b212d32ede0ffe546820acd1
7
+ data.tar.gz: 0ddfdffce145206db7ecac209d8cf420cb9a3f4b450a1e901b39167b26ea818d535c6f975ded66cdf158c71f997b56a2ac27ed4efae9ef9c0d99d40171954301
@@ -100,10 +100,21 @@ module Yes
100
100
  render json: { error: }, status: :unprocessable_content
101
101
  end
102
102
 
103
+ # Unwraps both legacy stateless `Yes::Core::Commands::Group` and
104
+ # the new aggregate-DSL `Yes::Core::Commands::CommandGroup` into
105
+ # their sub-commands for batch authorization + validation. This
106
+ # is used ONLY by `BatchAuthorizer`/`BatchValidator`; the wrapped
107
+ # originals are still passed to `command_bus.call` so the
108
+ # Processor dispatches groups as single atomic units.
103
109
  def expand_commands(deserialize_commands)
104
- deserialize_commands.map do |command|
105
- command.is_a?(Yes::Core::Commands::Group) ? command.commands : command
106
- end.flatten
110
+ deserialize_commands.flat_map do |command|
111
+ case command
112
+ when Yes::Core::Commands::Group, Yes::Core::Commands::CommandGroup
113
+ command.commands
114
+ else
115
+ command
116
+ end
117
+ end
107
118
  end
108
119
 
109
120
  def add_metadata(commands)
@@ -20,11 +20,28 @@ module Yes
20
20
  # @raise [CommandsNotAuthorized] if any command is not authorized
21
21
  # @return [void]
22
22
  def call(commands, auth_data)
23
- unauthorized = []
23
+ unauthorized = Yes::Core::Authorization::LookupCache.with_scope do
24
+ authorize_each(commands, auth_data)
25
+ end
26
+
27
+ return unless unauthorized.any?
24
28
 
25
- commands.each do |command|
26
- authorizer = authorizer_for(command)
27
- authorizer.call(command, auth_data)
29
+ trace_error('Unauthorized', { unauthorized: unauthorized.to_json })
30
+ raise CommandsNotAuthorized.new(extra: unauthorized)
31
+ end
32
+ otl_trackable :call, Yes::Core::OpenTelemetry::OtlSpan::OtlData.new(span_name: 'Authorize Commands')
33
+
34
+ private
35
+
36
+ # Authorizes every command, collecting the ones that were rejected instead
37
+ # of failing on the first rejection, so the caller can report all of them.
38
+ #
39
+ # @param commands [Array<Yes::Core::Command>] commands to authorize
40
+ # @param auth_data [Hash] authorization data
41
+ # @return [Array<Hash>] unauthorized command data
42
+ def authorize_each(commands, auth_data)
43
+ commands.each_with_object([]) do |command, unauthorized|
44
+ authorizer_for(command).call(command, auth_data)
28
45
  rescue CommandAuthorizerNotFound
29
46
  unauthorized << unauthorized_data(command, 'Not allowed').tap do
30
47
  trace_error('Command authorizer not found', { command: command.to_json })
@@ -34,15 +51,7 @@ module Yes
34
51
  trace_error('Command not authorized', { command: })
35
52
  end
36
53
  end
37
-
38
- return unless unauthorized.any?
39
-
40
- trace_error('Unauthorized', { unauthorized: unauthorized.to_json })
41
- raise CommandsNotAuthorized.new(extra: unauthorized)
42
54
  end
43
- otl_trackable :call, Yes::Core::OpenTelemetry::OtlSpan::OtlData.new(span_name: 'Authorize Commands')
44
-
45
- private
46
55
 
47
56
  # Returns the command authorizer for the given command.
48
57
  #
@@ -47,12 +47,21 @@ module Yes
47
47
 
48
48
  private
49
49
 
50
- # Resolves the command class name, trying command group, V2, then V1 conventions.
50
+ # Resolves the command class name, trying (in order):
51
+ # 1. legacy top-level command group: `CommandGroups::<Name>::Command`
52
+ # 2. aggregate-DSL command group: `<Ctx>::<Subj>::CommandGroups::<Name>::Command`
53
+ # 3. V2 command: `<Ctx>::<Subj>::Commands::<Name>::Command`
54
+ # 4. V1 command: `<Ctx>::Commands::<Subj>::<Name>`
51
55
  #
52
56
  # @param command [Hash] command data
53
57
  # @return [String] command class name
54
58
  def command_class_name(command)
55
- [command_group_class(command), command_v2_class(command), command_class(command)].each do |name|
59
+ [
60
+ command_group_class(command),
61
+ command_group_v2_class(command),
62
+ command_v2_class(command),
63
+ command_class(command)
64
+ ].each do |name|
56
65
  Kernel.const_get(name)
57
66
  return name
58
67
  rescue NameError
@@ -78,13 +87,23 @@ module Yes
78
87
  "#{command[:context]}::#{command[:subject]}::Commands::#{command[:command]}::Command"
79
88
  end
80
89
 
81
- # Returns the command group class name.
90
+ # Returns the legacy top-level command group class name (used by
91
+ # stateless cross-aggregate groups).
82
92
  #
83
93
  # @param command [Hash] command data
84
94
  # @return [String] command group class name
85
95
  def command_group_class(command)
86
96
  "CommandGroups::#{command[:command]}::Command"
87
97
  end
98
+
99
+ # Returns the aggregate-DSL command group class name (generated
100
+ # by the `command_group` macro inside an aggregate body).
101
+ #
102
+ # @param command [Hash] command data
103
+ # @return [String] command group class name
104
+ def command_group_v2_class(command)
105
+ "#{command[:context]}::#{command[:subject]}::CommandGroups::#{command[:command]}::Command"
106
+ end
88
107
  end
89
108
  end
90
109
  end
@@ -3,7 +3,7 @@
3
3
  module Yes
4
4
  module Command
5
5
  module Api
6
- VERSION = '1.3.0'
6
+ VERSION = '2.2.0'
7
7
  end
8
8
  end
9
9
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: yes-command-api
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.3.0
4
+ version: 2.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Nico Ritsche