Skip to content

Commit

Permalink
making tags_controller and tagged_items_controller fully v1 and v2 aw…
Browse files Browse the repository at this point in the history
…are; also updating the Swagger JSON specs
  • Loading branch information
Tim Schmelmer committed Apr 18, 2014
1 parent 571fb34 commit 615c10f
Show file tree
Hide file tree
Showing 11 changed files with 498 additions and 15 deletions.
27 changes: 23 additions & 4 deletions app/controllers/tagged_items_controller.rb
Expand Up @@ -5,6 +5,7 @@ class TaggedItemsController < ApplicationController
# Show a single tagged items for a given tag
# Example:
# `curl -v -H "Content-type: application/json" 'http://localhost:3000/api/v1/tags/android/tagged_items/1.json'`
# `curl -v -H "Content-type: application/json" 'http://localhost:3000/api/v2/tags/android/tagged_items/1.json?item_type=city'`
def show
Rails.logger.debug "Tagged item is #{@tagged_item.inspect}"
render_if_stale(@tagged_item, last_modified: @tagged_item.updated_at.utc, etag: @tagged_item) do |tagged_item_presenter|
Expand All @@ -17,8 +18,9 @@ def show
# List all tagged items for a given tag
# Example:
# `curl -v -H "Content-type: application/json" 'http://localhost:3000/api/v1/tags/android/tagged_items.json'`
# `curl -v -H "Content-type: application/json" 'http://localhost:3000/api/v2/tags/android/tagged_items.json?item_type=city'`
def index
all_tagged_items = @tag.tagged_items
all_tagged_items = filter_by_item_type(@tag.tagged_items, params[:version].to_i, params[:item_type])
return json_response([]) unless newest_tagged_item = all_tagged_items.sort_by(&:updated_at).last
Rails.logger.info "newest_tagged_item is #{newest_tagged_item.inspect}"
render_if_stale(all_tagged_items, last_modified: newest_tagged_item.updated_at.utc, etag: newest_tagged_item) do |tagged_item_presenters|
Expand All @@ -32,9 +34,11 @@ def index
# Example:
# `curl -v -H "Content-type: application/json" -X POST 'http://localhost:3000/api/v1/tags/android/tagged_items.json' \
# -d '{"id":1}'`
# `curl -v -H "Content-type: application/json" -X POST 'http://localhost:3000/api/v2/tags/android/tagged_items.json' \
# -d '{"id":1, "item_type": "city"}'`
def create
item = TaggedItem.find_or_initialize_by(item_id: params[:id], tag_id: @tag.id)
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?
item = TaggedItem.find_or_initialize_by(item_id: params[:id], tag_id: @tag.id, item_type: TaggedItem.item_type_id_for(params[:item_type]))
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?
if item.save
render text: '{"success": true}', status: :created, location: tagged_item_url(item, (params[:version]))
else
Expand All @@ -47,6 +51,7 @@ def create
# Delete a tagged_item entry for a given tag and item ID
# Example:
# `curl -v -H "Content-type: application/json" -X DELETE 'http://localhost:3000/api/v1/tags/android/tagged_items/1.json'`
# `curl -v -H "Content-type: application/json" -X DELETE 'http://localhost:3000/api/v2/tags/android/tagged_items/1.json?item_type=city'`
def destroy
Rails.logger.debug "Tagged item is #{@tagged_item.inspect}"
if @tagged_item.destroy
Expand All @@ -65,7 +70,21 @@ def find_tag
end

def find_tagged_item
@tagged_item = @tag.tagged_items.where(item_id: params[:id], item_type: TaggedItem.item_type_id_for(params[:type])).first
@tagged_item = @tag.tagged_items.where(item_id: params[:id], item_type: TaggedItem.item_type_id_for(params[:item_type])).first
not_found_with_max_age(caching_time) and return unless @tagged_item
end

def filter_by_item_type(tagged_items, version = 1, item_type_name = 'all')
case version
when 2
if item_type_name.nil? || (item_type_name == 'all')
tagged_items
else
tagged_items.where(item_type: TaggedItem.item_type_id_for(item_type_name))
end

else
tagged_items
end
end
end
30 changes: 27 additions & 3 deletions app/controllers/tags_controller.rb
Expand Up @@ -15,10 +15,10 @@ def show

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

