jira-ruby 2.1.3

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 (154) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +13 -0
  3. data/.travis.yml +9 -0
  4. data/Gemfile +14 -0
  5. data/Guardfile +14 -0
  6. data/LICENSE +19 -0
  7. data/README.md +427 -0
  8. data/Rakefile +31 -0
  9. data/example.rb +224 -0
  10. data/http-basic-example.rb +113 -0
  11. data/jira-ruby.gemspec +35 -0
  12. data/lib/jira-ruby.rb +49 -0
  13. data/lib/jira/base.rb +525 -0
  14. data/lib/jira/base_factory.rb +46 -0
  15. data/lib/jira/client.rb +308 -0
  16. data/lib/jira/has_many_proxy.rb +42 -0
  17. data/lib/jira/http_client.rb +112 -0
  18. data/lib/jira/http_error.rb +14 -0
  19. data/lib/jira/jwt_client.rb +67 -0
  20. data/lib/jira/oauth_client.rb +114 -0
  21. data/lib/jira/railtie.rb +10 -0
  22. data/lib/jira/request_client.rb +31 -0
  23. data/lib/jira/resource/agile.rb +79 -0
  24. data/lib/jira/resource/applinks.rb +39 -0
  25. data/lib/jira/resource/attachment.rb +50 -0
  26. data/lib/jira/resource/board.rb +91 -0
  27. data/lib/jira/resource/board_configuration.rb +9 -0
  28. data/lib/jira/resource/comment.rb +12 -0
  29. data/lib/jira/resource/component.rb +8 -0
  30. data/lib/jira/resource/createmeta.rb +44 -0
  31. data/lib/jira/resource/field.rb +83 -0
  32. data/lib/jira/resource/filter.rb +15 -0
  33. data/lib/jira/resource/issue.rb +141 -0
  34. data/lib/jira/resource/issuelink.rb +20 -0
  35. data/lib/jira/resource/issuelinktype.rb +14 -0
  36. data/lib/jira/resource/issuetype.rb +8 -0
  37. data/lib/jira/resource/priority.rb +8 -0
  38. data/lib/jira/resource/project.rb +41 -0
  39. data/lib/jira/resource/rapidview.rb +67 -0
  40. data/lib/jira/resource/remotelink.rb +26 -0
  41. data/lib/jira/resource/resolution.rb +8 -0
  42. data/lib/jira/resource/serverinfo.rb +18 -0
  43. data/lib/jira/resource/sprint.rb +105 -0
  44. data/lib/jira/resource/sprint_report.rb +8 -0
  45. data/lib/jira/resource/status.rb +8 -0
  46. data/lib/jira/resource/transition.rb +29 -0
  47. data/lib/jira/resource/user.rb +30 -0
  48. data/lib/jira/resource/version.rb +8 -0
  49. data/lib/jira/resource/watcher.rb +35 -0
  50. data/lib/jira/resource/webhook.rb +37 -0
  51. data/lib/jira/resource/worklog.rb +14 -0
  52. data/lib/jira/tasks.rb +0 -0
  53. data/lib/jira/version.rb +3 -0
  54. data/lib/tasks/generate.rake +18 -0
  55. data/spec/integration/attachment_spec.rb +32 -0
  56. data/spec/integration/comment_spec.rb +52 -0
  57. data/spec/integration/component_spec.rb +39 -0
  58. data/spec/integration/field_spec.rb +32 -0
  59. data/spec/integration/issue_spec.rb +93 -0
  60. data/spec/integration/issuelinktype_spec.rb +26 -0
  61. data/spec/integration/issuetype_spec.rb +24 -0
  62. data/spec/integration/priority_spec.rb +24 -0
  63. data/spec/integration/project_spec.rb +49 -0
  64. data/spec/integration/rapidview_spec.rb +74 -0
  65. data/spec/integration/resolution_spec.rb +26 -0
  66. data/spec/integration/status_spec.rb +24 -0
  67. data/spec/integration/transition_spec.rb +49 -0
  68. data/spec/integration/user_spec.rb +41 -0
  69. data/spec/integration/version_spec.rb +39 -0
  70. data/spec/integration/watcher_spec.rb +62 -0
  71. data/spec/integration/webhook.rb +25 -0
  72. data/spec/integration/worklog_spec.rb +51 -0
  73. data/spec/jira/base_factory_spec.rb +45 -0
  74. data/spec/jira/base_spec.rb +598 -0
  75. data/spec/jira/client_spec.rb +291 -0
  76. data/spec/jira/has_many_proxy_spec.rb +46 -0
  77. data/spec/jira/http_client_spec.rb +328 -0
  78. data/spec/jira/http_error_spec.rb +24 -0
  79. data/spec/jira/jwt_uri_builder_spec.rb +59 -0
  80. data/spec/jira/oauth_client_spec.rb +162 -0
  81. data/spec/jira/request_client_spec.rb +41 -0
  82. data/spec/jira/resource/agile_spec.rb +135 -0
  83. data/spec/jira/resource/attachment_spec.rb +138 -0
  84. data/spec/jira/resource/board_spec.rb +224 -0
  85. data/spec/jira/resource/createmeta_spec.rb +258 -0
  86. data/spec/jira/resource/field_spec.rb +85 -0
  87. data/spec/jira/resource/filter_spec.rb +97 -0
  88. data/spec/jira/resource/issue_spec.rb +227 -0
  89. data/spec/jira/resource/issuelink_spec.rb +14 -0
  90. data/spec/jira/resource/project_factory_spec.rb +11 -0
  91. data/spec/jira/resource/project_spec.rb +123 -0
  92. data/spec/jira/resource/sprint_spec.rb +90 -0
  93. data/spec/jira/resource/user_factory_spec.rb +31 -0
  94. data/spec/jira/resource/worklog_spec.rb +22 -0
  95. data/spec/mock_responses/board/1.json +33 -0
  96. data/spec/mock_responses/board/1_issues.json +62 -0
  97. data/spec/mock_responses/component.post.json +28 -0
  98. data/spec/mock_responses/component/10000.invalid.put.json +5 -0
  99. data/spec/mock_responses/component/10000.json +39 -0
  100. data/spec/mock_responses/component/10000.put.json +39 -0
  101. data/spec/mock_responses/empty_issues.json +8 -0
  102. data/spec/mock_responses/field.json +32 -0
  103. data/spec/mock_responses/field/1.json +15 -0
  104. data/spec/mock_responses/issue.json +1108 -0
  105. data/spec/mock_responses/issue.post.json +5 -0
  106. data/spec/mock_responses/issue/10002.invalid.put.json +6 -0
  107. data/spec/mock_responses/issue/10002.json +126 -0
  108. data/spec/mock_responses/issue/10002.put.missing_field_update.json +6 -0
  109. data/spec/mock_responses/issue/10002/attachments/10000.json +20 -0
  110. data/spec/mock_responses/issue/10002/comment.json +65 -0
  111. data/spec/mock_responses/issue/10002/comment.post.json +29 -0
  112. data/spec/mock_responses/issue/10002/comment/10000.json +29 -0
  113. data/spec/mock_responses/issue/10002/comment/10000.put.json +29 -0
  114. data/spec/mock_responses/issue/10002/transitions.json +49 -0
  115. data/spec/mock_responses/issue/10002/transitions.post.json +1 -0
  116. data/spec/mock_responses/issue/10002/watchers.json +13 -0
  117. data/spec/mock_responses/issue/10002/worklog.json +98 -0
  118. data/spec/mock_responses/issue/10002/worklog.post.json +30 -0
  119. data/spec/mock_responses/issue/10002/worklog/10000.json +31 -0
  120. data/spec/mock_responses/issue/10002/worklog/10000.put.json +30 -0
  121. data/spec/mock_responses/issueLinkType.json +25 -0
  122. data/spec/mock_responses/issueLinkType/10000.json +7 -0
  123. data/spec/mock_responses/issuetype.json +42 -0
  124. data/spec/mock_responses/issuetype/5.json +8 -0
  125. data/spec/mock_responses/jira/rest/webhooks/1.0/webhook.json +11 -0
  126. data/spec/mock_responses/jira/rest/webhooks/1.0/webhook/2.json +11 -0
  127. data/spec/mock_responses/priority.json +42 -0
  128. data/spec/mock_responses/priority/1.json +8 -0
  129. data/spec/mock_responses/project.json +12 -0
  130. data/spec/mock_responses/project/SAMPLEPROJECT.issues.json +1108 -0
  131. data/spec/mock_responses/project/SAMPLEPROJECT.json +84 -0
  132. data/spec/mock_responses/rapidview.json +10 -0
  133. data/spec/mock_responses/rapidview/SAMPLEPROJECT.issues.full.json +276 -0
  134. data/spec/mock_responses/rapidview/SAMPLEPROJECT.issues.json +111 -0
  135. data/spec/mock_responses/rapidview/SAMPLEPROJECT.json +6 -0
  136. data/spec/mock_responses/resolution.json +15 -0
  137. data/spec/mock_responses/resolution/1.json +7 -0
  138. data/spec/mock_responses/sprint/1_issues.json +125 -0
  139. data/spec/mock_responses/status.json +37 -0
  140. data/spec/mock_responses/status/1.json +7 -0
  141. data/spec/mock_responses/user_username=admin.json +17 -0
  142. data/spec/mock_responses/version.post.json +7 -0
  143. data/spec/mock_responses/version/10000.invalid.put.json +5 -0
  144. data/spec/mock_responses/version/10000.json +11 -0
  145. data/spec/mock_responses/version/10000.put.json +7 -0
  146. data/spec/mock_responses/webhook.json +11 -0
  147. data/spec/mock_responses/webhook/webhook.json +11 -0
  148. data/spec/spec_helper.rb +21 -0
  149. data/spec/support/clients_helper.rb +16 -0
  150. data/spec/support/matchers/have_attributes.rb +11 -0
  151. data/spec/support/matchers/have_many.rb +9 -0
  152. data/spec/support/matchers/have_one.rb +5 -0
  153. data/spec/support/shared_examples/integration.rb +177 -0
  154. metadata +491 -0
