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 +4 -4
- data/.rubocop.yml +33 -1
- data/CREDITS +1 -0
- data/Gemfile +3 -3
- data/README.md +3 -0
- data/lib/mediawiki_api.rb +1 -1
- data/lib/mediawiki_api/client.rb +63 -43
- data/lib/mediawiki_api/exceptions.rb +6 -3
- data/lib/mediawiki_api/response.rb +19 -7
- data/lib/mediawiki_api/version.rb +2 -1
- data/mediawiki_api.gemspec +28 -20
- data/spec/client_spec.rb +153 -120
- data/spec/response_spec.rb +25 -25
- data/spec/spec_helper.rb +1 -1
- data/spec/support/request_helpers.rb +27 -24
- metadata +51 -35
- data/.rubocop_todo.yml +0 -102
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b2e13c62db930923fbfd515cb2029d9d43508b9a
|
4
|
+
data.tar.gz: dada9ee27c4c09a1a2c7a157828e16079beace62
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 145fc6da63febab8ffc18b4d32a6eda108232052fd927380357a7fdfc375eb482681c1ac339e3299f4fee62c3f05fac2e03d766cd401a891991af74a5eb35111
|
7
|
+
data.tar.gz: eba8ace9d4a58f8148c7e5601d6e216475fc0bce8612bd78bea5fab637a63ed46ffcd4b042cb9bad82d0ace755685d025c0b1d89ccf84567c9fc698aeccbf23e
|
data/.rubocop.yml
CHANGED
@@ -1 +1,33 @@
|
|
1
|
-
|
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
data/Gemfile
CHANGED
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
|
1
|
+
require 'mediawiki_api/client'
|
data/lib/mediawiki_api/client.rb
CHANGED
@@ -1,17 +1,18 @@
|
|
1
|
-
require
|
2
|
-
require
|
3
|
-
require
|
1
|
+
require 'faraday'
|
2
|
+
require 'faraday-cookie_jar'
|
3
|
+
require 'json'
|
4
4
|
|
5
|
-
require
|
6
|
-
require
|
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 =
|
11
|
+
FORMAT = 'json'
|
11
12
|
|
12
13
|
attr_accessor :logged_in
|
13
14
|
|
14
|
-
|
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
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
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[
|
56
|
-
when
|
46
|
+
case data['result']
|
47
|
+
when 'Success'
|
57
48
|
@logged_in = true
|
58
49
|
@tokens.clear
|
59
|
-
when
|
60
|
-
data = create_account(username, password, data[
|
50
|
+
when 'NeedToken'
|
51
|
+
data = create_account(username, password, data['token'])
|
61
52
|
else
|
62
|
-
raise CreateAccountError, data[
|
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[
|
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
|
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[
|
97
|
-
when
|
87
|
+
case data['result']
|
88
|
+
when 'Success'
|
98
89
|
@logged_in = true
|
99
90
|
@tokens.clear
|
100
|
-
when
|
101
|
-
data = log_in(username, password, data[
|
91
|
+
when 'NeedToken'
|
92
|
+
data = log_in(username, password, data['token'])
|
102
93
|
else
|
103
|
-
raise LoginError, data[
|
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 =
|
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,
|
131
|
-
action(:upload,
|
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(
|
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 => [
|
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[
|
11
|
+
data['code']
|
11
12
|
end
|
12
13
|
|
13
14
|
def info
|
14
|
-
data[
|
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
|
-
|
48
|
+
'check the response data for details'
|
46
49
|
end
|
47
50
|
end
|
48
51
|
|
@@ -1,5 +1,5 @@
|
|
1
|
-
require
|
2
|
-
require
|
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
|
-
|
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
|
data/mediawiki_api.gemspec
CHANGED
@@ -1,29 +1,37 @@
|
|
1
1
|
# coding: utf-8
|
2
|
-
lib = File.expand_path(
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
3
|
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
-
require
|
4
|
+
require 'mediawiki_api/version'
|
5
5
|
|
6
6
|
Gem::Specification.new do |spec|
|
7
|
-
spec.name =
|
7
|
+
spec.name = 'mediawiki_api'
|
8
8
|
spec.version = MediawikiApi::VERSION
|
9
|
-
spec.authors = [
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
spec.
|
14
|
-
|
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(
|
18
|
-
spec.require_paths = [
|
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
|
21
|
-
spec.add_runtime_dependency
|
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
|
24
|
-
spec.add_development_dependency
|
25
|
-
spec.add_development_dependency
|
26
|
-
spec.add_development_dependency
|
27
|
-
spec.add_development_dependency
|
28
|
-
spec.add_development_dependency
|
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
|
2
|
-
require
|
3
|
-
require
|
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:
|
12
|
+
body_base = { cookieprefix: 'prefix', sessionid: '123' }
|
13
13
|
|
14
|
-
describe
|
14
|
+
describe '#action' do
|
15
15
|
subject { client.action(action, params) }
|
16
16
|
|
17
|
-
let(:action) {
|
17
|
+
let(:action) { 'something' }
|
18
18
|
let(:token_type) { action }
|
19
19
|
let(:params) { {} }
|
20
20
|
|
21
|
-
let(:response)
|
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) { {
|
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
|
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
|
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
|
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
|
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
|
62
|
-
let(:params) { { foo:
|
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:
|
68
|
+
@request_with_parameters = stub_action_request(action, foo: 'value').to_return(response)
|
66
69
|
end
|
67
70
|
|
68
|
-
it
|
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
|
75
|
-
context
|
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
|
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
|
91
|
-
let(:params) { { foo:
|
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:
|
97
|
+
@request = stub_action_request(action, foo: 'one|two').to_return(response)
|
95
98
|
end
|
96
99
|
|
97
|
-
it
|
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
|
107
|
+
context 'when the response status is in the 400 range' do
|
105
108
|
let(:response_status) { 403 }
|
106
109
|
|
107
|
-
it
|
108
|
-
expect { subject }.to raise_error(MediawikiApi::HttpError,
|
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
|
116
|
+
context 'when the response status is in the 500 range' do
|
113
117
|
let(:response_status) { 502 }
|
114
118
|
|
115
|
-
it
|
116
|
-
expect { subject }.to raise_error(MediawikiApi::HttpError,
|
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
|
121
|
-
let(:response_headers) { {
|
122
|
-
let(:response_body) { { error: { info:
|
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
|
125
|
-
expect { subject }.to raise_error(MediawikiApi::ApiError,
|
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
|
134
|
+
context 'given a bad token type' do
|
130
135
|
let(:params) { { token_type: token_type } }
|
131
|
-
let(:token_type) {
|
136
|
+
let(:token_type) { 'badtoken' }
|
132
137
|
let(:token_warning) { "Unrecognized value for parameter 'type': badtoken" }
|
133
138
|
|
134
|
-
it
|
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
|
140
|
-
let(:token_warning)
|
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
|
-
|
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
|
175
|
+
describe '#log_in' do
|
149
176
|
|
150
|
-
it
|
177
|
+
it 'logs in when API returns Success' do
|
151
178
|
stub_request(:post, api_url).
|
152
|
-
with(body: { format:
|
153
|
-
to_return(body: { login: body_base.merge(
|
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
|
182
|
+
subject.log_in 'Test', 'qwe123'
|
156
183
|
expect(subject.logged_in).to be true
|
157
184
|
end
|
158
185
|
|
159
|
-
context
|
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:
|
189
|
+
with(body: { format: 'json', action: 'login', lgname: 'Test', lgpassword: 'qwe123' }).
|
165
190
|
to_return(
|
166
|
-
body: { login: body_base.merge(
|
167
|
-
headers: {
|
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:
|
172
|
-
|
173
|
-
|
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
|
177
|
-
response = subject.log_in(
|
202
|
+
it 'logs in' do
|
203
|
+
response = subject.log_in('Test', 'qwe123')
|
178
204
|
|
179
|
-
expect(response).to include(
|
205
|
+
expect(response).to include('result' => 'Success')
|
180
206
|
expect(subject.logged_in).to be true
|
181
207
|
end
|
182
208
|
|
183
|
-
it
|
184
|
-
|
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
|
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:
|
194
|
-
to_return(body: { login: body_base.merge(
|
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
|
198
|
-
expect { subject.log_in
|
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
|
203
|
-
expect { subject.log_in
|
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
|
235
|
+
describe '#create_page' do
|
209
236
|
subject { client.create_page(title, text) }
|
210
237
|
|
211
|
-
let(:title) {
|
212
|
-
let(:text) {
|
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
|
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
|
254
|
+
describe '#delete_page' do
|
228
255
|
before do
|
229
256
|
stub_request(:get, api_url).
|
230
|
-
with(query: { format:
|
231
|
-
to_return(body: { tokens: { deletetoken:
|
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:
|
260
|
+
with(body: { format: 'json', action: 'delete',
|
261
|
+
title: 'Test', reason: 'deleting', token: 't123' })
|
234
262
|
end
|
235
263
|
|
236
|
-
it
|
237
|
-
subject.delete_page(
|
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
|
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
|
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
|
261
|
-
let(:response) { { edit: { result:
|
288
|
+
context 'upon an edit failure' do
|
289
|
+
let(:response) { { edit: { result: 'Failure' } } }
|
262
290
|
|
263
|
-
it
|
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
|
297
|
+
describe '#get_wikitext' do
|
270
298
|
before do
|
271
|
-
@get_req = stub_request(:get, index_url).with(query: { action:
|
299
|
+
@get_req = stub_request(:get, index_url).with(query: { action: 'raw', title: 'Test' })
|
272
300
|
end
|
273
301
|
|
274
|
-
it
|
275
|
-
subject.get_wikitext(
|
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
|
281
|
-
it
|
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:
|
284
|
-
to_return(body: { createaccount: body_base.merge(
|
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(
|
314
|
+
expect(subject.create_account('Test', 'qwe123')).to include('result' => 'Success')
|
287
315
|
end
|
288
316
|
|
289
|
-
context
|
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:
|
320
|
+
with(body: { format: 'json', action: 'createaccount',
|
321
|
+
name: 'Test', password: 'qwe123' }).
|
295
322
|
to_return(
|
296
|
-
body: { createaccount: body_base.merge(
|
297
|
-
headers: {
|
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:
|
302
|
-
|
303
|
-
|
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
|
307
|
-
expect(subject.create_account(
|
334
|
+
it 'creates an account' do
|
335
|
+
expect(subject.create_account('Test', 'qwe123')).to include('result' => 'Success')
|
308
336
|
end
|
309
337
|
|
310
|
-
it
|
311
|
-
subject.create_account
|
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
|
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:
|
322
|
-
|
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
|
326
|
-
expect { subject.create_account
|
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
|
363
|
+
describe '#watch_page' do
|
332
364
|
before do
|
333
365
|
stub_request(:get, api_url).
|
334
|
-
with(query: { format:
|
335
|
-
to_return(body: { tokens: { watchtoken:
|
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:
|
369
|
+
with(body: { format: 'json', token: 't123', action: 'watch', titles: 'Test' })
|
338
370
|
end
|
339
371
|
|
340
|
-
it
|
341
|
-
subject.watch_page(
|
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
|
378
|
+
describe '#unwatch_page' do
|
347
379
|
before do
|
348
380
|
stub_request(:get, api_url).
|
349
|
-
with(query: { format:
|
350
|
-
to_return(body: { tokens: { watchtoken:
|
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:
|
384
|
+
with(body: { format: 'json', token: 't123', action: 'watch',
|
385
|
+
titles: 'Test', unwatch: 'true' })
|
353
386
|
end
|
354
387
|
|
355
|
-
it
|
356
|
-
subject.unwatch_page(
|
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
|