private

def filter_by_item_it_and_type(item_id, item_type = 'all', version = 1)
item_id = params[:item_id].to_i
case version
when 2
case
when item_id == 0 && item_type = 'all'
Tag.all
when item_id == 0 && item_type != 'all'
Tag.joins(:tagged_items).where(tagged_items: { item_type: TaggedItem.item_type_id_for(item_type) })
when item_id > 0 && item_type == 'all'
Tag.joins(:tagged_items).where(tagged_items: { item_id: item_id })
when item_id > 0 && item_type != 'all'
Tag.joins(:tagged_items).where(tagged_items: {item_id: item_id, item_type: TaggedItem.item_type_id_for(item_type)})
end
else
if item_id > 0
Tag.joins(:tagged_items).where(tagged_items: {item_id: item_id, item_type: TaggedItem.item_type_id_for('inventory_item')})
else
Tag.all
end
end
end
end
7 changes: 6 additions & 1 deletion app/helpers/application_helper.rb
@@ -1,5 +1,10 @@
module ApplicationHelper
def tagged_item_url(tagged_item, version = 1)
"http://inventory-service-development.herokuapp.com/api/v#{version}/inventory_items/#{tagged_item.item_id}"
case TaggedItem.item_type_name_for(tagged_item.item_type)
when 'city'
"http://cities-service-development.herokuapp.com/api/v#{version}/cities/#{tagged_item.item_id}"
else
"http://inventory-service-development.herokuapp.com/api/v#{version}/inventory_items/#{tagged_item.item_id}"
end
end
end
13 changes: 11 additions & 2 deletions app/models/tagged_item.rb
Expand Up @@ -5,12 +5,21 @@ class TaggedItem < ActiveRecord::Base
validates_presence_of :item_id, :tag_id, :item_type
validates_uniqueness_of :item_id, scope: [:tag_id, :item_type]

def self.item_type_id_for(item_type = 'inventory_item')
case item_type
def self.item_type_id_for(item_type_name = 'inventory_item')
case item_type_name
when 'city'
2
else
1
end
end

def self.item_type_name_for(item_type_id = 1)
case item_type_id
when 2
'city'
else
'inventory_item'
end
end
end
5 changes: 5 additions & 0 deletions app/presenters/presenters.rb
Expand Up @@ -3,4 +3,9 @@ module V1
autoload :TagPresenter, "v1/tag_presenter"
autoload :TaggedItemPresenter, "v1/tagged_item_presenter"
end

module V2
autoload :TagPresenter, "v2/tag_presenter"
autoload :TaggedItemPresenter, "v2/tagged_item_presenter"
end
end
10 changes: 5 additions & 5 deletions app/presenters/v1/tag_presenter.rb
Expand Up @@ -7,18 +7,18 @@ def initialize(item)
super(@tag)
end

def to_hash(item = tag)
def to_hash(tg = tag)
HashWithIndifferentAccess.new(
{
name: tag.name,
name: tg.name,
tagged_items: {
count: tag.tagged_items.count,
items: tag.tagged_items.map do |item|
count: tg.tagged_items.count,
items: tg.tagged_items.map do |item|
{ id: item.item_id,
url: tagged_item_url(item, self.class.version_number) }
end
},
path: tag_path(self.class.version_number, tag.name)
path: tag_path(self.class.version_number, tg.name)
})
end
end
19 changes: 19 additions & 0 deletions app/presenters/v2/tag_presenter.rb
@@ -0,0 +1,19 @@
class Presenters::V2::TagPresenter < Presenters::V1::TagPresenter

def to_hash(tg = tag)
HashWithIndifferentAccess.new(
{
name: tg.name,
tagged_items: {
count: tg.tagged_items.count,
items: tg.tagged_items.map do |item|
{ id: item.item_id,
type: item.item_type,
url: tagged_item_url(item, self.class.version_number) }
end
},
path: tag_path(self.class.version_number, tg.name)
})
end

end
12 changes: 12 additions & 0 deletions app/presenters/v2/tagged_item_presenter.rb
@@ -0,0 +1,12 @@
class Presenters::V2::TaggedItemPresenter < Presenters::V1::TaggedItemPresenter