@@ -0,0 +1,26 @@
1
+ module JIRA
2
+ module Resource
3
+ class RemotelinkFactory < JIRA::BaseFactory # :nodoc:
4
+ end
5
+
6
+ class Remotelink < JIRA::Base
7
+ belongs_to :issue
8
+
9
+ def self.endpoint_name
10
+ 'remotelink'
11
+ end
12
+
13
+ def self.all(client, options = {})
14
+ issue = options[:issue]
15
+ raise ArgumentError, 'parent issue is required' unless issue
16
+
17
+ path = "#{issue.self}/#{endpoint_name}"
18
+ response = client.get(path)
19
+ json = parse_json(response.body)
20
+ json.map do |link|
21
+ issue.remotelink.build(link)
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,8 @@
1
+ module JIRA
2
+ module Resource
3
+ class ResolutionFactory < JIRA::BaseFactory # :nodoc:
4
+ end
5
+
6
+ class Resolution < JIRA::Base; end
7
+ end
8
+ end
@@ -0,0 +1,18 @@
1
+ module JIRA
2
+ module Resource
3
+ class ServerInfoFactory < JIRA::BaseFactory # :nodoc:
4
+ end
5
+
6
+ class ServerInfo < JIRA::Base
7
+ def self.endpoint_name
8
+ 'serverInfo'
9
+ end
10
+
11
+ def self.all(client, options = {})
12
+ response = client.get(collection_path(client))
13
+ json = parse_json(response.body)
14
+ new(client, { attrs: json }.merge(options))
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,105 @@
1
+ module JIRA
2
+ module Resource
3
+ class SprintFactory < JIRA::BaseFactory # :nodoc:
4
+ end
5
+
6
+ class Sprint < JIRA::Base
7
+ def self.find(client, key)
8
+ response = client.get(agile_path(client, key))
9
+ json = parse_json(response.body)
10
+ client.Sprint.build(json)
11
+ end
12
+
13
+ # get all issues of sprint
14
+ def issues(options = {})
15
+ jql = 'sprint = ' + id.to_s
16
+ jql += " and updated >= '#{options[:updated]}'" if options[:updated]
17
+ Issue.jql(client, jql)
18
+ end
19
+
20
+ def add_issue(issue)
21
+ request_body = { issues: [issue.id] }.to_json
22
+ response = client.post("#{agile_path}/issue", request_body)
23
+ true
24
+ end
25
+
26
+ def sprint_report
27
+ get_sprint_details_attribute('sprint_report')
28
+ end
29
+
30
+ def start_date
31
+ get_sprint_details_attribute('start_date')
32
+ end
33
+
34
+ def end_date
35
+ get_sprint_details_attribute('end_date')
36
+ end
37
+
38
+ def complete_date
39
+ get_sprint_details_attribute('complete_date')
40
+ end
41
+
42
+ def get_sprint_details_attribute(attribute_name)
43
+ attribute = instance_variable_get("@#{attribute_name}")
44
+ return attribute if attribute
45
+ get_sprint_details
46
+ instance_variable_get("@#{attribute_name}")
47
+ end
48
+
49
+ def get_sprint_details
50
+ search_url =
51
+ "#{client.options[:site]}#{client.options[:client_path]}/rest/greenhopper/1.0/rapid/charts/sprintreport?rapidViewId=#{rapidview_id}&sprintId=#{id}"
52
+ begin
53
+ response = client.get(search_url)
54
+ rescue StandardError
55
+ return nil
56
+ end
57
+ json = self.class.parse_json(response.body)
58
+
59
+ @start_date = Date.parse(json['sprint']['startDate']) unless json['sprint']['startDate'] == 'None'
60
+ @end_date = Date.parse(json['sprint']['endDate']) unless json['sprint']['endDate'] == 'None'
61
+ @completed_date = Date.parse(json['sprint']['completeDate']) unless json['sprint']['completeDate'] == 'None'
62
+ @sprint_report = client.SprintReport.build(json['contents'])
63
+ end
64
+
65
+ def rapidview_id
66
+ return @attrs['rapidview_id'] if @attrs['rapidview_id']
67
+ search_url = client.options[:site] + '/secure/GHGoToBoard.jspa?sprintId=' + id.to_s
68
+ begin
69
+ response = client.get(search_url)
70
+ rescue JIRA::HTTPError => error
71
+ return unless error.response.instance_of? Net::HTTPFound
72
+ rapid_view_match = /rapidView=(\d+)&/.match(error.response['location'])
73
+ @attrs['rapidview_id'] = rapid_view_match[1] unless rapid_view_match.nil?
74
+ end
75
+ end
76
+
77
+ def save(attrs = {}, _path = nil)
78
+ attrs = @attrs if attrs.empty?
79
+ super(attrs, agile_path)
80
+ end
81
+
82
+ def save!(attrs = {}, _path = nil)
83
+ attrs = @attrs if attrs.empty?
84
+ super(attrs, agile_path)
85
+ end
86
+
87
+ # WORK IN PROGRESS
88
+ def complete
89
+ complete_url = "#{client.options[:site]}/rest/greenhopper/1.0/sprint/#{id}/complete"
90
+ response = client.put(complete_url)
91
+ self.class.parse_json(response.body)
92
+ end
93
+
94
+ private
95
+
96
+ def agile_path
97
+ self.class.agile_path(client, id)
98
+ end
99
+
100
+ def self.agile_path(client, key)
101
+ "#{client.options[:context_path]}/rest/agile/1.0/sprint/#{key}"
102
+ end
103
+ end
104
+ end
105
+ end
@@ -0,0 +1,8 @@
1
+ module JIRA
2
+ module Resource
3
+ class SprintReportFactory < JIRA::BaseFactory # :nodoc:
4
+ end
5
+
6
+ class SprintReport < JIRA::Base; end
7
+ end
8
+ end
@@ -0,0 +1,8 @@
1
+ module JIRA
2
+ module Resource
3
+ class StatusFactory < JIRA::BaseFactory # :nodoc:
4
+ end
5
+
6
+ class Status < JIRA::Base; end
7
+ end
8
+ end
@@ -0,0 +1,29 @@
1
+ module JIRA
2
+ module Resource
3
+ class TransitionFactory < JIRA::BaseFactory # :nodoc:
4
+ end
5
+
6
+ class Transition < JIRA::Base
7
+ has_one :to, class: JIRA::Resource::Status
8
+ belongs_to :issue
9
+
10
+ nested_collections true
11
+
12
+ def self.endpoint_name
13
+ 'transitions'
14
+ end
15
+
16
+ def self.all(client, options = {})
17
+ issue = options[:issue]
18
+ raise ArgumentError, 'parent issue is required' unless issue
19
+
20
+ path = "#{issue.self}/#{endpoint_name}?expand=transitions.fields"
21
+ response = client.get(path)
22
+ json = parse_json(response.body)
23
+ json['transitions'].map do |transition|
24
+ issue.transitions.build(transition)
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,30 @@
1
+ module JIRA
2
+ module Resource
3
+ class UserFactory < JIRA::BaseFactory # :nodoc:
4
+ def myself
5
+ instance = build
6
+ response = client.get("#{client.options[:rest_base_path]}/myself")
7
+ instance.set_attrs_from_response(response)
8
+ instance
9
+ end
10
+ end
11
+
12
+ class User < JIRA::Base
13
+ MAX_RESULTS = 1000
14
+
15
+ def self.singular_path(client, key, prefix = '/')
16
+ collection_path(client, prefix) + '?username=' + key
17
+ end
18
+
19
+ # Cannot retrieve more than 1,000 users through the api, please see: https://jira.atlassian.com/browse/JRASERVER-65089
20
+ def self.all(client)
21
+ response = client.get("/rest/api/2/user/search?username=_&maxResults=#{MAX_RESULTS}")
22
+ all_users = JSON.parse(response.body)
23
+
24
+ all_users.flatten.uniq.map do |user|
25
+ client.User.build(user)
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,8 @@
1
+ module JIRA
2
+ module Resource
3
+ class VersionFactory < JIRA::BaseFactory # :nodoc:
4
+ end
5
+
6
+ class Version < JIRA::Base; end
7
+ end
8
+ end
@@ -0,0 +1,35 @@
1
+ module JIRA
2
+ module Resource
3
+ class WatcherFactory < JIRA::BaseFactory # :nodoc:
4
+ end
5
+
6
+ class Watcher < JIRA::Base
7
+ belongs_to :issue
8
+
9
+ nested_collections true
10
+
11
+ def self.endpoint_name
12
+ 'watchers'
13
+ end
14
+
15
+ def self.all(client, options = {})
16
+ issue = options[:issue]
17
+ raise ArgumentError, 'parent issue is required' unless issue
18
+
19
+ path = "#{issue.self}/#{endpoint_name}"
20
+ response = client.get(path)
21
+ json = parse_json(response.body)
22
+ json['watchers'].map do |watcher|
23
+ issue.watchers.build(watcher)
24
+ end
25
+ end
26
+
27
+ def save!(user_id, path = nil)
28
+ path ||= new_record? ? url : patched_url
29
+ response = client.post(path, user_id.to_json)
30
+ true
31
+ end
32
+
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,37 @@
1
+ module JIRA
2
+ module Resource
3
+ class WebhookFactory < JIRA::BaseFactory # :nodoc:
4
+ end
5
+
6
+ class Webhook < JIRA::Base
7
+ REST_BASE_PATH = '/rest/webhooks/1.0'.freeze
8
+
9
+ def self.endpoint_name
10
+ 'webhook'
11
+ end
12
+
13
+ def self.full_url(client)
14
+ client.options[:context_path] + REST_BASE_PATH
15
+ end
16
+
17
+ def self.collection_path(client, prefix = '/')
18
+ full_url(client) + prefix + endpoint_name
19
+ end
20
+
21
+ def self.all(client, options = {})
22
+ response = client.get(collection_path(client))
23
+ json = parse_json(response.body)
24
+ json.map do |attrs|
25
+ new(client, { attrs: attrs }.merge(options))
26
+ end
27
+ end
28
+
29
+ # def self.save(options={})
30
+ # end
31
+
32
+ # def self.delete(options={})
33
+
34
+ # end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,14 @@
1
+ module JIRA
2
+ module Resource
3
+ class WorklogFactory < JIRA::BaseFactory # :nodoc:
4
+ end
5
+
6
+ class Worklog < JIRA::Base
7
+ has_one :author, class: JIRA::Resource::User
8
+ has_one :update_author, class: JIRA::Resource::User,
9
+ attribute_key: 'updateAuthor'
10
+ belongs_to :issue
11
+ nested_collections true
12
+ end
13
+ end
14
+ end
File without changes
@@ -0,0 +1,3 @@
1
+ module JIRA
2
+ VERSION = '2.1.3'.freeze
3
+ end
@@ -0,0 +1,18 @@
1
+ require 'securerandom'
2
+
3
+ namespace :jira do
4
+ desc 'Generate a consumer key for your application'
5
+ task :generate_consumer_key do
6
+ key = SecureRandom.hex(16)
7
+ puts "You can use this as your consumer key: #{key}"
8
+ end
9
+
10
+ desc 'Run the system call to generate a RSA public certificate'
11
+ task :generate_public_cert do
12
+ puts "Executing 'openssl req -x509 -nodes -newkey rsa:1024 -sha1 -keyout rsakey.pem -out rsacert.pem'"
13
+ system('openssl req -x509 -subj "/C=US/ST=New York/L=New York/O=SUMO Heavy Industries/CN=www.sumoheavy.com" -nodes -newkey rsa:1024 -sha1 -keyout rsakey.pem -out rsacert.pem')
14
+ puts "Done. The RSA-SHA1 private keyfile is in the current directory: \'rsakey.pem\'."
15
+ puts 'You will need to copy the following certificate into your application link configuration in Jira:'
16
+ system('cat rsacert.pem')
17
+ end
18
+ end
@@ -0,0 +1,32 @@
1
+ require 'spec_helper'
2
+
3
+ describe JIRA::Resource::Attachment do
4
+ with_each_client do |site_url, client|
5
+ let(:client) { client }
6
+ let(:site_url) { site_url }
7
+
8
+ let(:key) { '10000' }
9
+
10
+ let(:target) { JIRA::Resource::Attachment.new(client, attrs: { 'id' => '99999' }, issue_id: '10002') }
11
+
12
+ let(:expected_attributes) do
13
+ {
14
+ 'self' => 'http://localhost:2990/jira/rest/api/2/attachment/10000',
15
+ 'size' => 15_360,
16
+ 'filename' => 'ballmer.png'
17
+ }
18
+ end
19
+
20
+ let(:belongs_to) do
21
+ JIRA::Resource::Issue.new(client, attrs: {
22
+ 'id' => '10002',
23
+ 'fields' => {
24
+ 'attachment' => { 'attachments' => [] }
25
+ }
26
+ })
27
+ end
28
+
29
+ it_should_behave_like 'a resource with a singular GET endpoint'
30
+ it_should_behave_like 'a resource with a DELETE endpoint'
31
+ end
32
+ end
@@ -0,0 +1,52 @@
1
+ require 'spec_helper'
2
+
3
+ describe JIRA::Resource::Comment do
4
+ with_each_client do |site_url, client|
5
+ let(:client) { client }
6
+ let(:site_url) { site_url }
7
+
8
+ let(:key) { '10000' }
9
+
10
+ let(:target) { JIRA::Resource::Comment.new(client, attrs: { 'id' => '99999' }, issue_id: '54321') }
11
+
12
+ let(:expected_collection_length) { 2 }
13
+
14
+ let(:belongs_to) do
15
+ JIRA::Resource::Issue.new(client, attrs: {
16
+ 'id' => '10002',
17
+ 'fields' => {
18
+ 'comment' => { 'comments' => [] }
19
+ }
20
+ })
21
+ end
22
+
23
+ let(:expected_attributes) do
24
+ {
25
+ 'self' => 'http://localhost:2990/jira/rest/api/2/issue/10002/comment/10000',
26
+ 'id' => key,
27
+ 'body' => 'This is a comment. Creative.'
28
+ }
29
+ end
30
+
31
+ let(:attributes_for_post) do
32
+ { 'body' => 'new comment' }
33
+ end
34
+ let(:expected_attributes_from_post) do
35
+ { 'id' => '10001', 'body' => 'new comment' }
36
+ end
37
+
38
+ let(:attributes_for_put) do
39
+ { 'body' => 'new body' }
40
+ end
41
+ let(:expected_attributes_from_put) do
42
+ { 'id' => '10000', 'body' => 'new body' }
43
+ end
44
+
45
+ it_should_behave_like 'a resource'
46
+ it_should_behave_like 'a resource with a collection GET endpoint'
47
+ it_should_behave_like 'a resource with a singular GET endpoint'
48
+ it_should_behave_like 'a resource with a DELETE endpoint'
49
+ it_should_behave_like 'a resource with a POST endpoint'
50
+ it_should_behave_like 'a resource with a PUT endpoint'
51
+ end
52
+ end