tracker_api 0.2.12 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (72) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -0
  3. data/README.md +1 -1
  4. data/lib/tracker_api.rb +5 -3
  5. data/lib/tracker_api/client.rb +12 -2
  6. data/lib/tracker_api/endpoints/epic.rb +8 -0
  7. data/lib/tracker_api/endpoints/story.rb +2 -0
  8. data/lib/tracker_api/resources/account.rb +1 -1
  9. data/lib/tracker_api/resources/activity.rb +2 -2
  10. data/lib/tracker_api/resources/change.rb +1 -1
  11. data/lib/tracker_api/resources/comment.rb +4 -4
  12. data/lib/tracker_api/resources/epic.rb +5 -5
  13. data/lib/tracker_api/resources/iteration.rb +2 -2
  14. data/lib/tracker_api/resources/label.rb +1 -1
  15. data/lib/tracker_api/resources/me.rb +4 -4
  16. data/lib/tracker_api/resources/membership_summary.rb +1 -1
  17. data/lib/tracker_api/resources/notification.rb +1 -1
  18. data/lib/tracker_api/resources/person.rb +1 -1
  19. data/lib/tracker_api/resources/primary_resource.rb +1 -1
  20. data/lib/tracker_api/resources/project.rb +11 -10
  21. data/lib/tracker_api/resources/project_membership.rb +1 -1
  22. data/lib/tracker_api/resources/shared/base.rb +30 -0
  23. data/lib/tracker_api/resources/shared/collection.rb +17 -0
  24. data/lib/tracker_api/resources/story.rb +31 -17
  25. data/lib/tracker_api/resources/task.rb +1 -1
  26. data/lib/tracker_api/version.rb +1 -1
  27. data/lib/virtus/attribute/nullify_blank.rb +27 -0
  28. data/lib/virtus/dirty_attribute.rb +104 -0
  29. data/lib/virtus/dirty_attribute/session.rb +54 -0
  30. data/test/client_test.rb +16 -1
  31. data/test/minitest_helper.rb +1 -0
  32. data/test/story_test.rb +10 -3
  33. data/test/vcr/cassettes/client_done_iterations_with_pagination.json +1 -1
  34. data/test/vcr/cassettes/client_get_all_stories_with_pagination.json +1 -1
  35. data/test/vcr/cassettes/client_get_limited_stories_with_no_pagination.json +1 -1
  36. data/test/vcr/cassettes/client_get_single_epic_by_epic_id.json +1 -0
  37. data/test/vcr/cassettes/client_get_single_story_by_story_id.json +1 -1
  38. data/test/vcr/cassettes/create_story.json +1 -1
  39. data/test/vcr/cassettes/create_story_with_lengthy_params.json +1 -1
  40. data/test/vcr/cassettes/create_task.json +1 -1
  41. data/test/vcr/cassettes/get_all_projects.json +1 -1
  42. data/test/vcr/cassettes/get_another_story.json +1 -1
  43. data/test/vcr/cassettes/get_current_iteration.json +1 -1
  44. data/test/vcr/cassettes/get_done_iterations.json +1 -1
  45. data/test/vcr/cassettes/get_epics.json +1 -1
  46. data/test/vcr/cassettes/get_iteration_by_number.json +1 -1
  47. data/test/vcr/cassettes/get_labels.json +1 -1
  48. data/test/vcr/cassettes/get_me.json +1 -1
  49. data/test/vcr/cassettes/get_my_activities.json +1 -1
  50. data/test/vcr/cassettes/get_owners_for_story.json +1 -1
  51. data/test/vcr/cassettes/get_project.json +1 -1
  52. data/test/vcr/cassettes/get_project_activity.json +1 -1
  53. data/test/vcr/cassettes/get_project_with_epics.json +1 -1
  54. data/test/vcr/cassettes/get_project_with_labels.json +1 -1
  55. data/test/vcr/cassettes/get_story.json +1 -1
  56. data/test/vcr/cassettes/get_story_activity.json +1 -1
  57. data/test/vcr/cassettes/get_story_comments.json +1 -1
  58. data/test/vcr/cassettes/get_story_owners.json +1 -1
  59. data/test/vcr/cassettes/get_story_with_tasks.json +1 -1
  60. data/test/vcr/cassettes/get_tasks.json +1 -1
  61. data/test/vcr/cassettes/get_tasks_for_story.json +1 -1
  62. data/test/vcr/cassettes/get_tasks_when_stories_filtered.json +1 -1
  63. data/test/vcr/cassettes/get_unscheduled_stories.json +1 -1
  64. data/test/vcr/cassettes/get_unscheduled_story.json +1 -1
  65. data/test/vcr/cassettes/save_story.json +1 -1
  66. data/test/vcr/cassettes/save_story_with_multiple_changes.json +1 -1
  67. data/test/vcr/cassettes/save_story_with_new_label.json +1 -1
  68. data/test/vcr/cassettes/update_story_to_create_activity.json +1 -1
  69. data/tracker_api.gemspec +2 -0
  70. metadata +23 -5
  71. data/lib/tracker_api/resources/shared/has_id.rb +0 -18
  72. data/test/vcr/cassettes/get_story_with_owners.json +0 -1
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: a4ea13ce0c74d3e644ebc7425d5100931b0e1e3a
4
- data.tar.gz: 4e5e624540678a7a39757ce74b102e3300dbce8d
3
+ metadata.gz: 3fbc8caee5a56cdf59e58fc545d0390f76685dc2
4
+ data.tar.gz: cae843ef9bdc193e763d440db310a79e9fb7fe4a
5
5
  SHA512:
