jira-ruby 2.1.3

Sign up to get free protection for your applications and to get access to all the features.
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