mediawiki_api 0.3.0 → 0.3.1

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 20db69d02e685dd6c01f43a617e783310b884784
4
- data.tar.gz: 8ea86db0e30f32006981ba4e04779f0e8eb1b73c
3
+ metadata.gz: b2e13c62db930923fbfd515cb2029d9d43508b9a
4
+ data.tar.gz: dada9ee27c4c09a1a2c7a157828e16079beace62
5
5
  SHA512:
6
- metadata.gz: c5c9196a44d8975fb8081273791487d88188ed4f452c892d656d2de940fc9e1049b61dd64ec14d2a71e7c0c6935109bfb006378eb697bd189f70536d10ed80b9
7
- data.tar.gz: cd1f86124b668e951ec1a608be92ec7c6aee66273eb3c360da85b546ffd460d87427d16a4dcac23c5e0c476c5f77c207cdce673af3053bb4b9a9a3ce03bc21c9
6
+ metadata.gz: 145fc6da63febab8ffc18b4d32a6eda108232052fd927380357a7fdfc375eb482681c1ac339e3299f4fee62c3f05fac2e03d766cd401a891991af74a5eb35111
7
+ data.tar.gz: eba8ace9d4a58f8148c7e5601d6e216475fc0bce8612bd78bea5fab637a63ed46ffcd4b042cb9bad82d0ace755685d025c0b1d89ccf84567c9fc698aeccbf23e
data/.rubocop.yml CHANGED
@@ -1 +1,33 @@
1
- inherit_from: .rubocop_todo.yml
1
+ Metrics/AbcSize:
2
+ Enabled: false
3
+
4
+ Metrics/ClassLength:
5
+ Enabled: false
6
+
7
+ Metrics/MethodLength:
8
+ Enabled: false
9
+
10
+ Metrics/ParameterLists:
11
+ Enabled: false
12
+
13
+ Metrics/LineLength:
14
+ Max: 100
15
+
16
+ Metrics/CyclomaticComplexity:
17
+ Enabled: false
18
+
19
+ Metrics/PerceivedComplexity:
20
+ Enabled: false
21
+
22
+ Metrics/MethodLength:
23
+ Enabled: false
24
+
25
+ Style/Alias:
26
+ Enabled: false
27
+
28
+ Style/DotPosition:
29
+ Enabled: true
30
+ EnforcedStyle: trailing
31
+
32
+ Style/SignalException:
33
+ Enabled: false
data/CREDITS CHANGED
@@ -6,6 +6,7 @@ For further details on licensing, see the LICENSE file.
6
6
 
7
7
  == Developers ==
8
8
  * Amir Aharoni
9
+ * Asaf Bartov
9
10
  * Chris McMahon
10
11
  * Dan Duvall
11
12
  * Jeff Hall
data/Gemfile CHANGED
@@ -1,7 +1,7 @@
1
- #ruby=ruby-2.1.2
2
- #ruby-gemset=mediawiki_api
1
+ # ruby=ruby-2.1.2
2
+ # ruby-gemset=mediawiki_api
3
3
 
4
- source "https://rubygems.org"
4
+ source 'https://rubygems.org'
5
5
 
6
6
  # Specify your gem's dependencies in mediawiki_api.gemspec
7
7
  gemspec
data/README.md CHANGED
@@ -54,6 +54,9 @@ See https://www.mediawiki.org/wiki/Gerrit
54
54
 
55
55
  ## Release notes
56
56
 
57
+ ### 0.3.1 2015-01-06
58
+ - actions now automatically refresh token and re-submit action if first attempt returns 'badtoken'.
59
+
57
60
  ### 0.3.0 2014-10-14
58
61
 
59
62
  - HTTP 400 and 500 responses now result in an HttpError exception.
data/lib/mediawiki_api.rb CHANGED
@@ -1 +1 @@
1
- require "mediawiki_api/client"
1
+ require 'mediawiki_api/client'
@@ -1,17 +1,18 @@
1
- require "faraday"
2
- require "faraday-cookie_jar"
3
- require "json"
1
+ require 'faraday'
2
+ require 'faraday-cookie_jar'
3
+ require 'json'
4
4
 
5
- require "mediawiki_api/exceptions"
6
- require "mediawiki_api/response"
5
+ require 'mediawiki_api/exceptions'
6
+ require 'mediawiki_api/response'
7
7
 
8
8
  module MediawikiApi
9
+ # high level client for MediaWiki
9
10
  class Client
10
- FORMAT = "json"
11
+ FORMAT = 'json'
11
12
 
12
13
  attr_accessor :logged_in
13
14
 
14
- alias logged_in? logged_in
15
+ alias_method :logged_in?, :logged_in
15
16
 
16
17
  def initialize(url, log = false)
17
18
  @conn = Faraday.new(url: url) do |faraday|
@@ -26,24 +27,14 @@ module MediawikiApi
26
27
  end
27
28
 
28
29
  def action(name, params = {})