def to_hash(tagged_item = item)
HashWithIndifferentAccess.new(
{
tagged_item_id: tagged_item.item_id,
tagged_item_type: tagged_item.item_type,
url: tagged_item_url(tagged_item, self.class.version_number),
tag_name: tagged_item.tag.name
})
end
end
15 changes: 15 additions & 0 deletions public/api_docs/v2/api-docs.json
@@ -0,0 +1,15 @@
{
"apiVersion": "2.0",
"swaggerVersion": "1.2",
"basePath": "http://tags-service-development.herokuapp.com/api_docs/v2",
"apis": [
{
"path": "/tagged_items.{format}",
"description": "Managing associating tags with items"
},
{
"path": "/tags.{format}",
"description": "Tag management"
}
]
}
165 changes: 165 additions & 0 deletions public/api_docs/v2/tagged_items.json
@@ -0,0 +1,165 @@
{
"apiVersion": "2.0",
"swaggerVersion": "1.2",
"basePath": "http://tags-service-development.herokuapp.com/api/v2",
"apis": [
{
"path": "/tags/{tag_name}/tagged_items.json",
"operations": [
{
"summary": "Fetches all tagged items for tag {tag_name}",
"parameters": [
{
"paramType": "path",
"name": "tag_name",
"type": "string",
"description": "Tag name for which all tagged items are retrieved",
"required": true
},
{
"paramType": "query",
"name": "item_type",
"type": "string",
"description": "Name of the type of item by which to filter the items tagged, e.g. 'city'; defaults to 'all'",
"required": false
}
],
"responseMessages": [
{
"code": 304,
"message": "The content has not changed in relation to the request ETag / If-Modified-Since"
}
],
"method": "get",
"nickname": "TaggedItems#index"
}
]
},
{
"path": "/tags/{tag_name}/tagged_items.json",
"operations": [
{
"summary": "Tags an item with ID {id} with tag by name {tag_name}",
"parameters": [
{
"paramType": "path",
"name": "tag_name",
"type": "string",
"description": "Tag name to be applied to the tagged item",
"required": true
},
{
"paramType": "form",
"name": "id",
"type": "integer",
"description": "ID of the item to be tagged",
"required": true
},
{
"paramType": "form",
"name": "item_type",
"type": "string",
"description": "Name of the type of item to be tagged, e.g. 'city'; defaults to 'inventory_item'",
"required": false
}
],
"responseMessages": [
{
"code": 422,
"message": "Unprocessable Entity"
}
],
"method": "post",
"nickname": "TaggedItems#create"
}
]
},
{
"path": "/tags/{tag_name}/tagged_items/{id}.json",
"operations": [
{
"summary": "Fetches a single tagged item for tag {tag_name} and item ID {id}",
"parameters": [
{
"paramType": "path",
"name": "id",
"type": "integer",
"description": "ID of the tagged item",
"required": true
},
{
"paramType": "path",
"name": "tag_name",
"type": "string",
"description": "Name of the tag that is was applied to the tagged item",
"required": true
},
{
"paramType": "query",
"name": "item_type",
"type": "string",
"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'",
"required": false
}
],
"responseMessages": [
{
"code": 304,
"message": "The content has not changed in relation to the request ETag / If-Modified-Since"
},
{
"code": 404,
"message": "Not Found"
}
],
"method": "get",
"nickname": "TaggedItems#show"
}
]
},
{
"path": "/tags/{tag_name}/tagged_items/{id}.json",
"operations": [
{
"summary": "Deletes an existing association between a tag (by name) and a tagged item",
"parameters": [
{
"paramType": "path",
"name": "id",
"type": "integer",
"description": "ID of the item that is currently tagged",
"required": true
},
{
"paramType": "path",
"name": "tag_name",
"type": "string",
"description": "name of the tag to be removed from the item",
"required": true
},
{
"paramType": "query",
"name": "item_type",
"type": "string",
"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'",
"required": false
}
],
"responseMessages": [
{
"code": 404,
"message": "Not Found"
},
{
"code": 400,
"message": "Bad Request"
}
],
"method": "delete",
"nickname": "TaggedItems#destroy"
}
]
}
],
"resourcePath": "/tagged_items"
}

0 comments on commit 615c10f

Please sign in to comment.