tracker_api 0.2.10 → 0.2.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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