29
- name = name.to_s
30
-
31
- method = params.delete(:http_method) || :post
32
- token_type = params.delete(:token_type)
33
- envelope = (params.delete(:envelope) || [name]).map(&:to_s)
34
-
35
- params[:token] = get_token(token_type || name) unless token_type == false
36
- params = compile_parameters(params)
37
-
38
- response = @conn.send(method, "", params.merge(action: name, format: FORMAT))
39
-
40
- raise HttpError, response.status if response.status >= 400
41
-
42
- if response.headers.include?("mediawiki-api-error")
43
- raise ApiError, Response.new(response, ["error"])
30
+ raw_action(name, params)
31
+ rescue ApiError => e
32
+ if e.code == 'badtoken'
33
+ @tokens.clear # ensure fresh token on re-try
34
+ raw_action(name, params) # no rescue this time; only re-try once.
35
+ else
36
+ raise # otherwise, propagate the exception
44
37
  end
45
-
46
- Response.new(response, envelope)
47
38
  end
48
39
 
49
40
  def create_account(username, password, token = nil)
@@ -52,14 +43,14 @@ module MediawikiApi
52
43
 
53
44
  data = action(:createaccount, params).data
54
45
 
55
- case data["result"]
56
- when "Success"
46
+ case data['result']
47
+ when 'Success'
57
48
  @logged_in = true
58
49
  @tokens.clear
59
- when "NeedToken"
60
- data = create_account(username, password, data["token"])
50
+ when 'NeedToken'
51
+ data = create_account(username, password, data['token'])
61
52
  else
62
- raise CreateAccountError, data["result"]
53
+ raise CreateAccountError, data['result']
63
54
  end
64
55
 
65
56
  data
@@ -75,12 +66,12 @@ module MediawikiApi
75
66
 
76
67
  def edit(params = {})
77
68
  response = action(:edit, params)
78
- raise EditError, response if response.data["result"] == "Failure"
69
+ raise EditError, response if response.data['result'] == 'Failure'
79
70
  response
80
71
  end
81
72
 
82
73
  def get_wikitext(title)
83
- @conn.get "/w/index.php", { action: "raw", title: title }
74
+ @conn.get '/w/index.php', action: 'raw', title: title
84
75
  end
85
76
 
86
77
  def list(type, params = {})
@@ -93,14 +84,14 @@ module MediawikiApi
93
84
 
94
85
  data = action(:login, params).data
95
86
 
96
- case data["result"]
97
- when "Success"
87
+ case data['result']
88
+ when 'Success'
98
89
  @logged_in = true
99
90
  @tokens.clear
100
- when "NeedToken"
101
- data = log_in(username, password, data["token"])
91
+ when 'NeedToken'
92
+ data = log_in(username, password, data['token'])
102
93
  else
103
- raise LoginError, data["result"]
94
+ raise LoginError, data['result']
104
95
  end
105
96
 
106
97
  data
@@ -114,7 +105,7 @@ module MediawikiApi
114
105
  subquery(:prop, type, params)
115
106
  end
116
107
 
117
- def protect_page(title, reason, protections = "edit=sysop|move=sysop")
108
+ def protect_page(title, reason, protections = 'edit=sysop|move=sysop')
118
109
  action(:protect, title: title, reason: reason, protections: protections)
119
110
  end
120
111
 
@@ -127,8 +118,10 @@ module MediawikiApi
127
118
  end
128
119
 
129
120
  def upload_image(filename, path, comment, ignorewarnings)
130
- file = Faraday::UploadIO.new(path, "image/png")
131
- action(:upload, token_type: "edit", filename: filename, file: file, comment: comment, ignorewarnings: ignorewarnings)
121
+ file = Faraday::UploadIO.new(path, 'image/png')
122
+ action(:upload,
123
+ token_type: 'edit', filename: filename, file: file, comment: comment,
124
+ ignorewarnings: ignorewarnings)
132
125
  end
133
126
 
134
127
  def watch_page(title)
@@ -143,7 +136,7 @@ module MediawikiApi
143
136
  when false
144
137
  # omit it entirely
145
138
  when Array
146
- params[name] = value.join("|")
139
+ params[name] = value.join('|')
147
140
  else
148
141
  params[name] = value
149
142
  end
@@ -153,9 +146,10 @@ module MediawikiApi
153
146
  def get_token(type)
154
147
  unless @tokens.include?(type)
155
148
  response = action(:tokens, type: type, http_method: :get, token_type: false)
149
+ parameter_warning = /Unrecognized value for parameter 'type'/
156
150
 
157
- if response.warnings? && response.warnings.grep(/Unrecognized value for parameter 'type'/).any?
158
- raise TokenError, response.warnings.join(", ")
151
+ if response.warnings? && response.warnings.grep(parameter_warning).any?
152
+ raise TokenError, response.warnings.join(', ')
159
153
  end
160
154
 
161
155
  @tokens[type] = response.data["#{type}token"]
@@ -164,8 +158,34 @@ module MediawikiApi
164
158
  @tokens[type]
165
159
  end
166
160
 