6
- metadata.gz: 9356f13e4620b8a7c420b52a2beba5bbd29ef6a921882df0edc0e612d8b77886869a964a1a723fc6b4bc8d73d0eb9f74a0dafd0737f54b40dec8cc3d9a816abe
7
- data.tar.gz: 199de08b674ddd887ece1a3f8d30e8be802723185554f70fdacc9dc74c3031c1fd3963b4beaa2c44ad7f61fc685e8f2f6d33874c6b24b90b65baa8bfc211e1e8
6
+ metadata.gz: 81a23229a778910df741e89fea770bec3096cadeb85c40dc9f79b587fc8fa1e6617c4e853f7f0722d6eb6037162276d49a6f6132e0501a8dfd0fdd1aa6db2e58
7
+ data.tar.gz: 5be684a973dd96a30c032fd50179790505bab01ba8be8a61d0fb7b9a9bd30ec8a25bb697b8836764f116e7c0070f130d21d69f548e7e80c67ea171c19e4da36c
data/.gitignore CHANGED
@@ -17,3 +17,4 @@ spec/reports
17
17
  test/tmp
18
18
  test/version_tmp
19
19
  tmp
20
+ .byebug_history
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 << TrackerApi::Resources::Label.new(name: 'Endor') # Add a new label to an existing story
56
+ story.add_label('Endor') # Add a new label to an existing story
57
57
  story.save # Save changes made to a story
58
58
 
