10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
|
# File 'lib/smart_brain/contracts/scope_context.rb', line 10
def self.normalize(domain_id:, session_id:, scope_context:, allowed_types: ScopeRef::DEFAULT_ALLOWED_TYPES)
normalized_domain = domain_id.to_s.strip
if normalized_domain.empty? && scope_context
raise ArgumentError, 'domain_id is required when scope_context is provided'
end
normalized_domain = LEGACY_DOMAIN_ID if normalized_domain.empty?
raise ArgumentError, 'session_id is required' if session_id.to_s.strip.empty?
raw = scope_context || legacy_context(session_id)
read = Array(raw[:read] || raw['read']).map { |ref| ScopeRef.normalize(ref, allowed_types: allowed_types) }
raise ArgumentError, 'scope_context.read must not be empty' if read.empty?
keys = read.map { |ref| "#{ref[:type]}:#{ref[:id]}" }
duplicates = keys.tally.select { |_key, count| count > 1 }.keys
raise ArgumentError, "duplicate scope refs: #{duplicates.join(', ')}" unless duplicates.empty?
default_write = ScopeRef.normalize(raw[:default_write] || raw['default_write'], allowed_types: allowed_types)
write_raw = raw.key?(:write) ? raw[:write] : raw['write']
write = Array(write_raw || [default_write]).map { |ref| ScopeRef.normalize(ref, allowed_types: allowed_types) }
write_keys = write.map { |ref| "#{ref[:type]}:#{ref[:id]}" }
write_duplicates = write_keys.tally.select { |_key, count| count > 1 }.keys
raise ArgumentError, "duplicate writable scope refs: #{write_duplicates.join(', ')}" unless write_duplicates.empty?
raise ArgumentError, 'scope_context.write must be included in read' unless write_keys.all? { |key| keys.include?(key) }
unless write_keys.include?("#{default_write[:type]}:#{default_write[:id]}")
raise ArgumentError, 'scope_context.default_write must be included in write'
end
{ domain_id: normalized_domain, read: read, write: write, default_write: default_write }
end
|