161
+ def send_request(method, params, envelope)
162
+ response = @conn.send(method, '', params)
163
+
164
+ raise HttpError, response.status if response.status >= 400
165
+
166
+ if response.headers.include?('mediawiki-api-error')
167
+ raise ApiError, Response.new(response, ['error'])
168
+ end
169
+
170
+ Response.new(response, envelope)
171
+ end
172
+
167
173
  def subquery(type, subtype, params = {})
168
- query(params.merge(type.to_sym => subtype, :envelope => ["query", subtype]))
174
+ query(params.merge(type.to_sym => subtype, :envelope => ['query', subtype]))
175
+ end
176
+
177
+ def raw_action(name, params = {})
178
+ name = name.to_s
179
+ params = params.clone
180
+
181
+ method = params.delete(:http_method) || :post
182
+ token_type = params.delete(:token_type)
183
+ envelope = (params.delete(:envelope) || [name]).map(&:to_s)
184
+
185
+ params[:token] = get_token(token_type || name) unless token_type == false
186
+ params = compile_parameters(params)
187
+
188
+ send_request(method, params.merge(action: name, format: FORMAT), envelope)
169
189
  end
170
190
  end
171
191
  end
@@ -1,4 +1,5 @@
1
1
  module MediawikiApi
2
+ # generic MediaWiki api errors
2
3
  class ApiError < StandardError
3
4
  attr_reader :response
4
5
 
@@ -7,11 +8,11 @@ module MediawikiApi
7
8
  end
8
9
 
9
10
  def code
10
- data["code"]
11
+ data['code']
11
12
  end
12
13
 
13
14
  def info
14
- data["info"]
15
+ data['info']
15
16
  end
16
17
 
17
18
  def to_s
@@ -28,6 +29,7 @@ module MediawikiApi
28
29
  class CreateAccountError < StandardError
29
30
  end
30
31
 
32
+ # for errors from HTTP requests
31
33
  class HttpError < StandardError
32
34
  attr_reader :status
33
35
 
@@ -40,9 +42,10 @@ module MediawikiApi
40
42
  end
41
43
  end
42
44
 
45
+ # for edit failures
43
46
  class EditError < ApiError
44
47
  def to_s
45
- "check the response data for details"
48
+ 'check the response data for details'
46
49
  end
47
50
  end
48
51
 
@@ -1,5 +1,5 @@
1
- require "forwardable"
2
- require "json"
1
+ require 'forwardable'
2
+ require 'json'
3
3
 
4
4
  module MediawikiApi
5
5
  # Provides access to a parsed MediaWiki API responses.
@@ -51,16 +51,20 @@ module MediawikiApi
51
51
  end
52
52
  end
53
53
 
54
+ # Set of error messages from the response.
55
+ #
56
+ # @return [Array]
57
+ #
58
+ def errors
59
+ flatten_resp('errors')
60
+ end
61
+
54
62
  # Set of warning messages from the response.
55
63
  #
56
64
  # @return [Array]
57
65
  #
58
66
  def warnings
59
- if response_object["warnings"]
60
- response_object["warnings"].values.map(&:values).flatten
61
- else
62
- []
63
- end
67
+ flatten_resp('warnings')
64
68
  end
65
69
 
66
70
  # Whether the response contains warnings.
@@ -73,6 +77,14 @@ module MediawikiApi
73
77
 
74
78
  private
75
79
 
80
+ def flatten_resp(str)
81
+ if response_object[str]
82
+ response_object[str].values.map(&:values).flatten
83
+ else
84
+ []
85
+ end
86
+ end
87
+
76
88
  def open_envelope(obj, env = @envelope)
77
89
  if !obj.is_a?(Hash) || env.nil? || env.empty? || !obj.include?(env.first)
78
90
  obj
@@ -1,3 +1,4 @@
1
+ # MediaWiki Ruby API
1
2
  module MediawikiApi
2
- VERSION = "0.3.0"
3
+ VERSION = '0.3.1'
3
4
  end
@@ -1,29 +1,37 @@
1
1
  # coding: utf-8
2
- lib = File.expand_path("../lib", __FILE__)
2
+ lib = File.expand_path('../lib', __FILE__)
3
3
  $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
- require "mediawiki_api/version"
4
+ require 'mediawiki_api/version'
5
5
 
6
6
  Gem::Specification.new do |spec|
7
- spec.name = "mediawiki_api"
7
+ spec.name = 'mediawiki_api'
8
8
  spec.version = MediawikiApi::VERSION
