Skip to content

Commit 615c10f

Browse files
author
Tim Schmelmer
committed
making tags_controller and tagged_items_controller fully v1 and v2 aware; also updating the Swagger JSON specs
1 parent 571fb34 commit 615c10f

File tree

11 files changed

+498
-15
lines changed

11 files changed

+498
-15
lines changed

app/controllers/tagged_items_controller.rb

Lines changed: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ class TaggedItemsController < ApplicationController
55
# Show a single tagged items for a given tag
66
# Example:
77
# `curl -v -H "Content-type: application/json" 'http://localhost:3000/api/v1/tags/android/tagged_items/1.json'`
8+
# `curl -v -H "Content-type: application/json" 'http://localhost:3000/api/v2/tags/android/tagged_items/1.json?item_type=city'`
89
def show
910
Rails.logger.debug "Tagged item is #{@tagged_item.inspect}"
1011
render_if_stale(@tagged_item, last_modified: @tagged_item.updated_at.utc, etag: @tagged_item) do |tagged_item_presenter|
@@ -17,8 +18,9 @@ def show
1718
# List all tagged items for a given tag
1819
# Example:
1920
# `curl -v -H "Content-type: application/json" 'http://localhost:3000/api/v1/tags/android/tagged_items.json'`
21+
# `curl -v -H "Content-type: application/json" 'http://localhost:3000/api/v2/tags/android/tagged_items.json?item_type=city'`
2022
def index
21-
all_tagged_items = @tag.tagged_items
23+
all_tagged_items = filter_by_item_type(@tag.tagged_items, params[:version].to_i, params[:item_type])
2224
return json_response([]) unless newest_tagged_item = all_tagged_items.sort_by(&:updated_at).last
2325
Rails.logger.info "newest_tagged_item is #{newest_tagged_item.inspect}"
2426
render_if_stale(all_tagged_items, last_modified: newest_tagged_item.updated_at.utc, etag: newest_tagged_item) do |tagged_item_presenters|
@@ -32,9 +34,11 @@ def index
3234
# Example:
3335
# `curl -v -H "Content-type: application/json" -X POST 'http://localhost:3000/api/v1/tags/android/tagged_items.json' \
3436
# -d '{"id":1}'`
37+
# `curl -v -H "Content-type: application/json" -X POST 'http://localhost:3000/api/v2/tags/android/tagged_items.json' \
38+
# -d '{"id":1, "item_type": "city"}'`
3539
def create
36-
item = TaggedItem.find_or_initialize_by(item_id: params[:id], tag_id: @tag.id)
37-
render(json: {error: "Tagged item combination {tag-name: #{@tag.name}, item_id: #{item.item_id}] already exists."}, status: :conflict) and return unless item.new_record?
40+
item = TaggedItem.find_or_initialize_by(item_id: params[:id], tag_id: @tag.id, item_type: TaggedItem.item_type_id_for(params[:item_type]))
41+
render(json: {error: "Tagged item combination {tag-name: #{@tag.name}, item_id: #{item.item_id}, item_type: #{item.item_type}] already exists."}, status: :conflict) and return unless item.new_record?
3842
if item.save
3943
render text: '{"success": true}', status: :created, location: tagged_item_url(item, (params[:version]))
4044
else
@@ -47,6 +51,7 @@ def create
4751
# Delete a tagged_item entry for a given tag and item ID
4852
# Example:
4953
# `curl -v -H "Content-type: application/json" -X DELETE 'http://localhost:3000/api/v1/tags/android/tagged_items/1.json'`
54+
# `curl -v -H "Content-type: application/json" -X DELETE 'http://localhost:3000/api/v2/tags/android/tagged_items/1.json?item_type=city'`
5055
def destroy
5156
Rails.logger.debug "Tagged item is #{@tagged_item.inspect}"
5257
if @tagged_item.destroy
@@ -65,7 +70,21 @@ def find_tag
6570
end
6671

