tracker_api 0.2.10 → 0.2.11

Sign up to get free protection for your applications and to get access to all the features.
Files changed (41) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +3 -1
  3. data/lib/tracker_api/client.rb +4 -1
  4. data/lib/tracker_api/endpoints/activity.rb +3 -3
  5. data/lib/tracker_api/endpoints/comments.rb +1 -1
  6. data/lib/tracker_api/endpoints/epic.rb +15 -1
  7. data/lib/tracker_api/endpoints/epics.rb +1 -1
  8. data/lib/tracker_api/endpoints/iterations.rb +1 -1
  9. data/lib/tracker_api/endpoints/labels.rb +1 -1
  10. data/lib/tracker_api/endpoints/memberships.rb +1 -1
  11. data/lib/tracker_api/endpoints/notifications.rb +1 -1
  12. data/lib/tracker_api/endpoints/projects.rb +1 -1
  13. data/lib/tracker_api/endpoints/stories.rb +1 -1
  14. data/lib/tracker_api/endpoints/story_owners.rb +20 -0
  15. data/lib/tracker_api/endpoints/tasks.rb +1 -1
  16. data/lib/tracker_api/resources/account.rb +1 -1
  17. data/lib/tracker_api/resources/activity.rb +4 -5
  18. data/lib/tracker_api/resources/change.rb +1 -1
  19. data/lib/tracker_api/resources/comment.rb +1 -1
  20. data/lib/tracker_api/resources/epic.rb +24 -2
  21. data/lib/tracker_api/resources/iteration.rb +1 -2
  22. data/lib/tracker_api/resources/label.rb +1 -1
  23. data/lib/tracker_api/resources/me.rb +3 -3
  24. data/lib/tracker_api/resources/membership_summary.rb +1 -1
  25. data/lib/tracker_api/resources/notification.rb +4 -4
  26. data/lib/tracker_api/resources/person.rb +1 -1
  27. data/lib/tracker_api/resources/primary_resource.rb +1 -1
  28. data/lib/tracker_api/resources/project.rb +21 -5
  29. data/lib/tracker_api/resources/project_membership.rb +2 -2
  30. data/lib/tracker_api/resources/shared/has_id.rb +18 -0
  31. data/lib/tracker_api/resources/story.rb +22 -8
  32. data/lib/tracker_api/resources/task.rb +1 -1
  33. data/lib/tracker_api/version.rb +1 -1
  34. data/lib/tracker_api.rb +9 -2
  35. data/test/minitest_helper.rb +1 -0
  36. data/test/story_test.rb +88 -47
  37. data/test/vcr/cassettes/get_owners_for_story.json +1 -0
  38. data/test/vcr/cassettes/get_story_owners.json +1 -0
  39. data/test/vcr/cassettes/get_story_with_owners.json +1 -0
  40. metadata +10 -3
  41. data/lib/tracker_api/resources/base.rb +0 -16
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: a617d2b19c294a813a8edf94eed605c18daa6969
4
- data.tar.gz: 8d05e8a155dcdf15e84c0efaba86dcd6b1914e86
3
+ metadata.gz: b4cba526663ee9306a7f26b12aa8f9f0c574b935
4
+ data.tar.gz: d91cb8b54927afb96dfb2ffd266a21fe9e4732fb
5
5
  SHA512:
6
- metadata.gz: 374ec16671b186d2309513d2419e7839f98b0e71e88fee635660a18f2f5a406e5133cc24252fc8cebb6a5094d2b3cdc725edf879ad94c4bf585aa79ded685fd9
7
- data.tar.gz: b8b668d588b2e6ffc4fd8455d0ef0b288e5f25c8ed896385fc707e35f608639e93781aef742aec4ef9483b48ef54a9ae8cf9e01fba4cc38dd43f2563d5f47de4
6
+ metadata.gz: a7784c955bea7296f2f92903c5722d114d46895935acb4d8f83519b1fd885803cf4be195cfc0835966a57ea745b4b8fbe295dd0d72bb985e66bd8c17f4bc9b72
7
+ data.tar.gz: 3fc7c32d7be48c0eaec816b99698ffb7f9ca7d12290dc68aca563ae90bc1a2f1b36702a77fb4be687beaf38ffa1a01fbbbde44f6ef6ebe5585861b1d3897d53f
data/README.md CHANGED
@@ -53,7 +53,7 @@ story.activity # Get
53
53
 
54
54
  story.name = 'Save the Ewoks' # Update a single story attribute
55
55
  story.attributes = { name: 'Save the Ewoks', description: '...' } # Update multiple story attributes
56
- story.labels << Label.new(name: 'Endor') # Add a new label to an existing story
56
+ story.labels << TrackerApi::Resources::Label.new(name: 'Endor') # Add a new label to an existing story
57
57
  story.save # Save changes made to a story
58
58
 
59
59
  epics = project.epics # Get all epics for a project
@@ -71,6 +71,8 @@ client = TrackerApi::Client.new(token: 'my-api-token') # Crea
71
71
  client.project(project_id, fields: ':default,labels(name)') # Eagerly get labels with a project
72
72
  client.project(project_id, fields: ':default,epics') # Eagerly get epics with a project
73
73
  client.project(project_id).stories(fields: ':default,tasks') # Eagerly get stories with tasks