9
- spec.authors = ["Amir Aharoni", "Chris McMahon", "Dan Duvall", "Jeff Hall", "Juliusz Gonera", "Zeljko Filipin"]
10
- spec.email = ["amir.aharoni@mail.huji.ac.il", "cmcmahon@wikimedia.org", "dduvall@wikimedia.org", "jhall@wikimedia.org", "jgonera@wikimedia.org", "zeljko.filipin@gmail.com"]
11
- spec.summary = %q{A library for interacting with MediaWiki API from Ruby.}
12
- spec.description = %q{Uses adapter-agnostic Faraday gem to talk to MediaWiki API.}
13
- spec.homepage = "https://github.com/wikimedia/mediawiki-ruby-api"
14
- spec.license = "GPL-2"
9
+ spec.authors = [
10
+ 'Amir Aharoni', 'Asaf Bartov', 'Chris McMahon', 'Dan Duvall', 'Jeff Hall', 'Juliusz Gonera',
11
+ 'Zeljko Filipin'
12
+ ]
13
+ spec.email = [
14
+ 'amir.aharoni@mail.huji.ac.il', 'asaf.bartov@gmail.com', 'cmcmahon@wikimedia.org',
15
+ 'dduvall@wikimedia.org', 'jhall@wikimedia.org', 'jgonera@wikimedia.org',
16
+ 'zeljko.filipin@gmail.com'
17
+ ]
18
+ spec.summary = 'A library for interacting with MediaWiki API from Ruby.'
19
+ spec.description = 'Uses adapter-agnostic Faraday gem to talk to MediaWiki API.'
20
+ spec.homepage = 'https://github.com/wikimedia/mediawiki-ruby-api'
21
+ spec.license = 'GPL-2'
15
22
 
16
- spec.files = `git ls-files`.split($/)
17
- spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
18
- spec.require_paths = ["lib"]
23
+ spec.files = `git ls-files`.split($INPUT_RECORD_SEPARATOR)
24
+ spec.test_files = spec.files.grep(/^(test|spec|features)/)
25
+ spec.require_paths = ['lib']
19
26
 
20
- spec.add_runtime_dependency "faraday", "~> 0.9", ">= 0.9.0"
21
- spec.add_runtime_dependency "faraday-cookie_jar", "~> 0.0", ">= 0.0.6"
27
+ spec.add_runtime_dependency 'faraday', '~> 0.9', '>= 0.9.0'
28
+ spec.add_runtime_dependency 'faraday-cookie_jar', '~> 0.0', '>= 0.0.6'
22
29
 
23
- spec.add_development_dependency "bundler", "~> 1.3"
24
- spec.add_development_dependency "rake", "~> 0"
25
- spec.add_development_dependency "rspec", "~> 3.0", ">= 3.0.0"
26
- spec.add_development_dependency "webmock", "~> 1.17", ">= 1.17.2"
27
- spec.add_development_dependency "redcarpet"
28
- spec.add_development_dependency "yard"
30
+ spec.add_development_dependency 'bundler', '~> 1.3'
31
+ spec.add_development_dependency 'rake', '~> 0'
32
+ spec.add_development_dependency 'rspec', '~> 3.0', '>= 3.0.0'
33
+ spec.add_development_dependency 'rubocop', '~> 0.26.1'
34
+ spec.add_development_dependency 'webmock', '~> 1.17', '>= 1.17.2'
35
+ spec.add_development_dependency 'redcarpet'
36
+ spec.add_development_dependency 'yard'
29
37
  end
data/spec/client_spec.rb CHANGED
@@ -1,6 +1,6 @@
1
- require "spec_helper"
2
- require "webmock/rspec"
3
- require "support/request_helpers"
1
+ require 'spec_helper'
2
+ require 'webmock/rspec'
3
+ require 'support/request_helpers'
4
4
 
5
5
  describe MediawikiApi::Client do
6
6
  include MediawikiApi::RequestHelpers
@@ -9,19 +9,22 @@ describe MediawikiApi::Client do
9
9
 
10
10
  subject { client }
11
11
 
12
- body_base = { cookieprefix: "prefix", sessionid: "123" }
12
+ body_base = { cookieprefix: 'prefix', sessionid: '123' }
13
13
 
14
- describe "#action" do
14
+ describe '#action' do
15
15
  subject { client.action(action, params) }
16
16
 
17
- let(:action) { "something" }
17
+ let(:action) { 'something' }
18
18
  let(:token_type) { action }
19
19
  let(:params) { {} }
20
20
 
21
- let(:response) { { status: response_status, headers: response_headers, body: response_body.to_json } }
21
+ let(:response) do
22
+ { status: response_status, headers: response_headers, body: response_body.to_json }
23
+ end
24
+
22
25
  let(:response_status) { 200 }
23
26
  let(:response_headers) { nil }
24
- let(:response_body) { { "something" => {} } }
27
+ let(:response_body) { { 'something' => {} } }
25
28
 
26
29
  let(:token_warning) { nil }
27
30
 
@@ -32,13 +35,13 @@ describe MediawikiApi::Client do
32
35
 
33
36
  it { is_expected.to be_a(MediawikiApi::Response) }
34
37
 
35
- it "makes requests for both the right token and API action" do
38
+ it 'makes requests for both the right token and API action' do
36
39
  subject
37
40
  expect(@token_request).to have_been_made
38
41
  expect(@request).to have_been_made
39
42
  end
40
43
 
41
- context "without a required token" do
44
+ context 'without a required token' do
42
45
  let(:params) { { token_type: false } }
43
46
 
44
47
  before do
@@ -46,33 +49,33 @@ describe MediawikiApi::Client do
46
49
  @request_without_token = stub_api_request(:post, action: action).to_return(response)
47
50
  end
48
51
 
49
- it "does not request a token" do
52
+ it 'does not request a token' do
50
53
  subject