6772
def find_tagged_item
68-
@tagged_item = @tag.tagged_items.where(item_id: params[:id], item_type: TaggedItem.item_type_id_for(params[:type])).first
73+
@tagged_item = @tag.tagged_items.where(item_id: params[:id], item_type: TaggedItem.item_type_id_for(params[:item_type])).first
6974
not_found_with_max_age(caching_time) and return unless @tagged_item
7075
end
76+
77+
def filter_by_item_type(tagged_items, version = 1, item_type_name = 'all')
78+
case version
79+
when 2
80+
if item_type_name.nil? || (item_type_name == 'all')
81+
tagged_items
82+
else
83+
tagged_items.where(item_type: TaggedItem.item_type_id_for(item_type_name))
84+
end
85+
86+
else
87+
tagged_items
88+
end
89+
end
7190
end

app/controllers/tags_controller.rb

Lines changed: 27 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,10 @@ def show
1515

1616
# List all tags (can be filtered by "item_id" parameter)
1717
# Example:
18-
# `curl -v -H "Content-type: application/json" 'http://localhost:3000/api/v1/tags.json'`
18+
# `curl -v -H "Content-type: application/json" 'http://localhost:3000/api/v1/tags.json{?item_id=1}'`
19+
# `curl -v -H "Content-type: application/json" 'http://localhost:3000/api/v2/tags.json{?item_id=1&item_type=city}'`
1920
def index
20-
item_id = params[:item_id].to_i
21-
all_tags = (item_id > 0) ? Tag.joins(:tagged_items).where(tagged_items: {item_id: item_id}) : Tag.all
21+
all_tags = filter_by_item_it_and_type(params[:item_id].to_i, params[:item_type], params[:version].to_i)
2222
return json_response([]) unless newest_tag = all_tags.sort_by(&:updated_at).last
2323
Rails.logger.info "newest_tag is #{newest_tag.inspect}"
2424
render_if_stale(all_tags, last_modified: newest_tag.updated_at.utc, etag: newest_tag) do |tag_presenters|
@@ -73,4 +73,28 @@ def destroy
7373
end
7474
end
7575

76+
private
77+
78+
def filter_by_item_it_and_type(item_id, item_type = 'all', version = 1)
79+
item_id = params[:item_id].to_i
80+
case version
81+
when 2
82+
case
83+
when item_id == 0 && item_type = 'all'
84+
Tag.all
85+
when item_id == 0 && item_type != 'all'
86+
Tag.joins(:tagged_items).where(tagged_items: { item_type: TaggedItem.item_type_id_for(item_type) })
87+
when item_id > 0 && item_type == 'all'
88+
Tag.joins(:tagged_items).where(tagged_items: { item_id: item_id })
89+
when item_id > 0 && item_type != 'all'
90+
Tag.joins(:tagged_items).where(tagged_items: {item_id: item_id, item_type: TaggedItem.item_type_id_for(item_type)})
91+
end
92+
else
93+
if item_id > 0
94+
Tag.joins(:tagged_items).where(tagged_items: {item_id: item_id, item_type: TaggedItem.item_type_id_for('inventory_item')})
95+
else
96+
Tag.all
97+
end
98+
end
99+
end
76100
end

app/helpers/application_helper.rb

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
11
module ApplicationHelper
22
def tagged_item_url(tagged_item, version = 1)
3-
"http://inventory-service-development.herokuapp.com/api/v#{version}/inventory_items/#{tagged_item.item_id}"
3+
case TaggedItem.item_type_name_for(tagged_item.item_type)
4+
when 'city'
5+
"http://cities-service-development.herokuapp.com/api/v#{version}/cities/#{tagged_item.item_id}"
6+
else
7+
"http://inventory-service-development.herokuapp.com/api/v#{version}/inventory_items/#{tagged_item.item_id}"
8+
end
49
end
510
end

app/models/tagged_item.rb

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,21 @@ class TaggedItem < ActiveRecord::Base
55
validates_presence_of :item_id, :tag_id, :item_type
66
validates_uniqueness_of :item_id, scope: [:tag_id, :item_type]
77

