Skip to content

Commit

Permalink
Added search support (#99)
Browse files Browse the repository at this point in the history
* Add #search to the project
* specs for Project#search
* Add README listing for Project#search
  • Loading branch information
masonnl authored and forest committed Apr 10, 2017
1 parent 032c0cf commit 62caf2a
Show file tree
Hide file tree
Showing 9 changed files with 94 additions and 0 deletions.
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,8 @@ project.stories(with_state: :unscheduled, limit: 10) # Get
project.stories(filter: 'requester:OWK label:"jedi stuff"') # Get all stories that match the given filters
project.create_story(name: 'Destroy death star') # Create a story with the name 'Destroy death star'

project.search('Destroy death star') # Get a search result with all epics and stories relevant to the query

story = project.story(847762630) # Find a story with the given ID
story.activity # Get a list of all the activity performed on this story
story.transitions # Get a list of all the story transitions on this story
Expand Down
4 changes: 4 additions & 0 deletions lib/tracker_api.rb
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ module Endpoints
autoload :Projects, 'tracker_api/endpoints/projects'
autoload :Workspace, 'tracker_api/endpoints/workspace'
autoload :Workspaces, 'tracker_api/endpoints/workspaces'
autoload :Search, 'tracker_api/endpoints/search'
autoload :Stories, 'tracker_api/endpoints/stories'
autoload :Story, 'tracker_api/endpoints/story'
autoload :StoryOwners, 'tracker_api/endpoints/story_owners'
Expand All @@ -65,6 +66,7 @@ module Shared
autoload :Account, 'tracker_api/resources/account'
autoload :Change, 'tracker_api/resources/change'
autoload :Epic, 'tracker_api/resources/epic'
autoload :EpicsSearchResult, 'tracker_api/resources/epics_search_result'
autoload :Iteration, 'tracker_api/resources/iteration'
autoload :Me, 'tracker_api/resources/me'
autoload :MembershipSummary, 'tracker_api/resources/membership_summary'
Expand All @@ -75,6 +77,8 @@ module Shared
autoload :Project, 'tracker_api/resources/project'
autoload :ProjectMembership, 'tracker_api/resources/project_membership'
autoload :Workspace, 'tracker_api/resources/workspace'
autoload :SearchResultContainer, 'tracker_api/resources/search_result_container'
autoload :StoriesSearchResult, 'tracker_api/resources/stories_search_result'
autoload :Story, 'tracker_api/resources/story'
autoload :Task, 'tracker_api/resources/task'
autoload :TimeZone, 'tracker_api/resources/time_zone'
Expand Down
22 changes: 22 additions & 0 deletions lib/tracker_api/endpoints/search.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
module TrackerApi
module Endpoints
class Search
attr_accessor :client

def initialize(client)
@client = client
end

def get(project_id, query, options={})
raise ArgumentError, 'Valid query string required to search' unless query.is_a?(String)

options.key?(:body) ? options[:body][:query] = query : options[:body] = { query: query }
data = client.get("/projects/#{project_id}/search", options).body

raise Errors::UnexpectedData, 'Hash of search results expect' unless data.is_a? Hash

Resources::SearchResultContainer.new(data)
end
end
end
end
12 changes: 12 additions & 0 deletions lib/tracker_api/resources/epics_search_result.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
module TrackerApi
module Resources
class EpicsSearchResult
include Shared::Base

attribute :epics, Array[Resources::Epic]
attribute :total_hits, Integer
attribute :total_hits_with_done, Integer
attribute :kind, String
end
end
end
10 changes: 10 additions & 0 deletions lib/tracker_api/resources/project.rb
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,16 @@ def add_webhook(params)
def delete_webhook(webhook_id)
Endpoints::Webhook.new(client).delete_from_project(id, webhook_id)
end

# Search for a term in the given project. This can be an arbitrary term or a specific search query.
# See https://www.pivotaltracker.com/help/articles/advanced_search/
#
# @param [String] query A versatile search query
# @param [Hash] params
# @return [SearchResultsContainer] An object composed of Epic(s) [EpicsSearchResult] and Story(s) [StoriesSearchResults]
def search(query, params={})
Endpoints::Search.new(client).get(id, query, params)
end
end
end
end
12 changes: 12 additions & 0 deletions lib/tracker_api/resources/search_result_container.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
module TrackerApi
module Resources
class SearchResultContainer
include Shared::Base

attribute :query, String
attribute :stories, Resources::StoriesSearchResult
attribute :epics, Resources::EpicsSearchResult
attribute :kind, String
end
end
end
14 changes: 14 additions & 0 deletions lib/tracker_api/resources/stories_search_result.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
module TrackerApi
module Resources
class StoriesSearchResult
include Shared::Base

attribute :stories, Array[Resources::Story]
attribute :total_hits, Integer
attribute :total_hits_with_done, Integer
attribute :total_points, Float
attribute :total_points_completed, Float
attribute :kind, String
end
end
end
17 changes: 17 additions & 0 deletions test/project_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -178,4 +178,21 @@
end
end
end

describe '.search' do
let(:pt_user) { PT_USER_3 }

it 'can search a project' do
VCR.use_cassette('search project') do
project = client.project(pt_user[:project_id])
search_container = project.search('name:"story to test search"')

search_container.wont_be_nil
search_container.must_be_instance_of TrackerApi::Resources::SearchResultContainer
search_container.epics.must_be_instance_of TrackerApi::Resources::EpicsSearchResult
search_container.stories.must_be_instance_of TrackerApi::Resources::StoriesSearchResult
search_container.stories.stories.first[:id].must_equal 143444685
end
end
end
end
1 change: 1 addition & 0 deletions test/vcr/cassettes/search_project.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"http_interactions":[{"request":{"method":"get","uri":"https://www.pivotaltracker.com/services/v5/projects/1027494","body":{"encoding":"US-ASCII","string":""},"headers":{"User-Agent":["Ruby/2.2.4 (x86_64-darwin15; ruby) TrackerApi/1.6.0 Faraday/0.9.2"],"X-TrackerToken":["77f9b9a466c436e6456939208c84c973"]}},"response":{"status":{"code":200,"message":null},"headers":{"Access-Control-Allow-Credentials":["false"],"Access-Control-Allow-Headers":["X-TrackerToken,DNT,X-Mx-ReqToken,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,X-Tracker-Warn-Unless-Project-Version-Is"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Access-Control-Allow-Origin":["*"],"Cache-Control":["max-age=0, private, must-revalidate"],"Content-Type":["application/json; charset=utf-8"],"Date":["Mon, 10 Apr 2017 18:34:56 GMT"],"Etag":["\"46471d209a7b433eba4710d6f9b001cb\""],"Server":["nginx + Phusion Passenger"],"Status":["200 OK"],"Strict-Transport-Security":["max-age=31536000; includeSubDomains; preload"],"X-Powered-By":["Phusion Passenger Enterprise"],"X-Rack-Cache":["miss"],"X-Request-Id":["480867069c44e12ed5491e58c5bf6542"],"X-Runtime":["0.057795"],"X-Tracker-Client-Pinger-Interval":["20"],"X-Tracker-Project-Version":["3"],"X-Ua-Compatible":["IE=Edge,chrome=1"],"X-Vcap-Request-Id":["ca69d9ea-1ecc-42af-7681-2957a99f14c1"],"Content-Length":["845"],"Connection":["keep-alive"]},"body":{"encoding":"ASCII-8BIT","string":"{\"id\":1027494,\"kind\":\"project\",\"name\":\"My Sample Project\",\"version\":3,\"iteration_length\":1,\"week_start_day\":\"Monday\",\"point_scale\":\"0,1,2,3\",\"point_scale_is_custom\":false,\"bugs_and_chores_are_estimatable\":false,\"automatic_planning\":true,\"enable_tasks\":true,\"time_zone\":{\"kind\":\"time_zone\",\"olson_name\":\"America/Los_Angeles\",\"offset\":\"-07:00\"},\"velocity_averaged_over\":3,\"number_of_done_iterations_to_show\":12,\"has_google_domain\":false,\"profile_content\":\"This is a demo project, created by Tracker, with example stories for a simple shopping web site.\",\"enable_incoming_emails\":true,\"initial_velocity\":10,\"public\":false,\"atom_enabled\":false,\"project_type\":\"demo\",\"start_time\":\"2014-02-10T08:00:00Z\",\"created_at\":\"2014-03-02T07:18:28Z\",\"updated_at\":\"2014-03-02T07:18:28Z\",\"account_id\":621388,\"current_iteration_number\":166,\"enable_following\":true}"},"http_version":null},"recorded_at":"Mon, 10 Apr 2017 18:34:56 GMT"},{"request":{"method":"get","uri":"https://www.pivotaltracker.com/services/v5/projects/1027494/search","body":{"encoding":"UTF-8","string":"{\"query\":\"name:\\\"story to test search\\\"\"}"},"headers":{"User-Agent":["Ruby/2.2.4 (x86_64-darwin15; ruby) TrackerApi/1.6.0 Faraday/0.9.2"],"X-TrackerToken":["77f9b9a466c436e6456939208c84c973"],"Content-Type":["application/json"]}},"response":{"status":{"code":200,"message":null},"headers":{"Access-Control-Allow-Credentials":["false"],"Access-Control-Allow-Headers":["X-TrackerToken,DNT,X-Mx-ReqToken,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,X-Tracker-Warn-Unless-Project-Version-Is"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Access-Control-Allow-Origin":["*"],"Cache-Control":["max-age=0, private, must-revalidate"],"Content-Type":["application/json; charset=utf-8"],"Date":["Mon, 10 Apr 2017 18:34:57 GMT"],"Etag":["\"8a5f6bd57393dc3dcfc9ee0386be8575\""],"Server":["nginx + Phusion Passenger"],"Status":["200 OK"],"Strict-Transport-Security":["max-age=31536000; includeSubDomains; preload"],"X-Powered-By":["Phusion Passenger Enterprise"],"X-Rack-Cache":["miss"],"X-Request-Id":["0976a6e85fab21b3a8c6767650c4f4b8"],"X-Runtime":["0.509539"],"X-Tracker-Client-Pinger-Interval":["20"],"X-Tracker-Project-Version":["3"],"X-Ua-Compatible":["IE=Edge,chrome=1"],"X-Vcap-Request-Id":["331980c0-1e08-4e76-5707-302b5cae005d"],"Content-Length":["506"],"Connection":["keep-alive"]},"body":{"encoding":"ASCII-8BIT","string":"{\"stories\":{\"stories\":[{\"kind\":\"story\",\"id\":143444685,\"created_at\":\"2017-04-10T18:20:52Z\",\"updated_at\":\"2017-04-10T18:20:52Z\",\"story_type\":\"feature\",\"name\":\"story to test search\",\"current_state\":\"unscheduled\",\"requested_by_id\":1266318,\"url\":\"https://www.pivotaltracker.com/story/show/143444685\",\"project_id\":1027494,\"owner_ids\":[],\"labels\":[]}],\"total_points\":0,\"total_points_completed\":0,\"total_hits\":1,\"total_hits_with_done\":1},\"epics\":{\"epics\":[],\"total_hits\":0},\"query\":\"name:\\\"story to test search\\\"\"}"},"http_version":null},"recorded_at":"Mon, 10 Apr 2017 18:34:57 GMT"}],"recorded_with":"VCR 3.0.3"}

0 comments on commit 62caf2a

Please sign in to comment.