Skip to content

Commit

Permalink
Fix content negotiation implementation (athena-framework/athena#431)
Browse files Browse the repository at this point in the history
* Restrict Method request matcher to Array
  • Loading branch information
Blacksmoke16 authored Jul 30, 2024
1 parent b30d63f commit 474dd12
Show file tree
Hide file tree
Showing 3 changed files with 59 additions and 7 deletions.
54 changes: 51 additions & 3 deletions spec/bundle_spec.cr
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ end
describe ATH::Bundle, tags: "compiled" do
describe ATH::Listeners::CORS do
it "wildcard allow_headers with allow_credentials" do
assert_error "'expose_headers' cannot contain a wildcard ('*') when 'allow_credentials' is 'true'.", <<-CODE
assert_error "'expose_headers' cannot contain a wildcard ('*') when 'allow_credentials' is 'true'.", <<-'CODE'
ATH.configure({
framework: {
cors: {
Expand All @@ -38,9 +38,8 @@ describe ATH::Bundle, tags: "compiled" do
CODE
end

# TODO: Is there a better way to test bundle extension logic?
it "correctly wires up the listener based on its configuration" do
assert_success <<-CODE, codegen: true
assert_success <<-'CODE', codegen: true
ATH.configure({
framework: {
cors: {
Expand Down Expand Up @@ -69,4 +68,53 @@ describe ATH::Bundle, tags: "compiled" do
CODE
end
end

describe ATH::Listeners::Format do
it "correctly wires up the listener based on its configuration" do
assert_success <<-'CODE', codegen: true
ATH.configure({
framework: {
format_listener: {
enabled: true,
rules: [
{priorities: ["json", "xml"], host: /api\.example\.com/, fallback_format: "json"},
{path: /^\/image/, priorities: ["jpeg", "gif"], fallback_format: false, stop: true},
{methods: ["HEAD"], priorities: ["xml", "html"], prefer_extension: false},
{path: /^\/image/, priorities: ["foo"]},
],
},
},
})

it do
negotiator = ADI.container.athena_framework_listeners_format.as(ATH::Listeners::Format).@format_negotiator.as(ATH::View::FormatNegotiator)
map = negotiator.@map
map.size.should eq 4

# Hostname rule
matcher, rule = map[0]
rule.should eq ATH::View::FormatNegotiator::Rule.new(fallback_format: "json", prefer_extension: true, priorities: ["json", "xml"], stop: false)
m0 = matcher.should be_a ATH::RequestMatcher
m0.@matchers.should eq [ATH::RequestMatcher::Hostname.new(/api\.example\.com/)]

# Path rule
matcher, rule = map[1]
rule.should eq ATH::View::FormatNegotiator::Rule.new(fallback_format: false, prefer_extension: true, priorities: ["jpeg", "gif"], stop: true)
m1 = matcher.should be_a ATH::RequestMatcher
m1.@matchers.should eq [ATH::RequestMatcher::Path.new(/^\/image/)]

# Methods rule
matcher, rule = map[2]
rule.should eq ATH::View::FormatNegotiator::Rule.new(fallback_format: "json", prefer_extension: false, priorities: ["xml", "html"], stop: false)
m2 = matcher.should be_a ATH::RequestMatcher
m2.@matchers.should eq [ATH::RequestMatcher::Method.new("HEAD")]

# Tests matcher reuse logic
matcher, _ = map[3]
m3 = matcher.should be_a ATH::RequestMatcher
m3.should be m1
end
CODE
end
end
end
8 changes: 6 additions & 2 deletions src/bundle.cr
Original file line number Diff line number Diff line change
Expand Up @@ -285,7 +285,7 @@ struct Athena::Framework::Bundle < Athena::Framework::AbstractBundle
end

if v = rule["host"]
matchers << "ATH::RequestMatcher::Host.new(#{v})".id
matchers << "ATH::RequestMatcher::Hostname.new(#{v})".id
end

if v = rule["methods"]
Expand All @@ -312,10 +312,14 @@ struct Athena::Framework::Bundle < Athena::Framework::AbstractBundle
)".id}}
end

SERVICE_HASH["athena_framework_listeners_format"] = {
SERVICE_HASH["athena_framework_view_format_negotiator"] = {
class: ATH::View::FormatNegotiator,
calls: calls,
}

SERVICE_HASH["athena_framework_listeners_format"] = {
class: ATH::Listeners::Format,
}
end
%}

Expand Down
4 changes: 2 additions & 2 deletions src/request_matcher/method.cr
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,13 @@
struct Athena::Framework::RequestMatcher::Method
include Interface

@methods : Enumerable(String)
@methods : Array(String)

def self.new(*methods : String)
new methods.to_a
end

def initialize(@methods : Enumerable(String))
def initialize(@methods : Array(String))
methods.map! &.upcase
end

Expand Down

0 comments on commit 474dd12

Please sign in to comment.