51
54
  expect(@token_request).to_not have_been_made
52
55
  end
53
56
 
54
- it "makes the action request without a token" do
57
+ it 'makes the action request without a token' do
55
58
  subject
56
59
  expect(@request_without_token).to have_been_made
57
60
  expect(@request_with_token).to_not have_been_made
58
61
  end
59
62
  end
60
63
 
61
- context "given parameters" do
62
- let(:params) { { foo: "value" } }
64
+ context 'given parameters' do
65
+ let(:params) { { foo: 'value' } }
63
66
 
64
67
  before do
65
- @request_with_parameters = stub_action_request(action, foo: "value").to_return(response)
68
+ @request_with_parameters = stub_action_request(action, foo: 'value').to_return(response)
66
69
  end
67
70
 
68
- it "includes them" do
71
+ it 'includes them' do
69
72
  subject
70
73
  expect(@request_with_parameters).to have_been_made
71
74
  end
72
75
  end
73
76
 
74
- context "parameter compilation" do
75
- context "negated parameters" do
77
+ context 'parameter compilation' do
78
+ context 'negated parameters' do
76
79
  let(:params) { { foo: false } }
77
80
 
78
81
  before do
@@ -80,136 +83,160 @@ describe MediawikiApi::Client do
80
83
  @request_without_parameter = stub_action_request(action).to_return(response)
81
84
  end
82
85
 
83
- it "omits the parameter" do
86
+ it 'omits the parameter' do
84
87
  subject
85
88
  expect(@request_with_parameter).to_not have_been_made
86
89
  expect(@request_without_parameter).to have_been_made
87
90
  end
88
91
  end
89
92
 
90
- context "array parameters" do
91
- let(:params) { { foo: ["one", "two"] } }
93
+ context 'array parameters' do
94
+ let(:params) { { foo: %w(one two) } }
92
95
 
93
96
  before do
94
- @request = stub_action_request(action, foo: "one|two").to_return(response)
97
+ @request = stub_action_request(action, foo: 'one|two').to_return(response)
95
98
  end
96
99
 
97
- it "pipe delimits values" do
100
+ it 'pipe delimits values' do
98
101
  subject
99
102
  expect(@request).to have_been_made
100
103
  end
101
104
  end
102
105
  end
103
106
 
104
- context "when the response status is in the 400 range" do
107
+ context 'when the response status is in the 400 range' do
105
108
  let(:response_status) { 403 }
106
109
 
107
- it "raises an HttpError" do
108
- expect { subject }.to raise_error(MediawikiApi::HttpError, "unexpected HTTP response (403)")
110
+ it 'raises an HttpError' do
111
+ expect { subject }.to raise_error(MediawikiApi::HttpError,
112
+ 'unexpected HTTP response (403)')
109
113
  end
110
114
  end
111
115
 
112
- context "when the response status is in the 500 range" do
116
+ context 'when the response status is in the 500 range' do
113
117
  let(:response_status) { 502 }
114
118
 
115
- it "raises an HttpError" do
116
- expect { subject }.to raise_error(MediawikiApi::HttpError, "unexpected HTTP response (502)")
119
+ it 'raises an HttpError' do
120
+ expect { subject }.to raise_error(MediawikiApi::HttpError,
121
+ 'unexpected HTTP response (502)')
117
122
  end
118
123
  end
119
124
 
120
- context "when the response is an error" do
121
- let(:response_headers) { { "MediaWiki-API-Error" => "code" } }
122
- let(:response_body) { { error: { info: "detailed message", code: "code" } } }
125
+ context 'when the response is an error' do
126
+ let(:response_headers) { { 'MediaWiki-API-Error' => 'code' } }
127
+ let(:response_body) { { error: { info: 'detailed message', code: 'code' } } }
123
128
 
124
- it "raises an ApiError" do
125
- expect { subject }.to raise_error(MediawikiApi::ApiError, "detailed message (code)")
129
+ it 'raises an ApiError' do
130
+ expect { subject }.to raise_error(MediawikiApi::ApiError, 'detailed message (code)')
126
131
  end
127
132
  end
128
133
 
129
- context "given a bad token type" do
134
+ context 'given a bad token type' do
130
135
  let(:params) { { token_type: token_type } }
131
- let(:token_type) { "badtoken" }
136
+ let(:token_type) { 'badtoken' }
132
137
  let(:token_warning) { "Unrecognized value for parameter 'type': badtoken" }
133
138
 
134
- it "raises a TokenError" do
139
+ it 'raises a TokenError' do
135
140
  expect { subject }.to raise_error(MediawikiApi::TokenError, token_warning)
136
141
  end
137
142
  end
138
143
 
139
- context "when the token response includes only other types of warnings (see bug 70066)" do
140
- let(:token_warning) { "action=tokens has been deprecated. Please use action=query&meta=tokens instead." }
144
+ context 'when the token response includes only other types of warnings (see bug 70066)' do
145
+ let(:token_warning) do
146
+ 'action=tokens has been deprecated. Please use action=query&meta=tokens instead.'
147
+ end
148
+
149
+ it 'raises no exception' do
150
+ expect { subject }.to_not raise_error
151
+ end
152
+ end
153
+
154
+ context 'when the token is invalid' do
155
+ let(:response_headers) { { 'MediaWiki-API-Error' => 'badtoken' } }
156
+ let(:response_body) { { error: { code: 'badtoken', info: 'Invalid token' } } }
141
157
 
