Skip to content

Commit ca98bb0

Browse files
committed
APIB: allows routes with optional query strings
This allows for routes to be defined with optional querystrings, like: ```ruby route '/orders/:id{?optional=:optional}', "Single Order" do ``` Before this, the http methods (e.g `get`, `post`) were injecting the optional into the final URI. This moves optionals into another metadata key (`options[:route_optionals]`).
1 parent c62e3a3 commit ca98bb0

File tree

6 files changed

+67
-27
lines changed

6 files changed

+67
-27
lines changed

features/api_blueprint_documentation.feature

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -107,8 +107,9 @@ Feature: Generate API Blueprint documentation from test examples
107107
end
108108
end
109109
110-
route '/orders/{id}', "Single Order" do
110+
route '/orders/:id{?optional=:optional}', "Single Order" do
111111
parameter :id, 'Order id', required: true, type: 'string', :example => '1'
112+
parameter :optional
112113
113114
attribute :name, 'The order name', required: true, :example => 'a name'
114115
attribute :amount, required: false
@@ -225,7 +226,7 @@ Feature: Generate API Blueprint documentation from test examples
225226
* Getting a list of orders
226227
POST Creates an order
227228
* Creating an order
228-
/orders/{id} Single Order
229+
/orders/:id{?optional=:optional} Single Order
229230
GET Returns a single order
230231
* Getting a specific order
231232
PUT Updates a single order
@@ -353,10 +354,11 @@ Feature: Generate API Blueprint documentation from test examples
353354
]
354355
}
355356
356-
## Single Order [/orders/{id}]
357+
## Single Order [/orders/:id{?optional=:optional}]
357358
358359
+ Parameters
359360
+ id: 1 (required, string) - Order id
361+
+ optional
360362
361363
+ Attributes (object)
362364
+ name: a name (required) - The order name

lib/rspec_api_documentation/dsl/resource.rb

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,8 @@ def route(*args, &block)
4747
raise "You must define the route URI" if args[0].blank?
4848
raise "You must define the route name" if args[1].blank?
4949
options = args.extract_options!
50-
options[:route_uri] = args[0]
50+
options[:route_uri] = args[0].gsub(/\{.*\}/, "")
51+
options[:route_optionals] = (optionals = args[0].match(/(\{.*\})/) and optionals[-1])
5152
options[:route_name] = args[1]
5253
args.push(options)
5354
context(*args, &block)

lib/rspec_api_documentation/views/api_blueprint_index.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ def initialize(index, configuration)
88

99
def sections
1010
super.map do |section|
11-
routes = section[:examples].group_by(&:route_uri).map do |route_uri, examples|
11+
routes = section[:examples].group_by { |e| "#{e.route_uri}#{e.route_optionals}" }.map do |route, examples|
1212
attrs = fields(:attributes, examples)
1313
params = fields(:parameters, examples)
1414

