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,6 @@
1
+ {
2
+ "id": 1,
3
+ "name": "SAMPLEPROJECT",
4
+ "canEdit": true,
5
+ "sprintSupportEnabled": true
6
+ }
@@ -0,0 +1,15 @@
1
+ [
2
+ {
3
+ "self": "http://www.example.com/jira/rest/api/2/resolution/1",
4
+ "description": "A fix for this issue is checked into the tree and tested.",
5
+ "iconUrl": "http://www.example.com/jira/images/icons/status_resolved.gif",
6
+ "name": "Fixed",
7
+ "id": "1"
8
+ },
9
+ {
10
+ "self": "http://www.example.com/jira/rest/api/2/resolution/3",
11
+ "description": "This is what it is supposed to do.",
12
+ "name": "Works as designed",
13
+ "id": "3"
14
+ }
15
+ ]
@@ -0,0 +1,7 @@
1
+ {
2
+ "self": "http://www.example.com/jira/rest/api/2/resolution/1",
3
+ "description": "A fix for this issue is checked into the tree and tested.",
4
+ "iconUrl": "http://www.example.com/jira/images/icons/status_resolved.gif",
5
+ "name": "Fixed",
6
+ "id": "1"
7
+ }
@@ -0,0 +1,125 @@
1
+ {
2
+ "expand": "schema,names",
3
+ "startAt": 0,
4
+ "maxResults": 50,
5
+ "total": 1,
6
+ "issues": [
7
+ {
8
+ "expand": "",
9
+ "id": "10001",
10
+ "self": "http://www.example.com/jira/rest/agile/1.0/board/92/issue/10001",
11
+ "key": "HSP-1",
12
+ "fields": {
13
+ "flagged": true,
14
+ "sprint": {
15
+ "id": 37,
16
+ "self": "http://www.example.com/jira/rest/agile/1.0/sprint/13",
17
+ "state": "future",
18
+ "name": "sprint 2"
19
+ },
20
+ "closedSprints": [
21
+ {
22
+ "id": 37,
23
+ "self": "http://www.example.com/jira/rest/agile/1.0/sprint/23",
24
+ "state": "closed",
25
+ "name": "sprint 1",
26
+ "startDate": "2015-04-11T15:22:00.000+10:00",
27
+ "endDate": "2015-04-20T01:22:00.000+10:00",
28
+ "completeDate": "2015-04-20T11:04:00.000+10:00"
29
+ }
30
+ ],
31
+ "description": "example bug report",
32
+ "project": {
33
+ "self": "http://www.example.com/jira/rest/api/2/project/EX",
34
+ "id": "10000",
35
+ "key": "EX",
36
+ "name": "Example",
37
+ "avatarUrls": {
38
+ "48x48": "http://www.example.com/jira/secure/projectavatar?size=large&pid=10000",
39
+ "24x24": "http://www.example.com/jira/secure/projectavatar?size=small&pid=10000",
40
+ "16x16": "http://www.example.com/jira/secure/projectavatar?size=xsmall&pid=10000",
41
+ "32x32": "http://www.example.com/jira/secure/projectavatar?size=medium&pid=10000"
42
+ },
43
+ "projectCategory": {
44
+ "self": "http://www.example.com/jira/rest/api/2/projectCategory/10000",
45
+ "id": "10000",
46
+ "name": "FIRST",
47
+ "description": "First Project Category"
48
+ }
49
+ },
50
+ "comment": [
51
+ {
52
+ "self": "http://www.example.com/jira/rest/api/2/issue/10010/comment/10000",
53
+ "id": "10000",
54
+ "author": {
55
+ "self": "http://www.example.com/jira/rest/api/2/user?username=fred",
56
+ "name": "fred",
57
+ "displayName": "Fred F. User",
58
+ "active": false
59
+ },
60
+ "body": "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque eget venenatis elit. Duis eu justo eget augue iaculis fermentum. Sed semper quam laoreet nisi egestas at posuere augue semper.",
61
+ "updateAuthor": {
62
+ "self": "http://www.example.com/jira/rest/api/2/user?username=fred",
63
+ "name": "fred",
64
+ "displayName": "Fred F. User",
65
+ "active": false
66
+ },
67
+ "created": "2016-06-22T11:49:57.797+0200",
68
+ "updated": "2016-06-22T11:49:57.800+0200",
69
+ "visibility": {
70
+ "type": "role",
71
+ "value": "Administrators"
72
+ }
73
+ }
74
+ ],
75
+ "epic": {
76
+ "id": 37,
77
+ "self": "http://www.example.com/jira/rest/agile/1.0/epic/23",
78
+ "name": "epic 1",
79
+ "summary": "epic 1 summary",
80
+ "color": {
81
+ "key": "color_4"
82
+ },
83
+ "done": true
84
+ },
85
+ "worklog": [
86
+ {
87
+ "self": "http://www.example.com/jira/rest/api/2/issue/10010/worklog/10000",
88
+ "author": {
89
+ "self": "http://www.example.com/jira/rest/api/2/user?username=fred",
90
+ "name": "fred",
91
+ "displayName": "Fred F. User",
92
+ "active": false
93
+ },
94
+ "updateAuthor": {
95
+ "self": "http://www.example.com/jira/rest/api/2/user?username=fred",
96
+ "name": "fred",
97
+ "displayName": "Fred F. User",
98
+ "active": false
99
+ },
100
+ "comment": "I did some work here.",
101
+ "updated": "2016-06-22T11:49:57.804+0200",
102
+ "visibility": {
103
+ "type": "group",
104
+ "value": "jira-developers"
105
+ },
106
+ "started": "2016-06-22T11:49:57.804+0200",
107
+ "timeSpent": "3h 20m",
108
+ "timeSpentSeconds": 12000,
109
+ "id": "100028",
110
+ "issueId": "10002"
111
+ }
112
+ ],
113
+ "updated": 1,
114
+ "timetracking": {
115
+ "originalEstimate": "10m",
116
+ "remainingEstimate": "3m",
117
+ "timeSpent": "6m",
118
+ "originalEstimateSeconds": 600,
119
+ "remainingEstimateSeconds": 200,
120
+ "timeSpentSeconds": 400
121
+ }
122
+ }
123
+ }
124
+ ]
125
+ }
@@ -0,0 +1,37 @@
1
+ [
2
+ {
3
+ "self": "http://localhost:2990/jira/rest/api/2/status/1",
4
+ "description": "The issue is open and ready for the assignee to start work on it.",
5
+ "iconUrl": "http://localhost:2990/jira/images/icons/status_open.gif",
6
+ "name": "Open",
7
+ "id": "1"
8
+ },
9
+ {
10
+ "self": "http://localhost:2990/jira/rest/api/2/status/3",
11
+ "description": "This issue is being actively worked on at the moment by the assignee.",
12
+ "iconUrl": "http://localhost:2990/jira/images/icons/status_inprogress.gif",
13
+ "name": "In Progress",
14
+ "id": "3"
15
+ },
16
+ {
17
+ "self": "http://localhost:2990/jira/rest/api/2/status/4",
18
+ "description": "This issue was once resolved, but the resolution was deemed incorrect. From here issues are either marked assigned or resolved.",
19
+ "iconUrl": "http://localhost:2990/jira/images/icons/status_reopened.gif",
20
+ "name": "Reopened",
21
+ "id": "4"
22
+ },
23
+ {
24
+ "self": "http://localhost:2990/jira/rest/api/2/status/5",
25
+ "description": "A resolution has been taken, and it is awaiting verification by reporter. From here issues are either reopened, or are closed.",
26
+ "iconUrl": "http://localhost:2990/jira/images/icons/status_resolved.gif",
27
+ "name": "Resolved",
28
+ "id": "5"
29
+ },
30
+ {
31
+ "self": "http://localhost:2990/jira/rest/api/2/status/6",
32
+ "description": "The issue is considered finished, the resolution is correct. Issues which are closed can be reopened.",
33
+ "iconUrl": "http://localhost:2990/jira/images/icons/status_closed.gif",
34
+ "name": "Closed",
35
+ "id": "6"
36
+ }
37
+ ]
@@ -0,0 +1,7 @@
1
+ {
2
+ "self": "http://localhost:2990/jira/rest/api/2/status/1",
3
+ "description": "The issue is open and ready for the assignee to start work on it.",
4
+ "iconUrl": "http://localhost:2990/jira/images/icons/status_open.gif",
5
+ "name": "Open",
6
+ "id": "1"
7
+ }
@@ -0,0 +1,17 @@
1
+ {
2
+ "self": "http://localhost:2990/jira/rest/api/2/user?username=admin",
3
+ "name": "admin",
4
+ "emailAddress": "admin@example.com",
5
+ "avatarUrls": {
6
+ "16x16": "http://localhost:2990/jira/secure/useravatar?size=small&avatarId=10122",
7
+ "48x48": "http://localhost:2990/jira/secure/useravatar?avatarId=10122"
8
+ },
9
+ "displayName": "admin",
10
+ "active": true,
11
+ "timeZone": "Pacific/Auckland",
12
+ "groups": {
13
+ "size": 3,
14
+ "items": []
15
+ },
16
+ "expand": "groups"
17
+ }
@@ -0,0 +1,7 @@
1
+ {
2
+ "self": "http://localhost:2990/jira/rest/api/2/version/10001",
3
+ "id": "10001",
4
+ "name": "2.0",
5
+ "archived": false,
6
+ "released": false
7
+ }
@@ -0,0 +1,5 @@
1
+ {
2
+ "errorMessages": [
3
+ "Unrecognized field \"chump\" (Class com.atlassian.jira.rest.v2.issue.version.VersionBean), not marked as ignorable\n at [Source: org.apache.catalina.connector.CoyoteInputStream@4264a42e; line: 1, column: 2]"
4
+ ]
5
+ }
@@ -0,0 +1,11 @@
1
+ {
2
+ "self": "http://localhost:2990/jira/rest/api/2/version/10000",
3
+ "id": "10000",
4
+ "description": "Initial version",
5
+ "name": "1.0",
6
+ "overdue": false,
7
+ "userReleaseDate": "12/Jan/12",
8
+ "archived": false,
9
+ "releaseDate": "2012-01-12",
10
+ "released": false
11
+ }
@@ -0,0 +1,7 @@
1
+ {
2
+ "self": "http://localhost:2990/jira/rest/api/2/version/10000",
3
+ "id": "10000",
4
+ "name": "2.0.0",
5
+ "archived": false,
6
+ "released": false
7
+ }
@@ -0,0 +1,11 @@
1
+ [{"name":"from API",
2
+ "url":"http://localhost:3000/webhooks/1",
3
+ "excludeBody":false,
4
+ "filters":{"issue-related-events-section":""},
5
+ "events":[],
6
+ "enabled":true,
7
+ "self":"http://localhost:2990/jira/rest/webhooks/1.0/webhook/2",
8
+ "lastUpdatedUser":"admin",
9
+ "lastUpdatedDisplayName":"admin",
10
+ "lastUpdated":1453306520188}
11
+ ]
@@ -0,0 +1,11 @@
1
+ {"name":"from API",
2
+ "url":"http://localhost:3000/webhooks/1",
3
+ "excludeBody":false,
4
+ "filters":{"issue-related-events-section":""},
5
+ "events":[],
6
+ "enabled":true,
7
+ "self":"http://localhost:2990/jira/rest/webhooks/1.0/webhook/2",
8
+ "lastUpdatedUser":"admin",
9
+ "lastUpdatedDisplayName":"admin",
10
+ "lastUpdated":1453306520188
11
+ }
@@ -0,0 +1,21 @@
1
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
2
+ require 'rubygems'
3
+ require 'bundler/setup'
4
+ require 'webmock/rspec'
5
+ require 'pry'
6
+
7
+ Dir['./spec/support/**/*.rb'].each { |f| require f }
8
+
9
+ require 'jira-ruby'
10
+
11
+ RSpec.configure do |config|
12
+ config.extend ClientsHelper
13
+ end
14
+
15
+ def get_mock_response(file, value_if_file_not_found = false)
16
+ file.sub!('?', '_') # we have to replace this character on Windows machine
17
+ File.read(File.join(File.dirname(__FILE__), 'mock_responses/', file))
18
+ rescue Errno::ENOENT => e
19
+ raise e if value_if_file_not_found == false
20
+ value_if_file_not_found
21
+ end
@@ -0,0 +1,16 @@
1
+ module ClientsHelper
2
+ def with_each_client
3
+ clients = {}
4
+
5
+ oauth_client = JIRA::Client.new(consumer_key: 'foo', consumer_secret: 'bar')
6
+ oauth_client.set_access_token('abc', '123')
7
+ clients['http://localhost:2990'] = oauth_client
8
+
9
+ basic_client = JIRA::Client.new(username: 'foo', password: 'bar', auth_type: :basic, use_ssl: false)
10
+ clients['http://foo:bar@localhost:2990'] = basic_client
11
+
12
+ clients.each do |site_url, client|
13
+ yield site_url, client
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,11 @@
1
+ RSpec::Matchers.define :have_attributes do |expected|
2
+ match do |actual|
3
+ expected.each do |key, value|
4
+ expect(actual.attrs[key]).to eq(value)
5
+ end
6
+ end
7
+
8
+ failure_message do |actual|
9
+ "expected #{actual.attrs} to match #{expected}"
10
+ end
11
+ end
@@ -0,0 +1,9 @@
1
+ RSpec::Matchers.define :have_many do |collection, klass|
2
+ match do |actual|
3
+ expect(actual.send(collection).class).to eq(JIRA::HasManyProxy)
4
+ expect(actual.send(collection).length).to be > 0
5
+ actual.send(collection).each do |member|
6
+ expect(member.class).to eq(klass)
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,5 @@
1
+ RSpec::Matchers.define :have_one do |resource, klass|
2
+ match do |actual|
3
+ expect(actual.send(resource).class).to eq(klass)
4
+ end
5
+ end
@@ -0,0 +1,177 @@
1
+ require 'cgi'
2
+
3
+ def get_mock_from_path(method, options = {})
4
+ prefix = if defined? belongs_to
5
+ belongs_to.path_component + '/'
6
+ else
7
+ ''
8
+ end
9
+
10
+ url = if options[:url]
11
+ options[:url]
12
+ elsif options[:key]
13
+ described_class.singular_path(client, options[:key], prefix)
14
+ else
15
+ described_class.collection_path(client, prefix)
16
+ end
17
+ file_path = url.sub(client.options[:rest_base_path], '')
18
+ file_path = file_path + '.' + options[:suffix] if options[:suffix]
19
+ file_path = file_path + '.' + method.to_s unless method == :get
20
+ value_if_not_found = options.key?(:value_if_not_found) ? options[:value_if_not_found] : false
21
+ get_mock_response("#{file_path}.json", value_if_not_found)
22
+ end
23
+
24
+ def class_basename
25
+ described_class.name.split('::').last
26
+ end
27
+
28
+ def options
29
+ options = {}
30
+ options[belongs_to.to_sym] = belongs_to if defined? belongs_to
31
+ options
32
+ end
33
+
34
+ def prefix
35
+ prefix = '/'
36
+ prefix = belongs_to.path_component + '/' if defined? belongs_to
37
+ prefix
38
+ end
39
+
40
+ def build_receiver
41
+ if defined?(belongs_to)
42
+ belongs_to.send(described_class.endpoint_name.pluralize.to_sym)
43
+ else
44
+ client.send(class_basename)
45
+ end
46
+ end
47
+
48
+ shared_examples 'a resource' do
49
+ it 'gracefully handles non-json responses' do
50
+ if defined? target
51
+ subject = target
52
+ else
53
+ subject = client.send(class_basename).build(described_class.key_attribute.to_s => '99999')
54
+ end
55
+ stub_request(:put, site_url + subject.url)
56
+ .to_return(status: 405, body: '<html><body>Some HTML</body></html>')
57
+ expect(subject.save('foo' => 'bar')).to be_falsey
58
+ expect(lambda do
59
+ expect(subject.save!('foo' => 'bar')).to be_falsey
60
+ end).to raise_error(JIRA::HTTPError)
61
+ end
62
+ end
63
+
64
+ shared_examples 'a resource with a collection GET endpoint' do
65
+ it 'should get the collection' do
66
+ stub_request(:get, site_url + described_class.collection_path(client))
67
+ .to_return(status: 200, body: get_mock_from_path(:get))
68
+ collection = build_receiver.all
69
+
70
+ expect(collection.length).to eq(expected_collection_length)
71
+ expect(collection.first).to have_attributes(expected_attributes)
72
+ end
73
+ end
74
+
75
+ shared_examples 'a resource with JQL inputs and a collection GET endpoint' do
76
+ it 'should get the collection' do
77
+ stub_request(
78
+ :get,
79
+ site_url +
80
+ client.options[:rest_base_path] +
81
+ '/search?jql=' +
82
+ CGI.escape(jql_query_string)
83
+ ).to_return(status: 200, body: get_mock_response('issue.json'))
84
+
85
+ collection = build_receiver.jql(jql_query_string)
86
+
87
+ expect(collection.length).to eq(expected_collection_length)
88
+ expect(collection.first).to have_attributes(expected_attributes)
89
+ end
90
+ end
91
+
92
+ shared_examples 'a resource with a singular GET endpoint' do
93
+ it 'GETs a single resource' do
94
+ # E.g., for JIRA::Resource::Project, we need to call
95
+ # client.Project.find()
96
+ stub_request(:get, site_url + described_class.singular_path(client, key, prefix))
97
+ .to_return(status: 200, body: get_mock_from_path(:get, key: key))
98
+ subject = client.send(class_basename).find(key, options)
99
+
100
+ expect(subject).to have_attributes(expected_attributes)
101
+ end
102
+
103
+ it 'builds and fetches a single resource' do
104
+ # E.g., for JIRA::Resource::Project, we need to call
105
+ # client.Project.build('key' => 'ABC123')
106
+ stub_request(:get, site_url + described_class.singular_path(client, key, prefix))
107
+ .to_return(status: 200, body: get_mock_from_path(:get, key: key))
108
+
109
+ subject = build_receiver.build(described_class.key_attribute.to_s => key)
110
+ subject.fetch
111
+
112
+ expect(subject).to have_attributes(expected_attributes)
113
+ end
114
+
115
+ it 'handles a 404' do
116
+ stub_request(:get, site_url + described_class.singular_path(client, '99999', prefix))
117
+ .to_return(status: 404, body: '{"errorMessages":["' + class_basename + ' Does Not Exist"],"errors": {}}')
118
+ expect(lambda do
119
+ client.send(class_basename).find('99999', options)
120
+ end).to raise_exception(JIRA::HTTPError)
121
+ end
122
+ end
123
+
124
+ shared_examples 'a resource with a DELETE endpoint' do
125
+ it 'deletes a resource' do
126
+ # E.g., for JIRA::Resource::Project, we need to call
127
+ # client.Project.delete()
128
+ stub_request(:delete, site_url + described_class.singular_path(client, key, prefix))
129
+ .to_return(status: 204, body: nil)
130
+
131
+ subject = build_receiver.build(described_class.key_attribute.to_s => key)
132
+ expect(subject.delete).to be_truthy
133
+ end
134
+ end
135
+
136
+ shared_examples 'a resource with a POST endpoint' do
137
+ it 'saves a new resource' do
138
+ stub_request(:post, site_url + described_class.collection_path(client, prefix))
139
+ .to_return(status: 201, body: get_mock_from_path(:post))
140
+ subject = build_receiver.build
141
+ expect(subject.save(attributes_for_post)).to be_truthy
142
+ expected_attributes_from_post.each do |method_name, value|
143
+ expect(subject.send(method_name)).to eq(value)
144
+ end
145
+ end
146
+ end
147
+
148
+ shared_examples 'a resource with a PUT endpoint' do
149
+ it 'saves an existing component' do
150
+ stub_request(:get, site_url + described_class.singular_path(client, key, prefix))
151
+ .to_return(status: 200, body: get_mock_from_path(:get, key: key))
152
+ stub_request(:put, site_url + described_class.singular_path(client, key, prefix))
153
+ .to_return(status: 200, body: get_mock_from_path(:put, key: key, value_if_not_found: nil))
154
+ subject = build_receiver.build(described_class.key_attribute.to_s => key)
155
+ subject.fetch
156
+ expect(subject.save(attributes_for_put)).to be_truthy
157
+ expected_attributes_from_put.each do |method_name, value|
158
+ expect(subject.send(method_name)).to eq(value)
159
+ end
160
+ end
161
+ end
162
+
163
+ shared_examples 'a resource with a PUT endpoint that rejects invalid fields' do
164
+ it 'fails to save with an invalid field' do
165
+ stub_request(:get, site_url + described_class.singular_path(client, key))
166
+ .to_return(status: 200, body: get_mock_from_path(:get, key: key))
167
+ stub_request(:put, site_url + described_class.singular_path(client, key))
168
+ .to_return(status: 400, body: get_mock_from_path(:put, key: key, suffix: 'invalid'))
169
+ subject = client.send(class_basename).build(described_class.key_attribute.to_s => key)
170
+ subject.fetch
171
+
172
+ expect(subject.save('fields' => { 'invalid' => 'field' })).to be_falsey
173
+ expect(lambda do
174
+ subject.save!('fields' => { 'invalid' => 'field' })
175
+ end).to raise_error(JIRA::HTTPError)
176
+ end
177
+ end