142
- it "raises no exception" do
158
+ before do
159
+ # Stub a second request without the error
160
+ @request.then.to_return(status: 200)
161
+ end
162
+
163
+ it 'rescues the initial exception' do
143
164
  expect { subject }.to_not raise_error
144
165
  end
166
+
167
+ it 'automatically retries the request' do
168
+ subject
169
+ expect(@token_request).to have_been_made.twice
170
+ expect(@request).to have_been_made.twice
171
+ end
145
172
  end
146
173
  end
147
174
 
148
- describe "#log_in" do
175
+ describe '#log_in' do
149
176
 
150
- it "logs in when API returns Success" do
177
+ it 'logs in when API returns Success' do
151
178
  stub_request(:post, api_url).
152
- with(body: { format: "json", action: "login", lgname: "Test", lgpassword: "qwe123" }).
153
- to_return(body: { login: body_base.merge({ result: "Success" }) }.to_json )
179
+ with(body: { format: 'json', action: 'login', lgname: 'Test', lgpassword: 'qwe123' }).
180
+ to_return(body: { login: body_base.merge(result: 'Success') }.to_json)
154
181
 
155
- subject.log_in "Test", "qwe123"
182
+ subject.log_in 'Test', 'qwe123'
156
183
  expect(subject.logged_in).to be true
157
184
  end
158
185
 
159
- context "when API returns NeedToken" do
186
+ context 'when API returns NeedToken' do
160
187
  before do
161
- headers = { "Set-Cookie" => "prefixSession=789; path=/; domain=localhost; HttpOnly" }
162
-
163
188
  stub_request(:post, api_url).
164
- with(body: { format: "json", action: "login", lgname: "Test", lgpassword: "qwe123" }).
189
+ with(body: { format: 'json', action: 'login', lgname: 'Test', lgpassword: 'qwe123' }).
165
190
  to_return(
166
- body: { login: body_base.merge({ result: "NeedToken", token: "456" }) }.to_json,
167
- headers: { "Set-Cookie" => "prefixSession=789; path=/; domain=localhost; HttpOnly" }
191
+ body: { login: body_base.merge(result: 'NeedToken', token: '456') }.to_json,
192
+ headers: { 'Set-Cookie' => 'prefixSession=789; path=/; domain=localhost; HttpOnly' }
168
193
  )
169
194
 
170
195
  @success_req = stub_request(:post, api_url).
171
- with(body: { format: "json", action: "login", lgname: "Test", lgpassword: "qwe123", lgtoken: "456" }).
172
- with(headers: { "Cookie" => "prefixSession=789" }).
173
- to_return(body: { login: body_base.merge({ result: "Success" }) }.to_json )
196
+ with(body: { format: 'json', action: 'login',
197
+ lgname: 'Test', lgpassword: 'qwe123', lgtoken: '456' }).
198
+ with(headers: { 'Cookie' => 'prefixSession=789' }).
199
+ to_return(body: { login: body_base.merge(result: 'Success') }.to_json)
174
200
  end
175
201
 
176
- it "logs in" do
177
- response = subject.log_in("Test", "qwe123")
202
+ it 'logs in' do
203
+ response = subject.log_in('Test', 'qwe123')
178
204
 
179
- expect(response).to include("result" => "Success")
205
+ expect(response).to include('result' => 'Success')
180
206
  expect(subject.logged_in).to be true
181
207
  end
182
208
 
183
- it "sends second request with token and cookies" do
184
- response = subject.log_in("Test", "qwe123")
209
+ it 'sends second request with token and cookies' do
210
+ subject.log_in('Test', 'qwe123')
185
211
 
186
212
  expect(@success_req).to have_been_requested
187
213
  end
188
214
  end
189
215
 
190
- context "when API returns neither Success nor NeedToken" do
216
+ context 'when API returns neither Success nor NeedToken' do
191
217
  before do
192
218
  stub_request(:post, api_url).
193
- with(body: { format: "json", action: "login", lgname: "Test", lgpassword: "qwe123" }).
194
- to_return(body: { login: body_base.merge({ result: "EmptyPass" }) }.to_json )
219
+ with(body: { format: 'json', action: 'login', lgname: 'Test', lgpassword: 'qwe123' }).
220
+ to_return(body: { login: body_base.merge(result: 'EmptyPass') }.to_json)
195
221
  end
196
222
 
197
- it "does not log in" do
198
- expect { subject.log_in "Test", "qwe123" }.to raise_error
223
+ it 'does not log in' do
224
+ expect { subject.log_in 'Test', 'qwe123' }.to raise_error
199
225
  expect(subject.logged_in).to be false
200
226
  end
201
227
 