74
+ client.project.stories(fields: ':default,comments(:default,person)') # Eagerly get stories with comments and the person that made the comment
75
+ story.comments(fields: ':default,person') # Eagerly get comments and the person that made the comment for a story
74
76
  ```
75
77
 
76
78
  ## TODO
@@ -20,7 +20,7 @@ module TrackerApi
20
20
  #
21
21
  # @example Creating a Client
22
22
  # Client.new token: 'my-super-special-token'
23
- def initialize(options={})
23
+ def initialize(options={}, &block)
24
24
  url = options.fetch(:url, 'https://www.pivotaltracker.com')
25
25
  @url = Addressable::URI.parse(url).to_s
26
26
  @api_version = options.fetch(:api_version, '/services/v5')
@@ -31,6 +31,8 @@ module TrackerApi
31
31
  @token = options[:token]
32
32
  raise 'Missing required options: :token' unless @token
33
33
 
34
+ @faraday_block = block if block_given?
35
+
34
36
  @connection = Faraday.new({ url: @url }.merge(connection_options)) do |builder|
35
37
  # response
36
38
  builder.use Faraday::Response::RaiseError
@@ -41,6 +43,7 @@ module TrackerApi
41
43
  builder.request :json
42
44
 
43
45
  builder.use TrackerApi::Logger, @logger
46
+ @faraday_block.call(builder) if @faraday_block
44
47
  builder.adapter adapter
45
48
  end
46
49
  end
@@ -9,7 +9,7 @@ module TrackerApi
9
9
 
10
10
  def get(params={})
11
11
  data = client.paginate("/my/activity", params: params)
12
- raise TrackerApi::Errors::UnexpectedData, 'Array of activities expected' unless data.is_a? Array
12
+ raise Errors::UnexpectedData, 'Array of activities expected' unless data.is_a? Array
13
13
 
14
14
  data.map do |activity|
15
15
  Resources::Activity.new({ client: client }.merge(activity))
@@ -18,7 +18,7 @@ module TrackerApi
18
18
 
19
19
  def get_project(project_id, params={})
20
20
  data = client.paginate("/projects/#{project_id}/activity", params: params)
21
- raise TrackerApi::Errors::UnexpectedData, 'Array of activities expected' unless data.is_a? Array
21
+ raise Errors::UnexpectedData, 'Array of activities expected' unless data.is_a? Array
22
22
 
23
23
  data.map do |activity|
24
24
  Resources::Activity.new({ client: client }.merge(activity))
@@ -27,7 +27,7 @@ module TrackerApi
27
27
 
28
28
  def get_story(project_id, story_id, params={})
29
29
  data = client.paginate("/projects/#{project_id}/stories/#{story_id}/activity", params: params)
30
- raise TrackerApi::Errors::UnexpectedData, 'Array of activities expected' unless data.is_a? Array
30
+ raise Errors::UnexpectedData, 'Array of activities expected' unless data.is_a? Array
31
31
 
32
32
  data.map do |activity|
33
33
  Resources::Activity.new({ client: client }.merge(activity))
@@ -9,7 +9,7 @@ module TrackerApi
9
9
 
10
10
  def get(project_id, story_id, params={})
11
11
  data = client.paginate("/projects/#{project_id}/stories/#{story_id}/comments", params: params)
12
- raise TrackerApi::Errors::UnexpectedData, 'Array of comments expected' unless data.is_a? Array
12
+ raise Errors::UnexpectedData, 'Array of comments expected' unless data.is_a? Array
13
13
 
14
14
  data.map do |comment|
15
15
  Resources::Comment.new({ story_id: story_id }.merge(comment))
@@ -10,7 +10,21 @@ module TrackerApi
10
10
  def get(project_id, id, params={})
11
11
  data = client.get("/projects/#{project_id}/epics/#{id}", params: params).body
12
12
 
13
- Resources::Epic.new({ project_id: project_id }.merge(data))
13
+ Resources::Epic.new({ client: client, project_id: project_id }.merge(data))
14
+ end
15
+
16
+ def create(project_id, params={})
17
+ data = client.post("/projects/#{project_id}/epics", params: params).body
18
+
19
+ Resources::Epic.new({ client: client }.merge(data))
20
+ end
21
+
22
+ def update(epic, params={})
23
+ raise ArgumentError, 'Valid epic required to update.' unless epic.instance_of?(Resources::Epic)
24
+
25
+ data = client.put("/projects/#{epic.project_id}/epics/#{epic.id}", params: params).body
26
+
27
+ epic.attributes = data
14
28
  end
15
29
  end
16
30
  end
@@ -9,7 +9,7 @@ module TrackerApi
9
9
 
10
10
  def get(project_id, params={})
11
11
  data = client.paginate("/projects/#{project_id}/epics", params: params)
12
- raise TrackerApi::Errors::UnexpectedData, 'Array of epics expected' unless data.is_a? Array
12
+ raise Errors::UnexpectedData, 'Array of epics expected' unless data.is_a? Array
13
13
 
14
14
  data.map do |epic|
15
15
  Resources::Epic.new({ project_id: project_id }.merge(epic))
@@ -9,7 +9,7 @@ module TrackerApi
9
9
 
10
10
  def get(project_id, params={})
11
11
  data = client.paginate("/projects/#{project_id}/iterations", params: params)
12
- raise TrackerApi::Errors::UnexpectedData, 'Array of iterations expected' unless data.is_a? Array
12
+ raise Errors::UnexpectedData, 'Array of iterations expected' unless data.is_a? Array
13
13
 
14
14
  data.map do |iteration|
15
15
  Resources::Iteration.new({ client: client, project_id: project_id }.merge(iteration))
@@ -9,7 +9,7 @@ module TrackerApi
9
9
 
10
10
  def get(project_id, params={})
11
11
  data = client.paginate("/projects/#{project_id}/labels", params: params)
12
- raise TrackerApi::Errors::UnexpectedData, 'Array of labels expected' unless data.is_a? Array
12
+ raise Errors::UnexpectedData, 'Array of labels expected' unless data.is_a? Array
13
13
 
14
14
  data.map do |label|
15
15
  Resources::Label.new({ project_id: project_id }.merge(label))
@@ -9,7 +9,7 @@ module TrackerApi
9
9
 
10
10
  def get(project_id, params={})
11
11
  data = client.paginate("/projects/#{project_id}/memberships", params: params)
12
- raise TrackerApi::Errors::UnexpectedData, 'Array of memberships expected' unless data.is_a? Array
12
+ raise Errors::UnexpectedData, 'Array of memberships expected' unless data.is_a? Array
13
13
 
14
14
  data.map do |membership|
15
15
  Resources::ProjectMembership.new({ project_id: project_id }.merge(membership))
@@ -9,7 +9,7 @@ module TrackerApi
9
9
 
10
10
  def get(params={})
11
11
  data = client.paginate('/my/notifications', params: params)
12
- raise TrackerApi::Errors::UnexpectedData, 'Array of notifications expected' unless data.is_a? Array
12
+ raise Errors::UnexpectedData, 'Array of notifications expected' unless data.is_a? Array
13
13
 
14
14
  data.map do |notification|
15
15
  Resources::Notification.new({ client: client }.merge(notification))
@@ -9,7 +9,7 @@ module TrackerApi
9
9
 
10
10
  def get(params={})
11
11
  data = client.paginate('/projects', params: params)
12
- raise TrackerApi::Errors::UnexpectedData, 'Array of projects expected' unless data.is_a? Array
12
+ raise Errors::UnexpectedData, 'Array of projects expected' unless data.is_a? Array
13
13
 
14
14
  data.map { |project| Resources::Project.new({ client: client }.merge(project)) }
15
15
  end
@@ -9,7 +9,7 @@ module TrackerApi
9
9
 
10
10
  def get(project_id, params={})
11
11
  data = client.paginate("/projects/#{project_id}/stories", params: params)
12
- raise TrackerApi::Errors::UnexpectedData, 'Array of stories expected' unless data.is_a? Array
12
+ raise Errors::UnexpectedData, 'Array of stories expected' unless data.is_a? Array
13
13
 
14
14
  data.map do |story|
15
15
  Resources::Story.new({ client: client, project_id: project_id }.merge(story))
@@ -0,0 +1,20 @@
1
+ module TrackerApi
2
+ module Endpoints
3
+ class StoryOwners
4
+ attr_accessor :client
5
+
6
+ def initialize(client)
7
+ @client = client
8
+ end
9
+
10
+ def get(project_id, story_id, params={})
11
+ data = client.paginate("/projects/#{project_id}/stories/#{story_id}/owners", params: params)
12
+ raise Errors::UnexpectedData, 'Array of comments expected' unless data.is_a? Array
13
+
14
+ data.map do |owner|
15
+ Resources::Person.new({ story_id: story_id }.merge(owner))
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
@@ -9,7 +9,7 @@ module TrackerApi
9
9
 
10
10
  def get(project_id, story_id, params={})
11
11
  data = client.paginate("/projects/#{project_id}/stories/#{story_id}/tasks", params: params)
12
- raise TrackerApi::Errors::UnexpectedData, 'Array of tasks expected' unless data.is_a? Array
12
+ raise Errors::UnexpectedData, 'Array of tasks expected' unless data.is_a? Array
13
13
 
14
14
  data.map do |task|
15
15
  Resources::Task.new({ story_id: story_id }.merge(task))
@@ -1,7 +1,7 @@
1
1
  module TrackerApi
2
2
  module Resources
3
3
  class Account
4
- include Resources::Base
4
+ include Shared::HasId
5
5
 
6
6
  attribute :created_at, DateTime
7
7
  attribute :status, String
@@ -2,7 +2,6 @@ module TrackerApi
2
2
  module Resources
3
3
  class Activity
4
4
  include Virtus.model
5
-
6
5
  include Equalizer.new(:guid)
7
6
 
8
7
  attribute :client
@@ -12,10 +11,10 @@ module TrackerApi
12
11
  attribute :project_version, Integer
13
12
  attribute :message, String
14
13
  attribute :highlight, String
15
- attribute :changes, Array[Resources::Change]
16
- attribute :primary_resources, Array[Resources::PrimaryResource]
17
- attribute :project, Resources::Project
18
- attribute :performed_by, Resources::Person
14
+ attribute :changes, Array[Change]
15
+ attribute :primary_resources, Array[PrimaryResource]
16
+ attribute :project, Project
17
+ attribute :performed_by, Person
19
18
  attribute :occurred_at, DateTime
20
19
 
21
20
  def project=(data)
@@ -1,7 +1,7 @@
1
1
  module TrackerApi
2
2
  module Resources
3
3
  class Change
4
- include Resources::Base
4
+ include Shared::HasId
5
5
 
6
6
  attribute :change_type, String
7
7
  attribute :kind, String
@@ -1,7 +1,7 @@
1
1
  module TrackerApi
2
2
  module Resources
3
3
  class Comment
4
- include Resources::Base
4
+ include Shared::HasId
5
5
 
6
6
  attribute :story_id, Integer
7
7
  attribute :epic_id, Integer
@@ -1,16 +1,38 @@
1
1
  module TrackerApi
2
2
  module Resources
3
3
  class Epic
4
- include Resources::Base
4
+ include Shared::HasId
5
5
 
6
+ attribute :client
7
+
8
+ attribute :comment_ids, Array[Integer]
9
+ attribute :comments, Array[Comment]
6
10
  attribute :created_at, DateTime
7
11
  attribute :description, String
12
+ attribute :follower_ids, Array[Integer]
13
+ attribute :followers, Array[Person]
8
14
  attribute :kind, String
9
- attribute :label, TrackerApi::Resources::Label
15
+ attribute :label, Label
16
+ attribute :label_id, Integer
10
17
  attribute :name, String
11
18
  attribute :project_id, Integer
12
19
  attribute :updated_at, DateTime
13
20
  attribute :url, String
21
+
22
+ class UpdateRepresenter < Representable::Decorator
23
+ include Representable::JSON
24
+
25
+ property :name
26
+ property :description
27
+ property :label, class: Label, decorator: Label::UpdateRepresenter, render_empty: true
28
+ end
29
+
30
+ # Save changes to an existing Epic.
31
+ def save
32
+ raise ArgumentError, 'Can not update an epic with an unknown project_id.' if project_id.nil?
33
+
34
+ Endpoints::Epic.new(client).update(self, UpdateRepresenter.new(self))
35
+ end
14
36
  end
15
37
  end
16
38
  end
@@ -2,7 +2,6 @@ module TrackerApi
2
2
  module Resources
3
3
  class Iteration
4
4
  include Virtus.model
5
-
6
5
  include Equalizer.new(:number, :project_id)
7
6
 
8
7
  attribute :client
@@ -14,7 +13,7 @@ module TrackerApi
14
13
  attribute :planned, Boolean
15
14
  attribute :project_id, Integer
16
15
  attribute :start, DateTime
17
- attribute :stories, [TrackerApi::Resources::Story]
16
+ attribute :stories, [Story]
18
17
  attribute :story_ids, [Integer]
19
18
  attribute :team_strength, Float
20
19
 
@@ -1,7 +1,7 @@
1
1
  module TrackerApi
2
2
  module Resources
3
3
  class Label
4
- include Resources::Base
4
+ include Shared::HasId
5
5
 
6
6
  attribute :created_at, DateTime
7
7
  attribute :kind, String
@@ -1,16 +1,16 @@
1
1
  module TrackerApi
2
2
  module Resources
3
3
  class Me
4
- include Resources::Base
4
+ include Shared::HasId
5
5
 
6
6
  attribute :name, String
7
7
  attribute :initials, String
8
8
  attribute :username, String
9
- attribute :time_zone, TrackerApi::Resources::TimeZone
9
+ attribute :time_zone, TimeZone
10
10
  attribute :api_token, String
11
11
  attribute :has_google_identity, Boolean
12
12
  attribute :project_ids, Array[Integer]
13
- attribute :projects, [TrackerApi::Resources::MembershipSummary]
13
+ attribute :projects, [MembershipSummary]
14
14
  attribute :workspace_ids, Array[Integer]
15
15
  attribute :email, String
16
16
  attribute :receives_in_app_notifications, Boolean
@@ -1,7 +1,7 @@
1
1
  module TrackerApi
2
2
  module Resources
3
3
  class MembershipSummary
4
- include Resources::Base
4
+ include Shared::HasId
5
5
 
6
6
  attribute :kind, String
7
7
  attribute :last_viewed_at, DateTime
@@ -1,15 +1,15 @@
1
1
  module TrackerApi
2
2
  module Resources
3
3
  class Notification
4
- include Resources::Base
4
+ include Shared::HasId
5
5
 
6
6
  attribute :client
7
7
 
8
8
  attribute :message, String
9
9
  attribute :kind, String
10
- attribute :project, TrackerApi::Resources::Project
11
- attribute :story, TrackerApi::Resources::Story
12
- attribute :performer, TrackerApi::Resources::Person
10
+ attribute :project, Project
11
+ attribute :story, Story
12
+ attribute :performer, Person
13
13
  attribute :created_at, DateTime
14
14
  attribute :updated_at, DateTime
15
15
 
@@ -1,7 +1,7 @@
1
1
  module TrackerApi
2
2
  module Resources
3
3
  class Person
4
- include Resources::Base
4
+ include Shared::HasId
5
5
 
6
6
  attribute :kind, String
7
7
  attribute :name, String
@@ -1,7 +1,7 @@
1
1
  module TrackerApi
2
2
  module Resources
3
3
  class PrimaryResource
4
- include Resources::Base
4
+ include Shared::HasId
5
5
 
6
6
  attribute :kind, String
7
7
  attribute :name, String
@@ -1,11 +1,11 @@
1
1
  module TrackerApi
2
2
  module Resources
3
3
  class Project
4
- include Resources::Base
4
+ include Shared::HasId
5
5
 
6
6
  attribute :client
7
7
 
8
- attribute :account, TrackerApi::Resources::Account
8
+ attribute :account, Account
9
9
  attribute :account_id, Integer
10
10
  attribute :atom_enabled, Boolean
11
11
  attribute :bugs_and_chores_are_estimatable, Boolean
@@ -18,13 +18,13 @@ module TrackerApi
18
18
  attribute :enable_planned_mode, Boolean
19
19
  attribute :enable_tasks, Boolean
20
20
  attribute :epic_ids, Array[Integer]
21
- attribute :epics, Array[TrackerApi::Resources::Epic]
21
+ attribute :epics, Array[Epic]
22
22
  attribute :has_google_domain, Boolean
23
23
  attribute :initial_velocity, Integer
24
24
  attribute :iteration_length, Integer
25
25
  attribute :kind, String
26
26
  attribute :label_ids, Array[Integer]
27
- attribute :labels, Array[TrackerApi::Resources::Label]
27
+ attribute :labels, Array[Label]
28
28
  attribute :name, String
29
29
  attribute :number_of_done_iterations_to_show, Integer
30
30
  attribute :point_scale, String
@@ -33,7 +33,7 @@ module TrackerApi
33
33
  attribute :public, Boolean
34
34
  attribute :start_date, DateTime
35
35
  attribute :start_time, DateTime
36
- attribute :time_zone, TrackerApi::Resources::TimeZone
36
+ attribute :time_zone, TimeZone
37
37
  attribute :updated_at, DateTime
38
38
  attribute :velocity_averaged_over, Integer
39
39
  attribute :version, Integer
@@ -149,6 +149,22 @@ module TrackerApi
149
149
  Endpoints::Story.new(client).create(id, params)
150
150
  end
151
151
 
152
+ # Find a epic by id for the project.
153
+ #
154
+ # @param [Fixnum] epic_id id of epic to get
155
+ # @return [Epic] epic with given id
156
+ def epic(epic_id, params={})
157
+ Endpoints::Epic.new(client).get(id, epic_id, params)
158
+ end
159
+
160
+ # Create a new epic in the project.
161
+ #
162
+ # @param [Hash] params attributes to create the epic with
163
+ # @return [epic] newly created Epic
164
+ def create_epic(params)
165
+ Endpoints::Epic.new(client).create(id, params)
166
+ end
167
+
152
168
  # Add a new membership for the project.
153
169
  #
154
170
  # @param [Hash] params attributes to add a member; must have at least email or user_id
@@ -1,7 +1,7 @@
1
1
  module TrackerApi
2
2
  module Resources
3
3
  class ProjectMembership
4
- include Resources::Base
4
+ include Shared::HasId
5
5
 
6
6
  attribute :person_id, Integer
7
7
  attribute :project_id, Integer
@@ -9,7 +9,7 @@ module TrackerApi
9
9
  attribute :project_color, String
10
10
  attribute :wants_comment_notification_emails, Boolean
11
11
  attribute :kind, String
12
- attribute :person, TrackerApi::Resources::Person
12
+ attribute :person, Person
13
13
  end
14
14
  end
15
15
  end
@@ -0,0 +1,18 @@
1
+ require 'virtus'
2
+
3
+ module TrackerApi
4
+ module Resources
5
+ module Shared
6
+ module HasId
7
+ def self.included(base)
8
+ base.class_eval do
9
+ include Virtus.model
10
+ include Equalizer.new(:id)
11
+
12
+ attribute :id, Integer
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
@@ -1,13 +1,13 @@
1
1
  module TrackerApi
2
2
  module Resources
3
3
  class Story
4
- include Resources::Base
4
+ include Shared::HasId
5
5
 
6
6
  attribute :client
7
7
 
8
8
  attribute :accepted_at, DateTime
9
9
  attribute :comment_ids, Array[Integer]
10
- attribute :comments, Array[Resources::Comment]
10
+ attribute :comments, Array[Comment], :default => []
11
11
  attribute :created_at, DateTime
12
12
  attribute :current_state, String # (accepted, delivered, finished, started, rejected, planned, unstarted, unscheduled)
13
13
  attribute :deadline, DateTime
@@ -15,21 +15,23 @@ module TrackerApi
15
15
  attribute :estimate, Float
16
16
  attribute :external_id, String
17
17
  attribute :follower_ids, Array[Integer]
18
+ attribute :followers, Array[Person]
18
19
  attribute :integration_id, Integer
19
20
  attribute :kind, String
20
21
  attribute :label_ids, Array[Integer]
21
- attribute :labels, Array[Resources::Label]
22
+ attribute :labels, Array[Label], :default => []
22
23
  attribute :name, String
23
24
  attribute :owned_by_id, Integer # deprecated!
25
+ attribute :owned_by, Person
24
26
  attribute :owner_ids, Array[Integer]
25
- attribute :owners, Array[Resources::Person]
27
+ attribute :owners, Array[Person], :default => []
26
28
  attribute :planned_iteration_number, Integer
27
29
  attribute :project_id, Integer
28
30
  attribute :requested_by, Person
29
31
  attribute :requested_by_id, Integer
30
32
  attribute :story_type, String # (feature, bug, chore, release)
31
33
  attribute :task_ids, Array[Integer]
32
- attribute :tasks, Array[Resources::Task]
34
+ attribute :tasks, Array[Task], :default => []
33
35
  attribute :updated_at, DateTime
34
36
  attribute :url, String
35
37
 
@@ -47,7 +49,7 @@ module TrackerApi
47
49
  property :deadline
48
50
  property :requested_by_id
49
51
  property :owner_ids
50
- collection :labels, class: Resources::Label, decorator: Resources::Label::UpdateRepresenter, render_empty: true
52
+ collection :labels, class: Label, decorator: Label::UpdateRepresenter, render_empty: true
51
53
  property :integration_id
52
54
  property :external_id
53
55
  end
@@ -70,7 +72,7 @@ module TrackerApi
70
72
  # @param [Hash] params
71
73
  # @return [Array[Comment]]
72
74
  def comments(params = {})
73
- if @comments && @comments.any?
75
+ if params.blank? && @comments.any?
74
76
  @comments
75
77
  else
76
78
  @comments = Endpoints::Comments.new(client).get(project_id, id, params)
@@ -82,13 +84,25 @@ module TrackerApi
82
84
  # @param [Hash] params
83
85
  # @return [Array[Task]]
84
86
  def tasks(params = {})
85
- if @tasks && @tasks.any?
87
+ if params.blank? && @tasks.any?
86
88
  @tasks
87
89
  else
88
90
  @tasks = Endpoints::Tasks.new(client).get(project_id, id, params)
89
91
  end
90
92
  end
91
93
 
94
+ # Provides a list of all the owners of the story.
95
+ #
96
+ # @param [Hash] params
97
+ # @return [Array[Person]]
98
+ def owners(params = {})
99
+ if params.blank? && @owners.any?
100
+ @owners
101
+ else
102
+ @owners = Endpoints::StoryOwners.new(client).get(project_id, id, params)
103
+ end
104
+ end
105
+
92
106
  # @param [Hash] params attributes to create the task with
93
107
  # @return [Task] newly created Task
94
108
  def create_task(params)
@@ -1,7 +1,7 @@
1
1
  module TrackerApi
2
2
  module Resources
3
3
  class Task
4
- include Resources::Base
4
+ include Shared::HasId
5
5
 
6
6
  attribute :story_id, Integer
7
7
  attribute :description, String
@@ -1,3 +1,3 @@
1
1
  module TrackerApi
2
- VERSION = '0.2.10'
2
+ VERSION = '0.2.11'
3
3
  end
data/lib/tracker_api.rb CHANGED
@@ -4,7 +4,11 @@ require 'tracker_api/version'
4
4
  require 'virtus'
5
5
  require 'faraday'
6
6
  require 'faraday_middleware'
7
- require 'core_ext/object/blank'
7
+ if defined?(ActiveSupport)
8
+ require 'active_support/core_ext/object/blank'
9
+ else
10
+ require 'core_ext/object/blank'
11
+ end
8
12
  require 'equalizer'
9
13
  require 'representable/json'
10
14
  require 'oj'
@@ -38,13 +42,16 @@ module TrackerApi
38
42
  autoload :Projects, 'tracker_api/endpoints/projects'
39
43
  autoload :Stories, 'tracker_api/endpoints/stories'
40
44
  autoload :Story, 'tracker_api/endpoints/story'
45
+ autoload :StoryOwners, 'tracker_api/endpoints/story_owners'
41
46
  autoload :Task, 'tracker_api/endpoints/task'
42
47
  autoload :Tasks, 'tracker_api/endpoints/tasks'
43
48
  autoload :Comments, 'tracker_api/endpoints/comments'
44
49
  end
45
50
 
46
51
  module Resources
47
- autoload :Base, 'tracker_api/resources/base'
52
+ module Shared
53
+ autoload :HasId, 'tracker_api/resources/shared/has_id'
54
+ end
48
55
  autoload :Activity, 'tracker_api/resources/activity'
49
56
  autoload :Account, 'tracker_api/resources/account'
50
57
  autoload :Change, 'tracker_api/resources/change'
@@ -30,3 +30,4 @@ PT_USER_3 = { username: 'trackerapi3', password: 'trackerapi3', token: '77f9b9a4
30
30
  PT_USERS = [PT_USER_1, PT_USER_2, PT_USER_3]
31
31
 
32
32
  LOGGER = ::Logger.new(STDOUT)
33
+ # LOGGER.level = ::Logger::DEBUG
data/test/story_test.rb CHANGED
@@ -67,71 +67,112 @@ describe TrackerApi::Resources::Story do
67
67
  story_a.equal?(story_c).must_equal false
68
68
  end
69
69
 
70
- describe '.tasks' do
71
- it 'gets all tasks for this story with eager loading' do
72
- VCR.use_cassette('get story with tasks', record: :new_episodes) do
73
- tasks = project.story(story_id, fields: ':default,tasks').tasks
74
-
75
- tasks.wont_be_empty
76
- task = tasks.first
77
- task.must_be_instance_of TrackerApi::Resources::Task
70
+ describe '.owners' do
71
+ it 'gets all owners for this story with eager loading' do
72
+ skip('Until this is resolved: https://pivotaltracker.zendesk.com/requests/35823')
73
+
74
+ story = VCR.use_cassette('get story with owners', record: :new_episodes) do
75
+ project.story(story_id, fields: ':default,owners')
78
76
  end
77
+
78
+ # this should not raise VCR::Errors::UnhandledHTTPRequestError since
79
+ # it should not be making another HTTP request.
80
+ owners = story.owners
81
+
82
+ owners.wont_be_empty
83
+ owner = owners.first
84
+ owner.must_be_instance_of TrackerApi::Resources::Person
79
85
  end
80
86
 
81
- it 'gets all tasks for this story' do
82
- VCR.use_cassette('get tasks', record: :new_episodes) do
83
- story = project.story(story_id)
84
- tasks = VCR.use_cassette('get tasks for story') { story.tasks }
87
+ it 'gets all owners for this story' do
88
+ VCR.use_cassette('get story', record: :new_episodes) do
89
+ story = project.story(story_id)
90
+ owners = VCR.use_cassette('get owners for story') { story.owners }
85
91
 
86
- tasks.wont_be_empty
87
- task = tasks.first
88
- task.must_be_instance_of TrackerApi::Resources::Task
89
- end
92
+ owners.wont_be_empty
93
+ owner = owners.first
94
+ owner.must_be_instance_of TrackerApi::Resources::Person
95
+ end
96
+ end
97
+ end
98
+
99
+ describe '.tasks' do
100
+ it 'gets all tasks for this story with eager loading' do
101
+ VCR.use_cassette('get story with tasks', record: :new_episodes) do
102
+ tasks = project.story(story_id, fields: ':default,tasks').tasks
103
+
104
+ tasks.wont_be_empty
105
+ task = tasks.first
106
+ task.must_be_instance_of TrackerApi::Resources::Task
90
107
  end
108
+ end
109
+
110
+ it 'gets all tasks for this story' do
111
+ VCR.use_cassette('get tasks', record: :new_episodes) do
112
+ story = project.story(story_id)
113
+ tasks = VCR.use_cassette('get tasks for story') { story.tasks }
114
+
115
+ tasks.wont_be_empty
116
+ task = tasks.first
117
+ task.must_be_instance_of TrackerApi::Resources::Task
118
+ end
119
+ end
91
120
 
92
- it 'gets all tasks even when the project_id is excluded from the story fields' do
93
- VCR.use_cassette('get tasks when stories filtered', record: :new_episodes) do
94
- stories = project.stories(with_state: 'unstarted', fields: 'name,story_type')
95
- stories.each do |story|
96
- tasks = story.tasks
97
- unless tasks.empty?
98
- task = tasks.first
99
- task.must_be_instance_of TrackerApi::Resources::Task
100
- end
121
+ it 'gets all tasks even when the project_id is excluded from the story fields' do
122
+ VCR.use_cassette('get tasks when stories filtered', record: :new_episodes) do
123
+ stories = project.stories(with_state: 'unstarted', fields: 'name,story_type')
124
+ stories.each do |story|
125
+ tasks = story.tasks
126
+ unless tasks.empty?
127
+ task = tasks.first
128
+ task.must_be_instance_of TrackerApi::Resources::Task
101
129
  end
102
130
  end
103
131
  end
132
+ end
104
133
 
105
- it 'can create task' do
106
- VCR.use_cassette('create task') do
107
- task = project.story(story_id).create_task(description: 'Test task')
134
+ it 'can create task' do
135
+ VCR.use_cassette('create task') do
136
+ task = project.story(story_id).create_task(description: 'Test task')
108
137
 
109
- task.must_be_instance_of TrackerApi::Resources::Task
110
- task.id.wont_be_nil
111
- task.id.must_be :>, 0
112
- task.description.must_equal 'Test task'
113
- end
138
+ task.must_be_instance_of TrackerApi::Resources::Task
139
+ task.id.wont_be_nil
140
+ task.id.must_be :>, 0
141
+ task.description.must_equal 'Test task'
114
142
  end
115
143
  end
144
+ end
116
145
 
117
- describe '.activity' do
118
- before do
119
- # create some activity
120
- story.name = "#{story.name}+"
146
+ describe '.activity' do
147
+ before do
148
+ # create some activity
149
+ story.name = "#{story.name}+"
121
150
 
122
- VCR.use_cassette('update story to create activity', record: :new_episodes) do
123
- story.save
124
- end
151
+ VCR.use_cassette('update story to create activity', record: :new_episodes) do
152
+ story.save
125
153
  end
154
+ end
126
155
 
127
- it 'gets all the activity for this story' do
128
- VCR.use_cassette('get story activity', record: :new_episodes) do
129
- activity = story.activity
156
+ it 'gets all the activity for this story' do
157
+ VCR.use_cassette('get story activity', record: :new_episodes) do
158
+ activity = story.activity
130
159
 
131
- activity.wont_be_empty
132
- event = activity.first
133
- event.must_be_instance_of TrackerApi::Resources::Activity
134
- end
160
+ activity.wont_be_empty
161
+ event = activity.first
162
+ event.must_be_instance_of TrackerApi::Resources::Activity
135
163
  end
136
164
  end
137
165
  end
166
+
167
+ describe '.owners' do
168
+ it 'gets all owners of this story' do
169
+ VCR.use_cassette('get story owners', record: :new_episodes) do
170
+ owners = story.owners
171
+
172
+ owners.wont_be_empty
173
+ owner = owners.first
174
+ owner.must_be_instance_of TrackerApi::Resources::Person
175
+ end
176
+ end
177
+ end
178
+ end
@@ -0,0 +1 @@
1
+ {"http_interactions":[{"request":{"method":"get","uri":"https://www.pivotaltracker.com/services/v5/projects/1027488/stories/66728004/owners","body":{"encoding":"US-ASCII","string":""},"headers":{"User-Agent":["Ruby/2.1.5 (x86_64-darwin14.0; ruby) TrackerApi/0.2.10 Faraday/0.9.1"],"X-TrackerToken":["d55c3bc1f74346b843ca84ba340b29bf"]}},"response":{"status":{"code":200,"message":null},"headers":{"Content-Type":["application/json; charset=utf-8"],"Status":["200 OK"],"X-Tracker-Project-Version":["78"],"X-UA-Compatible":["IE=Edge,chrome=1"],"ETag":["\"1b3412c670febaa8cc831588a4b38288\""],"Cache-Control":["max-age=0, private, must-revalidate"],"X-Request-Id":["468eec53ea706a1fdf0b69be835c09f4"],"X-Runtime":["0.057243"],"Date":["Mon, 20 Jul 2015 20:16:28 GMT"],"X-Rack-Cache":["miss"],"X-Powered-By":["Phusion Passenger 4.0.41"],"Server":["nginx/1.6.0 + Phusion Passenger 4.0.41"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Credentials":["false"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"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"],"X-Tracker-Client-Pinger-Interval":["12"]},"body":{"encoding":"UTF-8","string":"[{\"kind\":\"person\",\"id\":1266314,\"name\":\"Tracker API User1\",\"email\":\"forestcarlisle+trackerapi1@gmail.com\",\"initials\":\"TU1\",\"username\":\"trackerapi1\"},{\"kind\":\"person\",\"id\":1266316,\"name\":\"Tracker API User2\",\"email\":\"forestcarlisle+trackerapi2@gmail.com\",\"initials\":\"TU2\",\"username\":\"trackerapi2\"}]"},"http_version":null},"recorded_at":"Mon, 20 Jul 2015 20:16:49 GMT"}],"recorded_with":"VCR 2.9.3"}
@@ -0,0 +1 @@
1
+ {"http_interactions":[{"request":{"method":"get","uri":"https://www.pivotaltracker.com/services/v5/projects/1027488/stories/66728004/owners","body":{"encoding":"US-ASCII","string":""},"headers":{"User-Agent":["Ruby/2.1.5 (x86_64-darwin14.0; ruby) TrackerApi/0.2.9 Faraday/0.9.1"],"X-TrackerToken":["d55c3bc1f74346b843ca84ba340b29bf"]}},"response":{"status":{"code":200,"message":null},"headers":{"Content-Type":["application/json; charset=utf-8"],"Status":["200 OK"],"X-Tracker-Project-Version":["66"],"X-UA-Compatible":["IE=Edge,chrome=1"],"ETag":["\"2694c3642376e2adeda5f9cea6310da6\""],"Cache-Control":["max-age=0, private, must-revalidate"],"X-Request-Id":["0d0edc87fef723a07ac224c1b7e4ba9f"],"X-Runtime":["0.051762"],"Date":["Wed, 24 Jun 2015 21:06:02 GMT"],"X-Rack-Cache":["miss"],"X-Powered-By":["Phusion Passenger 4.0.41"],"Server":["nginx/1.6.0 + Phusion Passenger 4.0.41"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Credentials":["false"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"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"],"X-Tracker-Client-Pinger-Interval":["12"]},"body":{"encoding":"UTF-8","string":"[{\"kind\":\"person\",\"id\":22778038,\"name\":\"John Smith\",\"email\":\"john.smith@example.org\",\"initials\":\"JS\",\"username\":\"john.smith\"},{\"kind\":\"person\",\"id\":32873326,\"name\":\"Jane Smith\",\"email\":\"jane.smith@example.org\",\"initials\":\"JaS\",\"username\":\"jane.smith\"}]"},"http_version":null},"recorded_at":"Wed, 24 Jun 2015 21:06:11 GMT"}],"recorded_with":"VCR 2.9.3"}
@@ -0,0 +1 @@
1
+ {"http_interactions":[{"request":{"method":"get","uri":"https://www.pivotaltracker.com/services/v5/projects/1027488/stories/66728004?fields=%3Adefault%2Cowners","body":{"encoding":"US-ASCII","string":""},"headers":{"User-Agent":["Ruby/2.1.5 (x86_64-darwin14.0; ruby) TrackerApi/0.2.10 Faraday/0.9.1"],"X-TrackerToken":["d55c3bc1f74346b843ca84ba340b29bf"]}},"response":{"status":{"code":200,"message":null},"headers":{"Content-Type":["application/json; charset=utf-8"],"Status":["200 OK"],"X-Tracker-Project-Version":["78"],"X-UA-Compatible":["IE=Edge,chrome=1"],"ETag":["\"1f26a282513ccf8b2f5b0e873938e95b\""],"Cache-Control":["max-age=0, private, must-revalidate"],"X-Request-Id":["a4c140d6a71860108446763556b27a88"],"X-Runtime":["0.074538"],"Date":["Mon, 20 Jul 2015 20:12:29 GMT"],"X-Rack-Cache":["miss"],"X-Powered-By":["Phusion Passenger 4.0.41"],"Server":["nginx/1.6.0 + Phusion Passenger 4.0.41"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Credentials":["false"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"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"],"X-Tracker-Client-Pinger-Interval":["12"]},"body":{"encoding":"UTF-8","string":"{\"kind\":\"story\",\"id\":66728004,\"created_at\":\"2014-02-17T00:00:00Z\",\"updated_at\":\"2015-07-02T16:57:53Z\",\"story_type\":\"bug\",\"name\":\"Some product photos not scaled properly when browsing products++\",\"description\":\"+\",\"current_state\":\"started\",\"requested_by_id\":1266314,\"project_id\":1027488,\"url\":\"https://www.pivotaltracker.com/story/show/66728004\",\"owner_ids\":[1266314,1266316],\"owners\":[],\"labels\":[{\"id\":11049868,\"project_id\":1027488,\"kind\":\"label\",\"name\":\"label1\",\"created_at\":\"2015-03-07T12:51:39Z\",\"updated_at\":\"2015-03-07T12:51:39Z\"},{\"id\":11049870,\"project_id\":1027488,\"kind\":\"label\",\"name\":\"label2\",\"created_at\":\"2015-03-07T12:51:39Z\",\"updated_at\":\"2015-03-07T12:51:39Z\"},{\"id\":12049778,\"project_id\":1027488,\"kind\":\"label\",\"name\":\"super-special-label\",\"created_at\":\"2015-06-24T22:46:12Z\",\"updated_at\":\"2015-06-24T22:46:12Z\"}],\"owned_by_id\":1266314}"},"http_version":null},"recorded_at":"Mon, 20 Jul 2015 20:12:49 GMT"}],"recorded_with":"VCR 2.9.3"}
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: tracker_api
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.10
4
+ version: 0.2.11
5
5
  platform: ruby
6
6
  authors:
7
7
  - Forest Carlisle
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-06-25 00:00:00.000000000 Z
11
+ date: 2015-10-13 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -236,13 +236,13 @@ files:
236
236
  - lib/tracker_api/endpoints/projects.rb
237
237
  - lib/tracker_api/endpoints/stories.rb
238
238
  - lib/tracker_api/endpoints/story.rb
239
+ - lib/tracker_api/endpoints/story_owners.rb
239
240
  - lib/tracker_api/endpoints/task.rb
240
241
  - lib/tracker_api/endpoints/tasks.rb
241
242
  - lib/tracker_api/error.rb
242
243
  - lib/tracker_api/logger.rb
243
244
  - lib/tracker_api/resources/account.rb
244
245
  - lib/tracker_api/resources/activity.rb
245
- - lib/tracker_api/resources/base.rb
246
246
  - lib/tracker_api/resources/change.rb
247
247
  - lib/tracker_api/resources/comment.rb
248
248
  - lib/tracker_api/resources/epic.rb
@@ -255,6 +255,7 @@ files:
255
255
  - lib/tracker_api/resources/primary_resource.rb
256
256
  - lib/tracker_api/resources/project.rb
257
257
  - lib/tracker_api/resources/project_membership.rb
258
+ - lib/tracker_api/resources/shared/has_id.rb
258
259
  - lib/tracker_api/resources/story.rb
259
260
  - lib/tracker_api/resources/task.rb
260
261
  - lib/tracker_api/resources/time_zone.rb
@@ -280,12 +281,15 @@ files:
280
281
  - test/vcr/cassettes/get_labels.json
281
282
  - test/vcr/cassettes/get_me.json
282
283
  - test/vcr/cassettes/get_my_activities.json
284
+ - test/vcr/cassettes/get_owners_for_story.json
283
285
  - test/vcr/cassettes/get_project.json
284
286
  - test/vcr/cassettes/get_project_activity.json
285
287
  - test/vcr/cassettes/get_project_with_epics.json
286
288
  - test/vcr/cassettes/get_project_with_labels.json
287
289
  - test/vcr/cassettes/get_story.json
288
290
  - test/vcr/cassettes/get_story_activity.json
291
+ - test/vcr/cassettes/get_story_owners.json
292
+ - test/vcr/cassettes/get_story_with_owners.json
289
293
  - test/vcr/cassettes/get_story_with_tasks.json
290
294
  - test/vcr/cassettes/get_tasks.json
291
295
  - test/vcr/cassettes/get_tasks_for_story.json
@@ -343,12 +347,15 @@ test_files:
343
347
  - test/vcr/cassettes/get_labels.json
344
348
  - test/vcr/cassettes/get_me.json
345
349
  - test/vcr/cassettes/get_my_activities.json
350
+ - test/vcr/cassettes/get_owners_for_story.json
346
351
  - test/vcr/cassettes/get_project.json
347
352
  - test/vcr/cassettes/get_project_activity.json
348
353
  - test/vcr/cassettes/get_project_with_epics.json
349
354
  - test/vcr/cassettes/get_project_with_labels.json
350
355
  - test/vcr/cassettes/get_story.json
351
356
  - test/vcr/cassettes/get_story_activity.json
357
+ - test/vcr/cassettes/get_story_owners.json
358
+ - test/vcr/cassettes/get_story_with_owners.json
352
359
  - test/vcr/cassettes/get_story_with_tasks.json
353
360
  - test/vcr/cassettes/get_tasks.json
354
361
  - test/vcr/cassettes/get_tasks_for_story.json
@@ -1,16 +0,0 @@
1
- require 'virtus'
2
-
3
- module TrackerApi
4
- module Resources
5
- module Base
6
- def self.included(base)
7
- base.class_eval do
8
- include Virtus.model
9
- include Equalizer.new(:id)
10
-
11
- attribute :id, Integer
12
- end
13
- end
14
- end
15
- end
16
- end