8-
def self.item_type_id_for(item_type = 'inventory_item')
9-
case item_type
8+
def self.item_type_id_for(item_type_name = 'inventory_item')
9+
case item_type_name
1010
when 'city'
1111
2
1212
else
1313
1
1414
end
1515
end
16+
17+
def self.item_type_name_for(item_type_id = 1)
18+
case item_type_id
19+
when 2
20+
'city'
21+
else
22+
'inventory_item'
23+
end
24+
end
1625
end

app/presenters/presenters.rb

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,9 @@ module V1
33
autoload :TagPresenter, "v1/tag_presenter"
44
autoload :TaggedItemPresenter, "v1/tagged_item_presenter"
55
end
6+
7+
module V2
8+
autoload :TagPresenter, "v2/tag_presenter"
9+
autoload :TaggedItemPresenter, "v2/tagged_item_presenter"
10+
end
611
end

app/presenters/v1/tag_presenter.rb

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,18 +7,18 @@ def initialize(item)
77
super(@tag)
88
end
99

10-
def to_hash(item = tag)
10+
def to_hash(tg = tag)
1111
HashWithIndifferentAccess.new(
1212
{
13-
name: tag.name,
13+
name: tg.name,
1414
tagged_items: {
15-
count: tag.tagged_items.count,
16-
items: tag.tagged_items.map do |item|
15+
count: tg.tagged_items.count,
16+
items: tg.tagged_items.map do |item|
1717
{ id: item.item_id,
1818
url: tagged_item_url(item, self.class.version_number) }
1919
end
2020
},
21-
path: tag_path(self.class.version_number, tag.name)
21+
path: tag_path(self.class.version_number, tg.name)
2222
})
2323
end
2424
end

app/presenters/v2/tag_presenter.rb

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
class Presenters::V2::TagPresenter < Presenters::V1::TagPresenter
2+
3+
def to_hash(tg = tag)
4+
HashWithIndifferentAccess.new(
5+
{
6+
name: tg.name,
7+
tagged_items: {
8+
count: tg.tagged_items.count,
9+
items: tg.tagged_items.map do |item|
10+
{ id: item.item_id,
11+
type: item.item_type,
12+
url: tagged_item_url(item, self.class.version_number) }
13+
end
14+
},
15+
path: tag_path(self.class.version_number, tg.name)
16+
})
17+
end
18+
19+
end
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
class Presenters::V2::TaggedItemPresenter < Presenters::V1::TaggedItemPresenter
2+
3+
def to_hash(tagged_item = item)
4+
HashWithIndifferentAccess.new(
5+
{
6+
tagged_item_id: tagged_item.item_id,
7+
tagged_item_type: tagged_item.item_type,
8+
url: tagged_item_url(tagged_item, self.class.version_number),
9+
tag_name: tagged_item.tag.name
10+
})
11+
end
12+
end