202
- it "raises error with proper message" do
203
- expect { subject.log_in "Test", "qwe123" }.to raise_error MediawikiApi::LoginError, "EmptyPass"
228
+ it 'raises error with proper message' do
229
+ expect { subject.log_in 'Test', 'qwe123' }.to raise_error(MediawikiApi::LoginError,
230
+ 'EmptyPass')
204
231
  end
205
232
  end
206
233
  end
207
234
 
208
- describe "#create_page" do
235
+ describe '#create_page' do
209
236
  subject { client.create_page(title, text) }
210
237
 
211
- let(:title) { "Test" }
212
- let(:text) { "test123" }
238
+ let(:title) { 'Test' }
239
+ let(:text) { 'test123' }
213
240
  let(:response) { {} }
214
241
 
215
242
  before do
@@ -218,30 +245,31 @@ describe MediawikiApi::Client do
218
245
  to_return(body: response.to_json)
219
246
  end
220
247
 
221
- it "makes the right request" do
248
+ it 'makes the right request' do
222
249
  subject
223
250
  expect(@edit_request).to have_been_requested
224
251
  end
225
252
  end
226
253
 
227
- describe "#delete_page" do
254
+ describe '#delete_page' do
228
255
  before do
229
256
  stub_request(:get, api_url).
230
- with(query: { format: "json", action: "tokens", type: "delete" }).
231
- to_return(body: { tokens: { deletetoken: "t123" } }.to_json )
257
+ with(query: { format: 'json', action: 'tokens', type: 'delete' }).
258
+ to_return(body: { tokens: { deletetoken: 't123' } }.to_json)
232
259
  @delete_req = stub_request(:post, api_url).
233
- with(body: { format: "json", action: "delete", title: "Test", reason: "deleting", token: "t123" })
260
+ with(body: { format: 'json', action: 'delete',
261
+ title: 'Test', reason: 'deleting', token: 't123' })
234
262
  end
235
263
 
236
- it "deletes a page using a delete token" do
237
- subject.delete_page("Test", "deleting")
264
+ it 'deletes a page using a delete token' do
265
+ subject.delete_page('Test', 'deleting')
238
266
  expect(@delete_req).to have_been_requested
239
267
  end
240
268
 
241
269
  # evaluate results
242
270
  end
243
271
 
244
- describe "#edit" do
272
+ describe '#edit' do
245
273
  subject { client.edit(params) }
246
274
 
247
275
  let(:params) { {} }
@@ -252,108 +280,113 @@ describe MediawikiApi::Client do
252
280
  @edit_request = stub_action_request(:edit).to_return(body: response.to_json)
253
281
  end
254
282
 
255
- it "makes the request" do
283
+ it 'makes the request' do
256
284
  subject
257
285
  expect(@edit_request).to have_been_requested
258
286
  end
259
287
 
260
- context "upon an edit failure" do
261
- let(:response) { { edit: { result: "Failure" } } }
288
+ context 'upon an edit failure' do
289
+ let(:response) { { edit: { result: 'Failure' } } }
262
290
 
263
- it "raises an EditError" do
291
+ it 'raises an EditError' do
264
292
  expect { subject }.to raise_error(MediawikiApi::EditError)
265
293
  end
266
294
  end
267
295
  end
268
296
 
269
- describe "#get_wikitext" do
297
+ describe '#get_wikitext' do
270
298
  before do
271
- @get_req = stub_request(:get, index_url).with(query: { action: "raw", title: "Test" })
299
+ @get_req = stub_request(:get, index_url).with(query: { action: 'raw', title: 'Test' })
272
300
  end
273
301
 
274
- it "fetches a page" do
275
- subject.get_wikitext("Test")
302
+ it 'fetches a page' do
303
+ subject.get_wikitext('Test')
276
304
  expect(@get_req).to have_been_requested
277
305
  end
278
306
  end
279
307
 
280
- describe "#create_account" do
281
- it "creates an account when API returns Success" do
308
+ describe '#create_account' do
309
+ it 'creates an account when API returns Success' do
282
310
  stub_request(:post, api_url).
283
- with(body: { format: "json", action: "createaccount", name: "Test", password: "qwe123" }).
284
- to_return(body: { createaccount: body_base.merge({ result: "Success" }) }.to_json )
311
+ with(body: { format: 'json', action: 'createaccount', name: 'Test', password: 'qwe123' }).
312
+ to_return(body: { createaccount: body_base.merge(result: 'Success') }.to_json)
285
313
 
286
- expect(subject.create_account("Test", "qwe123")).to include("result" => "Success")
314
+ expect(subject.create_account('Test', 'qwe123')).to include('result' => 'Success')
287
315
  end
288
316
 
289
- context "when API returns NeedToken" do
317
+ context 'when API returns NeedToken' do
290
318
  before do
291
- headers = { "Set-Cookie" => "prefixSession=789; path=/; domain=localhost; HttpOnly" }
292
-
293
319
  stub_request(:post, api_url).