59
59
  story = TrackerApi::Resources::Story.new( client: client,
@@ -1,17 +1,18 @@
1
1
  require 'tracker_api/version'
2
2
 
3
3
  # dependencies
4
- require 'virtus'
5
4
  require 'faraday'
6
5
  require 'faraday_middleware'
6
+
7
7
  if defined?(ActiveSupport)
8
8
  require 'active_support/core_ext/object/blank'
9
9
  else
10
10
  require 'core_ext/object/blank'
11
11
  end
12
12
  require 'equalizer'
13
- require 'representable/json'
13
+
14
14
  require 'multi_json'
15
+ require 'representable/json'
15
16
 
16
17
  # stdlib
17
18
  require 'addressable/uri'
@@ -51,7 +52,8 @@ module TrackerApi
51
52
 
52
53
  module Resources
53
54
  module Shared
54
- autoload :HasId, 'tracker_api/resources/shared/has_id'
55
+ autoload :Base, 'tracker_api/resources/shared/base'
56
+ autoload :Collection, 'tracker_api/resources/shared/collection'
55
57
  end
56
58
  autoload :Activity, 'tracker_api/resources/activity'
57
59
  autoload :Account, 'tracker_api/resources/account'
@@ -138,9 +138,19 @@ module TrackerApi
138
138
  # Get information about a client story without knowing what project the story belongs to
139
139
  #
140
140
  # @param [String] story_id
141
+ # @param [Hash] params
141
142
  # @return [TrackerApi::Resources::Story]
142
- def story(story_id)
143
- Endpoints::Story.new(self).get_story(story_id)
143
+ def story(story_id, params={})
144
+ Endpoints::Story.new(self).get_story(story_id, params)
145
+ end
146
+
147
+ # Get information about an epic without knowing what project the epic belongs to
148
+ #
149
+ # @param [String] epic_id
150
+ # @param [Hash] params
151
+ # @return [TrackerApi::Resources::Epic]
152
+ def epic(epic_id, params={})
153
+ Endpoints::Epic.new(self).get_epic(epic_id, params)
144
154
  end
145
155
 
146
156
  # Get notifications for the authenticated person
@@ -13,6 +13,12 @@ module TrackerApi
13
13
  Resources::Epic.new({ client: client, project_id: project_id }.merge(data))
14
14
  end
15
15
 
16
+ def get_epic(epic_id, params={})
17
+ data = client.get("/epics/#{epic_id}", params: params).body
18
+
19
+ Resources::Epic.new({ client: client }.merge(data))
20
+ end
21
+
16
22
  def create(project_id, params={})
17
23
  data = client.post("/projects/#{project_id}/epics", params: params).body
18
24
 
@@ -25,6 +31,8 @@ module TrackerApi
25
31
  data = client.put("/projects/#{epic.project_id}/epics/#{epic.id}", params: params).body
26
32
 
27
33
  epic.attributes = data
34
+ epic.clean!
35
+ epic
28
36
  end
29
37
  end
30
38
  end
@@ -31,6 +31,8 @@ module TrackerApi
31
31
  data = client.put("/projects/#{story.project_id}/stories/#{story.id}", params: params).body
32
32
 
33
33
  story.attributes = data
34
+ story.clean!
35
+ story
34
36
  end
35
37
  end
36
38
  end
@@ -1,7 +1,7 @@
1
1
  module TrackerApi
2
2
  module Resources
3
3
  class Account
4
- include Shared::HasId
4
+ include Shared::Base
5
5
 
6
6
  attribute :created_at, DateTime
7
7
  attribute :status, String
@@ -11,8 +11,8 @@ module TrackerApi
11
11
  attribute :project_version, Integer
12
12
  attribute :message, String
13
13
  attribute :highlight, String
14
- attribute :changes, Array[Change]
15
- attribute :primary_resources, Array[PrimaryResource]
14
+ attribute :changes, Shared::Collection[Change]
15
+ attribute :primary_resources, Shared::Collection[PrimaryResource]
16
16
  attribute :project, Project
17
17
  attribute :performed_by, Person
18
18
  attribute :occurred_at, DateTime
@@ -1,7 +1,7 @@
1
1
  module TrackerApi
2
2
  module Resources
3
3
  class Change
4
- include Shared::HasId
4
+ include Shared::Base
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 Shared::HasId
4
+ include Shared::Base
5
5
 
6
6
  attribute :story_id, Integer
7
7
  attribute :epic_id, Integer
@@ -9,11 +9,11 @@ module TrackerApi
9
9
  attribute :person_id, Integer
10
10
  attribute :created_at, DateTime
11
11
  attribute :updated_at, DateTime
12
- attribute :file_attachment_ids, Array[Integer]
13
- attribute :google_attachment_ids, Array[Integer]
12
+ attribute :file_attachment_ids, Shared::Collection[Integer]
13
+ attribute :google_attachment_ids, Shared::Collection[Integer]
14
14
  attribute :commit_identifier, String
15
15
  attribute :commit_type, String
16
16
  attribute :kind, String
17
17
  end
18
18
  end
19
- end
19
+ end
@@ -1,16 +1,16 @@
1
1
  module TrackerApi
2
2
  module Resources
3
3
  class Epic
4
- include Shared::HasId
4
+ include Shared::Base
5
5
 
6
6
  attribute :client
7
7
 
8
- attribute :comment_ids, Array[Integer]
9
- attribute :comments, Array[Comment]
8
+ attribute :comment_ids, Shared::Collection[Integer]
9
+ attribute :comments, Shared::Collection[Comment]
10
10
  attribute :created_at, DateTime
11
11
  attribute :description, String
12
- attribute :follower_ids, Array[Integer]
13
- attribute :followers, Array[Person]
12
+ attribute :follower_ids, Shared::Collection[Integer]
13
+ attribute :followers, Shared::Collection[Person]
14
14
  attribute :kind, String
15
15
  attribute :label, Label
16
16
  attribute :label_id, Integer
@@ -13,8 +13,8 @@ module TrackerApi
13
13
  attribute :planned, Boolean
14
14
  attribute :project_id, Integer
15
15
  attribute :start, DateTime
16
- attribute :stories, [Story]
17
- attribute :story_ids, [Integer]
16
+ attribute :stories, Shared::Collection[Story]
17
+ attribute :story_ids, Shared::Collection[Integer]
18
18
  attribute :team_strength, Float
19
19
 
20
20
  def stories=(data)
@@ -1,7 +1,7 @@
1
1
  module TrackerApi
2
2
  module Resources
3
3
  class Label
4
- include Shared::HasId
4
+ include Shared::Base
5
5
 
6
6
  attribute :created_at, DateTime
7
7
  attribute :kind, String
@@ -1,7 +1,7 @@
1
1
  module TrackerApi
2
2
  module Resources
3
3
  class Me
4
- include Shared::HasId
4
+ include Shared::Base
5
5
 
6
6
  attribute :name, String
7
7
  attribute :initials, String
@@ -9,9 +9,9 @@ module TrackerApi
9
9
  attribute :time_zone, TimeZone
10
10
  attribute :api_token, String
11
11
  attribute :has_google_identity, Boolean
12
- attribute :project_ids, Array[Integer]
13
- attribute :projects, [MembershipSummary]
14
- attribute :workspace_ids, Array[Integer]
12
+ attribute :project_ids, Shared::Collection[Integer]
13
+ attribute :projects, Shared::Collection[MembershipSummary]
14
+ attribute :workspace_ids, Shared::Collection[Integer]
15
15
  attribute :email, String
16
16
  attribute :receives_in_app_notifications, Boolean
17
17
  attribute :kind, String
@@ -1,7 +1,7 @@
1
1
  module TrackerApi
2
2
  module Resources
3
3
  class MembershipSummary
4
- include Shared::HasId
4
+ include Shared::Base
5
5
 
6
6
  attribute :kind, String
7
7
  attribute :last_viewed_at, DateTime
@@ -1,7 +1,7 @@
1
1
  module TrackerApi
2
2
  module Resources
3
3
  class Notification
4
- include Shared::HasId
4
+ include Shared::Base
5
5
 
6
6
  attribute :client
7
7
 
@@ -1,7 +1,7 @@
1
1
  module TrackerApi
2
2
  module Resources
3
3
  class Person
4
- include Shared::HasId
4
+ include Shared::Base
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 Shared::HasId
4
+ include Shared::Base
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 Project
4
- include Shared::HasId
4
+ include Shared::Base
5
5
 
6
6
  attribute :client
7
7
 
@@ -17,14 +17,14 @@ module TrackerApi
17
17
  attribute :enable_incoming_emails, Boolean
18
18
  attribute :enable_planned_mode, Boolean
19
19
  attribute :enable_tasks, Boolean
20
- attribute :epic_ids, Array[Integer]
21
- attribute :epics, Array[Epic]
20
+ attribute :epic_ids, Shared::Collection[Integer]
21
+ attribute :epics, Shared::Collection[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
- attribute :label_ids, Array[Integer]
27
- attribute :labels, Array[Label]
26
+ attribute :label_ids, Shared::Collection[Integer]
27
+ attribute :labels, Shared::Collection[Label]
28
28
  attribute :name, String
29
29
  attribute :number_of_done_iterations_to_show, Integer
30
30
  attribute :point_scale, String
@@ -54,7 +54,7 @@ module TrackerApi
54
54
  # @param [Hash] params
55
55
  # @return [Array[Label]] labels of this project
56
56
  def labels(params = {})
57
- if @labels && @labels.any?
57
+ if @labels && @labels.present?
58
58
  @labels
59
59
  else
60
60
  @labels = Endpoints::Labels.new(client).get(id, params)
@@ -66,10 +66,11 @@ module TrackerApi
66
66
  # @param [Hash] params
67
67
  # @return [Array[Epic]] epics associated with this project
68
68
  def epics(params={})
69
- raise ArgumentError, 'Expected @epics to be an Array' unless @epics.is_a? Array
70
- return @epics unless @epics.empty?
71
-
72
- @epics = Endpoints::Epics.new(client).get(id, params)
69
+ if @epics && @epics.present?
70
+ @epics
71
+ else
72
+ @epics = Endpoints::Epics.new(client).get(id, params)
73
+ end
73
74
  end
74
75
 
75
76
  # Provides a list of all the iterations in the project.
@@ -1,7 +1,7 @@
1
1
  module TrackerApi
2
2
  module Resources
3
3
  class ProjectMembership
4
- include Shared::HasId
4
+ include Shared::Base
5
5
 
6
6
  attribute :person_id, Integer
7
7
  attribute :project_id, Integer
@@ -0,0 +1,30 @@
1
+ require 'virtus'
2
+ if Virtus::Attribute::NullifyBlank.method_defined?(:coerce)
3
+ require 'virtus/attribute/nullify_blank'
4
+ else
5
+ raise """
6
+ WARNING: The above monkey patch can't be applied as expected.
7
+ See discussion here: https://github.com/dashofcode/tracker_api/commit/27599e7e2169776c32bbff8c972a31b930452879
8
+ """
9
+ end
10
+ require 'virtus/dirty_attribute'
11
+
12
+ module TrackerApi
13
+ module Resources
14
+ module Shared
15
+ module Base
16
+ def self.included(base)
17
+ base.class_eval do
18
+ include Virtus.model(nullify_blank: true)
19
+ include Virtus::DirtyAttribute
20
+ include Virtus::DirtyAttribute::InitiallyClean
21
+
22
+ include Equalizer.new(:id)
23
+
24
+ attribute :id, Integer
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,17 @@
1
+ module TrackerApi
2
+ module Resources
3
+ module Shared
4
+ class Collection < Array
5
+ def <<(item)
6
+ warn """
7
+ WARNING: Direct mutation of an attribute value skips coercion
8
+ and dirty tracking. Please use direct assignment or the
9
+ specialized add_* methods to get expected behavior.
10
+ https://github.com/solnic/virtus#important-note-about-member-coercions
11
+ """
12
+ super
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
@@ -1,37 +1,37 @@
1
1
  module TrackerApi
2
2
  module Resources
3
3
  class Story
4
- include Shared::HasId
4
+ include Shared::Base
5
5
 
6
6
  attribute :client
7
7
 
8
8
  attribute :accepted_at, DateTime
9
- attribute :comment_ids, Array[Integer]
10
- attribute :comments, Array[Comment], :default => []
9
+ attribute :comment_ids, Shared::Collection[Integer]
10
+ attribute :comments, Shared::Collection[Comment]
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
14
14
  attribute :description, String
15
15
  attribute :estimate, Float
16
16
  attribute :external_id, String
17
- attribute :follower_ids, Array[Integer]
18
- attribute :followers, Array[Person]
17
+ attribute :follower_ids, Shared::Collection[Integer]
18
+ attribute :followers, Shared::Collection[Person]
19
19
  attribute :integration_id, Integer
20
20
  attribute :kind, String
21
- attribute :label_ids, Array[Integer]
22
- attribute :labels, Array[Label], :default => []
21
+ attribute :label_ids, Shared::Collection[Integer]
22
+ attribute :labels, Shared::Collection[Label]
23
23
  attribute :name, String
24
24
  attribute :owned_by_id, Integer # deprecated!
25
25
  attribute :owned_by, Person
26
- attribute :owner_ids, Array[Integer]
27
- attribute :owners, Array[Person], :default => []
26
+ attribute :owner_ids, Shared::Collection[Integer]
27
+ attribute :owners, Shared::Collection[Person]
28
28
  attribute :planned_iteration_number, Integer
29
29
  attribute :project_id, Integer
30
30
  attribute :requested_by, Person
31
31
  attribute :requested_by_id, Integer
32
32
  attribute :story_type, String # (feature, bug, chore, release)
33
- attribute :task_ids, Array[Integer]
34
- attribute :tasks, Array[Task], :default => []
33
+ attribute :task_ids, Shared::Collection[Integer]
34
+ attribute :tasks, Shared::Collection[Task]
35
35
  attribute :updated_at, DateTime
36
36
  attribute :url, String
37
37
 
@@ -39,7 +39,7 @@ module TrackerApi
39
39
  class UpdateRepresenter < Representable::Decorator
40
40
  include Representable::JSON
41
41
 
42
- property :follower_ids
42
+ property :follower_ids, if: ->(options) { !options[:input].blank? }
43
43
  property :name
44
44
  property :description
45
45
  property :story_type
@@ -48,7 +48,7 @@ module TrackerApi
48
48
  property :accepted_at
49
49
  property :deadline
50
50
  property :requested_by_id
51
- property :owner_ids
51
+ property :owner_ids, if: ->(options) { !options[:input].blank? }
52
52
  collection :labels, class: Label, decorator: Label::UpdateRepresenter, render_empty: true
53
53
  property :integration_id
54
54
  property :external_id
@@ -59,6 +59,20 @@ module TrackerApi
59
59
  @label_list ||= labels.collect(&:name).join(',')
60
60
  end
61
61
 
62
+ # Adds a new label to the story.
63
+ #
64
+ # @param [Label|Hash|String] label
65
+ def add_label(label)
66
+ new_label = if label.kind_of?(String)
67
+ Label.new(name: label)
68
+ else
69
+ label
70
+ end
71
+
72
+ # Use attribute writer to get coercion and dirty tracking.
73
+ self.labels = @labels.dup.push(new_label)
74
+ end
75
+
62
76
  # Provides a list of all the activity performed on the story.
63
77
  #
64
78
  # @param [Hash] params
@@ -72,7 +86,7 @@ module TrackerApi
72
86
  # @param [Hash] params
73
87
  # @return [Array[Comment]]
74
88
  def comments(params = {})
75
- if params.blank? && @comments.any?
89
+ if params.blank? && @comments.present?
76
90
  @comments
77
91
  else
78
92
  @comments = Endpoints::Comments.new(client).get(project_id, id, params)
@@ -84,7 +98,7 @@ module TrackerApi
84
98
  # @param [Hash] params
85
99
  # @return [Array[Task]]
86
100
  def tasks(params = {})
87
- if params.blank? && @tasks.any?
101
+ if params.blank? && @tasks.present?
88
102
  @tasks
89
103
  else
90
104
  @tasks = Endpoints::Tasks.new(client).get(project_id, id, params)
@@ -96,7 +110,7 @@ module TrackerApi
96
110
  # @param [Hash] params
97
111
  # @return [Array[Person]]
98
112
  def owners(params = {})
99
- if params.blank? && @owners.any?
113
+ if params.blank? && @owners.present?
100
114
  @owners
101
115
  else
102
116
  @owners = Endpoints::StoryOwners.new(client).get(project_id, id, params)
@@ -113,7 +127,7 @@ module TrackerApi
113
127
  def save
114
128
  raise ArgumentError, 'Can not update a story with an unknown project_id.' if project_id.nil?
115
129
 
116
- Endpoints::Story.new(client).update(self, UpdateRepresenter.new(self))
130
+ Endpoints::Story.new(client).update(self, UpdateRepresenter.new(Story.new(self.dirty_attributes)))
117
131
  end
118
132
  end
119
133
  end