@@ -23,7 +23,7 @@ def sections
2323
{
2424
"has_attributes?".to_sym => attrs.size > 0,
2525
"has_parameters?".to_sym => params.size > 0,
26-
route_uri: route_uri,
26+
route: route,
2727
route_name: examples[0][:route_name],
2828
attributes: attrs,
2929
parameters: params,

spec/dsl_spec.rb

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -594,13 +594,17 @@
594594
end
595595
end
596596

597-
route "/orders", "Orders Collection" do
597+
route "/orders{?application_id=:some_id}", "Orders Collection" do
598598
attribute :description, "Order description"
599599

600600
it "saves the route URI" do |example|
601601
expect(example.metadata[:route_uri]).to eq "/orders"
602602
end
603603

604+
it "saves the route optionals" do |example|
605+
expect(example.metadata[:route_optionals]).to eq "{?application_id=:some_id}"
606+
end
607+
604608
it "saves the route name" do |example|
605609
expect(example.metadata[:route_name]).to eq "Orders Collection"
606610
end

spec/views/api_blueprint_index_spec.rb

Lines changed: 52 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,9 @@
77
let(:post_group) { RSpec::Core::ExampleGroup.resource("Posts") }
88
let(:comment_group) { RSpec::Core::ExampleGroup.resource("Comments") }
99
let(:rspec_example_post_get) do
10-
post_group.route "/posts/{id}", "Single Post" do
10+
post_group.route "/posts/:id{?option=:option}", "Single Post" do
1111
parameter :id, "The id", required: true, type: "string", example: "1"
12-
attribute :name, "Order name 1", required: true
13-
attribute :name, "Order name 2", required: true
12+
parameter :option
1413

1514
get("/posts/{id}") do
1615
example_request 'Gets a post' do
@@ -25,15 +24,31 @@
2524
end
2625

2726
let(:rspec_example_post_delete) do
28-
post_group.route "/posts/{id}", "Single Post" do
29-
get("/posts/{id}") do
27+
post_group.route "/posts/:id", "Single Post" do
28+
parameter :id, "The id", required: true, type: "string", example: "1"
29+
30+
delete("/posts/:id") do
3031
example_request 'Deletes a post' do
3132
do_request
3233
end
3334
end
3435
end
3536
end
3637

38+
let(:rspec_example_post_update) do
39+
post_group.route "/posts/:id", "Single Post" do
40+
parameter :id, "The id", required: true, type: "string", example: "1"
41+
attribute :name, "Order name 1", required: true
42+
attribute :name, "Order name 2", required: true
43+
44+
put("/posts/:id") do
45+
example_request 'Updates a post' do
46+
do_request
47+
end
48+
end
49+
end
50+
end
51+
3752

3853
let(:rspec_example_posts) do
3954
post_group.route "/posts", "Posts Collection" do
@@ -54,16 +69,13 @@
5469
end
5570
end
5671
end
57-
let(:example1) { RspecApiDocumentation::Example.new(rspec_example_post_get, config) }
58-
let(:example2) { RspecApiDocumentation::Example.new(rspec_example_post_delete, config) }
59-
let(:example3) { RspecApiDocumentation::Example.new(rspec_example_posts, config) }
60-
let(:example4) { RspecApiDocumentation::Example.new(rspec_example_comments, config) }
6172
let(:index) do
6273
RspecApiDocumentation::Index.new.tap do |index|
63-
index.examples << example1
64-
index.examples << example2
65-
index.examples << example3
66-
index.examples << example4
74+
index.examples << RspecApiDocumentation::Example.new(rspec_example_post_get, config)
75+
index.examples << RspecApiDocumentation::Example.new(rspec_example_post_delete, config)
76+
index.examples << RspecApiDocumentation::Example.new(rspec_example_post_update, config)
77+
index.examples << RspecApiDocumentation::Example.new(rspec_example_posts, config)
78+
index.examples << RspecApiDocumentation::Example.new(rspec_example_comments, config)
6779
end
6880
end
6981
let(:config) { RspecApiDocumentation::Configuration.new }
@@ -82,12 +94,13 @@
8294

8395
it "returns routes grouped" do
8496
comments_route = sections[0][:routes][0]
85-
posts_route = sections[1][:routes][0]
86-
post_route = sections[1][:routes][1]
97+
posts_route = sections[1][:routes][0]
98+
post_route = sections[1][:routes][1]
99+
post_route_with_optionals = sections[1][:routes][2]
87100

88101
comments_examples = comments_route[:http_methods].map { |http_method| http_method[:examples] }.flatten
89102
expect(comments_examples.size).to eq 1
90-
expect(comments_route[:route_uri]).to eq "/comments"
103+
expect(comments_route[:route]).to eq "/comments"
91104
expect(comments_route[:route_name]).to eq "Comments Collection"
92105
expect(comments_route[:has_parameters?]).to eq false
93106
expect(comments_route[:parameters]).to eq []
@@ -96,13 +109,13 @@
96109

97110
post_examples = post_route[:http_methods].map { |http_method| http_method[:examples] }.flatten
98111
expect(post_examples.size).to eq 2
99-
expect(post_route[:route_uri]).to eq "/posts/{id}"
112+
expect(post_route[:route]).to eq "/posts/:id"
100113
expect(post_route[:route_name]).to eq "Single Post"
101114
expect(post_route[:has_parameters?]).to eq true
102115
expect(post_route[:parameters]).to eq [{
103116
required: true,
104-
example: "1",
105117
type: "string",
118+
example: "1",
106119
name: "id",
107120
description: "The id",
108121
properties_description: "required, string"
@@ -115,9 +128,29 @@
115128
properties_description: "required"
116129
}]
117130

131+
post_w_optionals_examples = post_route_with_optionals[:http_methods].map { |http_method| http_method[:examples] }.flatten
132+
expect(post_w_optionals_examples.size).to eq 1
133+
expect(post_route_with_optionals[:route]).to eq "/posts/:id{?option=:option}"
134+
expect(post_route_with_optionals[:route_name]).to eq "Single Post"
135+
expect(post_route_with_optionals[:has_parameters?]).to eq true
136+
expect(post_route_with_optionals[:parameters]).to eq [{
137+
required: true,
138+
type: "string",
139+
example: "1",
140+
name: "id",
141+
description: "The id",
142+
properties_description: "required, string"
143+
}, {
144+
name: "option",
145+
description: nil,
146+
properties_description: nil
147+
}]
148+
expect(post_route_with_optionals[:has_attributes?]).to eq false
149+
expect(post_route_with_optionals[:attributes]).to eq []
150+
118151
posts_examples = posts_route[:http_methods].map { |http_method| http_method[:examples] }.flatten
119152
expect(posts_examples.size).to eq 1
120-
expect(posts_route[:route_uri]).to eq "/posts"
153+
expect(posts_route[:route]).to eq "/posts"
121154
expect(posts_route[:route_name]).to eq "Posts Collection"
122155
expect(posts_route[:has_parameters?]).to eq false
123156
expect(posts_route[:parameters]).to eq []

templates/rspec_api_documentation/api_blueprint_index.mustache

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ FORMAT: A1
1212
{{/ description }}
1313
{{# routes }}
1414

15-
## {{ route_name }} [{{ route_uri }}]
15+
## {{ route_name }} [{{ route }}]
1616
{{# description }}
1717

1818
description: {{ description }}

0 commit comments

Comments
 (0)