294
- with(body: { format: "json", action: "createaccount", name: "Test", password: "qwe123" }).
320
+ with(body: { format: 'json', action: 'createaccount',
321
+ name: 'Test', password: 'qwe123' }).
295
322
  to_return(
296
- body: { createaccount: body_base.merge({ result: "NeedToken", token: "456" }) }.to_json,
297
- headers: { "Set-Cookie" => "prefixSession=789; path=/; domain=localhost; HttpOnly" }
323
+ body: { createaccount: body_base.merge(result: 'NeedToken', token: '456') }.to_json,
324
+ headers: { 'Set-Cookie' => 'prefixSession=789; path=/; domain=localhost; HttpOnly' }
298
325
  )
299
326
 
300
327
  @success_req = stub_request(:post, api_url).
301
- with(body: { format: "json", action: "createaccount", name: "Test", password: "qwe123", token: "456" }).
302
- with(headers: { "Cookie" => "prefixSession=789" }).
303
- to_return(body: { createaccount: body_base.merge({ result: "Success" }) }.to_json )
328
+ with(body: { format: 'json', action: 'createaccount',
329
+ name: 'Test', password: 'qwe123', token: '456' }).
330
+ with(headers: { 'Cookie' => 'prefixSession=789' }).
331
+ to_return(body: { createaccount: body_base.merge(result: 'Success') }.to_json)
304
332
  end
305
333
 
306
- it "creates an account" do
307
- expect(subject.create_account("Test", "qwe123")).to include("result" => "Success")
334
+ it 'creates an account' do
335
+ expect(subject.create_account('Test', 'qwe123')).to include('result' => 'Success')
308
336
  end
309
337
 
310
- it "sends second request with token and cookies" do
311
- subject.create_account "Test", "qwe123"
338
+ it 'sends second request with token and cookies' do
339
+ subject.create_account 'Test', 'qwe123'
312
340
  expect(@success_req).to have_been_requested
313
341
  end
314
342
  end
315
343
 
316
344
  # docs don't specify other results, but who knows
317
345
  # http://www.mediawiki.org/wiki/API:Account_creation
318
- context "when API returns neither Success nor NeedToken" do
346
+ context 'when API returns neither Success nor NeedToken' do
319
347
  before do
320
348
  stub_request(:post, api_url).
321
- with(body: { format: "json", action: "createaccount", name: "Test", password: "qwe123" }).
322
- to_return(body: { createaccount: body_base.merge({ result: "WhoKnows" }) }.to_json )
349
+ with(body: { format: 'json', action: 'createaccount',
350
+ name: 'Test', password: 'qwe123' }).
351
+ to_return(body: { createaccount: body_base.merge(result: 'WhoKnows') }.to_json)
323
352
  end
324
353
 
325
- it "raises error with proper message" do
326
- expect { subject.create_account "Test", "qwe123" }.to raise_error MediawikiApi::CreateAccountError, "WhoKnows"
354
+ it 'raises error with proper message' do
355
+ expect { subject.create_account 'Test', 'qwe123' }.to raise_error(
356
+ MediawikiApi::CreateAccountError,
357
+ 'WhoKnows'
358
+ )
327
359
  end
328
360
  end
329
361
  end
330
362
 
331
- describe "#watch_page" do
363
+ describe '#watch_page' do
332
364
  before do
333
365
  stub_request(:get, api_url).
334
- with(query: { format: "json", action: "tokens", type: "watch" }).
335
- to_return(body: { tokens: { watchtoken: "t123" } }.to_json )
366
+ with(query: { format: 'json', action: 'tokens', type: 'watch' }).
367
+ to_return(body: { tokens: { watchtoken: 't123' } }.to_json)
336
368
  @watch_req = stub_request(:post, api_url).
337
- with(body: { format: "json", token: "t123", action: "watch", titles: "Test" })
369
+ with(body: { format: 'json', token: 't123', action: 'watch', titles: 'Test' })
338
370
  end
339
371
 
340
- it "sends a valid watch request" do
341
- subject.watch_page("Test")
372
+ it 'sends a valid watch request' do
373
+ subject.watch_page('Test')
342
374
  expect(@watch_req).to have_been_requested
343
375
  end
344
376
  end
345
377
 
346
- describe "#unwatch_page" do
378
+ describe '#unwatch_page' do
347
379
  before do
348
380
  stub_request(:get, api_url).
349
- with(query: { format: "json", action: "tokens", type: "watch" }).
350
- to_return(body: { tokens: { watchtoken: "t123" } }.to_json )
381
+ with(query: { format: 'json', action: 'tokens', type: 'watch' }).
382
+ to_return(body: { tokens: { watchtoken: 't123' } }.to_json)
351
383
  @watch_req = stub_request(:post, api_url).
352
- with(body: { format: "json", token: "t123", action: "watch", titles: "Test", unwatch: "true" })
384
+ with(body: { format: 'json', token: 't123', action: 'watch',
385
+ titles: 'Test', unwatch: 'true' })
353
386
  end
354
387
 
355
- it "sends a valid unwatch request" do
356
- subject.unwatch_page("Test")
388
+ it 'sends a valid unwatch request' do
389
+ subject.unwatch_page('Test')
357
390
  expect(@watch_req).to have_been_requested
358
391
  end
359
392
  end