public/api_docs/v2/api-docs.json

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
{
2+
"apiVersion": "2.0",
3+
"swaggerVersion": "1.2",
4+
"basePath": "http://tags-service-development.herokuapp.com/api_docs/v2",
5+
"apis": [
6+
{
7+
"path": "/tagged_items.{format}",
8+
"description": "Managing associating tags with items"
9+
},
10+
{
11+
"path": "/tags.{format}",
12+
"description": "Tag management"
13+
}
14+
]
15+
}
Lines changed: 165 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,165 @@
1+
{
2+
"apiVersion": "2.0",
3+
"swaggerVersion": "1.2",
4+
"basePath": "http://tags-service-development.herokuapp.com/api/v2",
5+
"apis": [
6+
{
7+
"path": "/tags/{tag_name}/tagged_items.json",
8+
"operations": [
9+
{
10+
"summary": "Fetches all tagged items for tag {tag_name}",
11+
"parameters": [
12+
{
13+
"paramType": "path",
14+
"name": "tag_name",
15+
"type": "string",
16+
"description": "Tag name for which all tagged items are retrieved",
17+
"required": true
18+
},
19+
{
20+
"paramType": "query",
21+
"name": "item_type",
22+
"type": "string",
23+
"description": "Name of the type of item by which to filter the items tagged, e.g. 'city'; defaults to 'all'",
24+
"required": false
25+
}
26+
],
27+
"responseMessages": [
28+
{
29+
"code": 304,
30+
"message": "The content has not changed in relation to the request ETag / If-Modified-Since"
31+
}
32+
],
33+
"method": "get",
34+
"nickname": "TaggedItems#index"
35+
}
36+
]
37+
},
38+
{
39+
"path": "/tags/{tag_name}/tagged_items.json",
40+
"operations": [
41+
{
42+
"summary": "Tags an item with ID {id} with tag by name {tag_name}",
43+
"parameters": [
44+
{
45+
"paramType": "path",
46+
"name": "tag_name",
47+
"type": "string",
48+
"description": "Tag name to be applied to the tagged item",
49+
"required": true
50+
},
51+
{
52+
"paramType": "form",
53+
"name": "id",
54+
"type": "integer",
55+
"description": "ID of the item to be tagged",
56+
"required": true
57+
},
58+
{
59+
"paramType": "form",
60+
"name": "item_type",
61+
"type": "string",
62+
"description": "Name of the type of item to be tagged, e.g. 'city'; defaults to 'inventory_item'",
63+
"required": false
64+
}
65+
],
66+
"responseMessages": [
67+
{
68+
"code": 422,
69+
"message": "Unprocessable Entity"
70+
}
71+
],
72+
"method": "post",
73+
"nickname": "TaggedItems#create"
74+
}
75+
]
76+
},
77+
{
78+
"path": "/tags/{tag_name}/tagged_items/{id}.json",
79+
"operations": [
80+
{
81+
"summary": "Fetches a single tagged item for tag {tag_name} and item ID {id}",
82+
"parameters": [
83+
{
84+
"paramType": "path",
85+
"name": "id",
86+
"type": "integer",
87+
"description": "ID of the tagged item",
88+
"required": true
89+
},
90+
{
91+
"paramType": "path",
92+
"name": "tag_name",
93+
"type": "string",
94+
"description": "Name of the tag that is was applied to the tagged item",
95+
"required": true
96+
},
97+
{
98+
"paramType": "query",
99+
"name": "item_type",
100+
"type": "string",
101+
"description": "Name of the item type by which to filter the applicable tags (e.g., 'city'), so that just one tagged item is ever returned; defaults to 'inventory_item'",
102+
"required": false
103+
}
104+
],
105+
"responseMessages": [
106+
{
107+
"code": 304,
108+
"message": "The content has not changed in relation to the request ETag / If-Modified-Since"
109+
},
110+
{
111+
"code": 404,
112+
"message": "Not Found"
113+
}
114+
],
115+
"method": "get",
116+
"nickname": "TaggedItems#show"
117+
}
118+
]
119+
},
120+
{
121+
"path": "/tags/{tag_name}/tagged_items/{id}.json",
122+
"operations": [
123+
{
124+
"summary": "Deletes an existing association between a tag (by name) and a tagged item",
125+
"parameters": [
126+
{
127+
"paramType": "path",
128+
"name": "id",
129+
"type": "integer",
130+
"description": "ID of the item that is currently tagged",
131+
"required": true
132+
},
133+
{
134+
"paramType": "path",
135+
"name": "tag_name",
136+
"type": "string",
137+
"description": "name of the tag to be removed from the item",
138+
"required": true
139+
},
140+
{
141+
"paramType": "query",
142+
"name": "item_type",
143+
"type": "string",
144+
"description": "Name of the item type by which to filter the applicable tags (e.g., 'city'), so that just one tagged item is ever destroyed; defaults to 'inventory_item'",
145+
"required": false
146+
}
147+
],
148+
"responseMessages": [
149+
{
150+
"code": 404,
151+
"message": "Not Found"
152+
},
153+
{
154+
"code": 400,
155+
"message": "Bad Request"
156+
}
157+
],
158+
"method": "delete",
159+
"nickname": "TaggedItems#destroy"
160+
}
161+
]
162+
}
163+
],
164+
"resourcePath": "/tagged_items"
165+
}

0 commit comments

Comments
 (0)