mediawiki_api 0.1.4 → 0.2.0
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/.gitignore +6 -0
- data/CREDITS +1 -0
- data/README.md +18 -1
- data/lib/mediawiki_api/client.rb +105 -43
- data/lib/mediawiki_api/exceptions.rb +36 -0
- data/lib/mediawiki_api/response.rb +88 -0
- data/lib/mediawiki_api/version.rb +1 -1
- data/mediawiki_api.gemspec +2 -2
- data/spec/client_spec.rb +141 -13
- data/spec/response_spec.rb +90 -0
- data/spec/spec_helper.rb +2 -16
- data/spec/support/request_helpers.rb +29 -0
- metadata +12 -4
- data/Gemfile.lock +0 -54
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 47ada7ae6cca60fe668ec85a2db600bc2c8476d3
|
4
|
+
data.tar.gz: 1404eb28565f5c58106dd534426a1d29de9686bb
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d19e7cceb2b8436cfcae8198eba6d220ef333bf8b9397328929dec24a7b68bd738e24118b4994d60c19639514d3f85fc636bcef525ba5ce9a038d20555274b91
|
7
|
+
data.tar.gz: d1911e5759b7ac06de105c3728c7afbb50a3eec5e79549dfe2d92bdbe45f861ea91cc7d1827a40032350867a274feca6cbe8aee9f0fbca1763c9465b67f3390c
|
data/.gitignore
ADDED
data/CREDITS
CHANGED
data/README.md
CHANGED
@@ -32,8 +32,17 @@ client.get_wikitext "title"
|
|
32
32
|
client.protect_page "title", "reason", "protections" # protections are optional, default is "edit=sysop|move=sysop"
|
33
33
|
client.delete_page "title", "reason"
|
34
34
|
client.upload_image "filename", "path", "comment", "ignorewarnings"
|
35
|
+
client.meta :siteinfo, siprop: "extensions"
|
36
|
+
client.prop :info, titles: "Some page"
|
37
|
+
client.query titles: ["Some page", "Some other page"]
|
35
38
|
```
|
36
39
|
|
40
|
+
## Advanced Usage
|
41
|
+
|
42
|
+
Any API action can be requested using `#action`. See the
|
43
|
+
[MediaWiki API documentation](http://www.mediawiki.org/wiki/API) for supported
|
44
|
+
actions and parameters.
|
45
|
+
|
37
46
|
## Links
|
38
47
|
|
39
48
|
MediaWiki API gem at: [Gerrit](https://gerrit.wikimedia.org/r/#/admin/projects/mediawiki/ruby/api), [GitHub](https://github.com/wikimedia/mediawiki-ruby-api), [RubyGems](https://rubygems.org/gems/mediawiki_api), [Code Climate](https://codeclimate.com/github/wikimedia/mediawiki-ruby-api).
|
@@ -49,7 +58,15 @@ MediaWiki API gem at: [Gerrit](https://gerrit.wikimedia.org/r/#/admin/projects/m
|
|
49
58
|
|
50
59
|
## Release notes
|
51
60
|
|
52
|
-
### 0.
|
61
|
+
### 0.2.0 2014-08-06
|
62
|
+
|
63
|
+
- Automatic response parsing.
|
64
|
+
- Handling of API error responses.
|
65
|
+
- Watch/unwatch support.
|
66
|
+
- Query support.
|
67
|
+
- Public MediawikiApi::Client#action method for advanced API use.
|
68
|
+
|
69
|
+
### 0.1.4 2014-07-18
|
53
70
|
|
54
71
|
- Added MediawikiApi::Client#protect_page.
|
55
72
|
- Updated documentation.
|
data/lib/mediawiki_api/client.rb
CHANGED
@@ -2,19 +2,17 @@ require "faraday"
|
|
2
2
|
require "faraday-cookie_jar"
|
3
3
|
require "json"
|
4
4
|
|
5
|
-
|
6
|
-
|
7
|
-
end
|
8
|
-
|
9
|
-
class CreateAccountError < StandardError
|
10
|
-
end
|
11
|
-
|
12
|
-
class TokenError < StandardError
|
13
|
-
end
|
5
|
+
require "mediawiki_api/exceptions"
|
6
|
+
require "mediawiki_api/response"
|
14
7
|
|
8
|
+
module MediawikiApi
|
15
9
|
class Client
|
10
|
+
FORMAT = "json"
|
11
|
+
|
16
12
|
attr_accessor :logged_in
|
17
13
|
|
14
|
+
alias logged_in? logged_in
|
15
|
+
|
18
16
|
def initialize(url, log = false)
|
19
17
|
@conn = Faraday.new(url: url) do |faraday|
|
20
18
|
faraday.request :multipart
|
@@ -24,76 +22,140 @@ module MediawikiApi
|
|
24
22
|
faraday.adapter Faraday.default_adapter
|
25
23
|
end
|
26
24
|
@logged_in = false
|
25
|
+
@tokens = {}
|
27
26
|
end
|
28
27
|
|
29
|
-
def
|
30
|
-
|
31
|
-
params[:lgtoken] = token unless token.nil?
|
32
|
-
resp = @conn.post "", params
|
28
|
+
def action(name, params = {})
|
29
|
+
name = name.to_s
|
33
30
|
|
34
|
-
|
31
|
+
method = params.delete(:http_method) || :post
|
32
|
+
token_type = params.delete(:token_type)
|
33
|
+
envelope = (params.delete(:envelope) || [name]).map(&:to_s)
|
35
34
|
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
raise
|
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
|
+
if response.headers.include?("mediawiki-api-error")
|
41
|
+
raise ApiError.new(Response.new(response, ["error"]))
|
43
42
|
end
|
43
|
+
|
44
|
+
Response.new(response, envelope)
|
44
45
|
end
|
45
46
|
|
46
47
|
def create_account(username, password, token = nil)
|
47
|
-
params = {
|
48
|
+
params = { name: username, password: password, token_type: false }
|
48
49
|
params[:token] = token unless token.nil?
|
49
|
-
resp = @conn.post "", params
|
50
50
|
|
51
|
-
data =
|
51
|
+
data = action(:createaccount, params).data
|
52
52
|
|
53
53
|
case data["result"]
|
54
54
|
when "Success"
|
55
55
|
@logged_in = true
|
56
|
+
@tokens.clear
|
56
57
|
when "NeedToken"
|
57
|
-
create_account
|
58
|
+
data = create_account(username, password, data["token"])
|
58
59
|
else
|
59
60
|
raise CreateAccountError, data["result"]
|
60
61
|
end
|
62
|
+
|
63
|
+
data
|
61
64
|
end
|
62
65
|
|
63
66
|
def create_page(title, content)
|
64
|
-
|
65
|
-
@conn.post "", action: "edit", title: title, text: content, token: token, format: "json"
|
67
|
+
action(:edit, title: title, text: content)
|
66
68
|
end
|
67
69
|
|
68
70
|
def delete_page(title, reason)
|
69
|
-
|
70
|
-
@conn.post "", action: "delete", title: title, reason: reason, token: token, format: "json"
|
71
|
-
end
|
72
|
-
|
73
|
-
def upload_image(filename, path, comment, ignorewarnings)
|
74
|
-
token = get_token "edit"
|
75
|
-
@conn.post "", action: "upload", filename: filename, file: Faraday::UploadIO.new(path, 'image/png'), token: token, comment: comment, ignorewarnings: ignorewarnings, format: "json"
|
71
|
+
action(:delete, title: title, reason: reason)
|
76
72
|
end
|
77
73
|
|
78
74
|
def get_wikitext(title)
|
79
75
|
@conn.get "/w/index.php", { action: "raw", title: title }
|
80
76
|
end
|
81
77
|
|
82
|
-
def
|
83
|
-
|
84
|
-
|
78
|
+
def list(type, params = {})
|
79
|
+
subquery(:list, type, params)
|
80
|
+
end
|
81
|
+
|
82
|
+
def log_in(username, password, token = nil)
|
83
|
+
params = { lgname: username, lgpassword: password, token_type: false }
|
84
|
+
params[:lgtoken] = token unless token.nil?
|
85
|
+
|
86
|
+
data = action(:login, params).data
|
87
|
+
|
88
|
+
case data["result"]
|
89
|
+
when "Success"
|
90
|
+
@logged_in = true
|
91
|
+
@tokens.clear
|
92
|
+
when "NeedToken"
|
93
|
+
data = log_in(username, password, data["token"])
|
94
|
+
else
|
95
|
+
raise LoginError, data["result"]
|
96
|
+
end
|
97
|
+
|
98
|
+
data
|
99
|
+
end
|
100
|
+
|
101
|
+
def meta(type, params = {})
|
102
|
+
subquery(:meta, type, params)
|
103
|
+
end
|
104
|
+
|
105
|
+
def prop(type, params = {})
|
106
|
+
subquery(:prop, type, params)
|
107
|
+
end
|
108
|
+
|
109
|
+
def protect_page(title, reason, protections = "edit=sysop|move=sysop")
|
110
|
+
action(:protect, title: title, reason: reason, protections: protections)
|
111
|
+
end
|
112
|
+
|
113
|
+
def query(params = {})
|
114
|
+
action(:query, { token_type: false, http_method: :get }.merge(params))
|
115
|
+
end
|
116
|
+
|
117
|
+
def unwatch_page(title)
|
118
|
+
action(:watch, titles: title, unwatch: true)
|
119
|
+
end
|
120
|
+
|
121
|
+
def upload_image(filename, path, comment, ignorewarnings)
|
122
|
+
file = Faraday::UploadIO.new(path, "image/png")
|
123
|
+
action(:upload, token_type: "edit", filename: filename, file: file, comment: comment, ignorewarnings: ignorewarnings)
|
124
|
+
end
|
125
|
+
|
126
|
+
def watch_page(title)
|
127
|
+
action(:watch, titles: title)
|
85
128
|
end
|
86
129
|
|
87
130
|
protected
|
88
131
|
|
132
|
+
def compile_parameters(parameters)
|
133
|
+
parameters.each.with_object({}) do |(name, value), params|
|
134
|
+
case value
|
135
|
+
when false
|
136
|
+
# omit it entirely
|
137
|
+
when Array
|
138
|
+
params[name] = value.join("|")
|
139
|
+
else
|
140
|
+
params[name] = value
|
141
|
+
end
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
89
145
|
def get_token(type)
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
raise TokenError,
|
94
|
-
|
95
|
-
|
146
|
+
unless @tokens.include?(type)
|
147
|
+
response = action(:tokens, type: type, http_method: :get, token_type: false)
|
148
|
+
|
149
|
+
raise TokenError, response.warnings.join(", ") if response.warnings?
|
150
|
+
|
151
|
+
@tokens[type] = response.data["#{type}token"]
|
96
152
|
end
|
153
|
+
|
154
|
+
@tokens[type]
|
155
|
+
end
|
156
|
+
|
157
|
+
def subquery(type, subtype, params = {})
|
158
|
+
query(params.merge(type.to_sym => subtype, :envelope => ["query", subtype]))
|
97
159
|
end
|
98
160
|
end
|
99
161
|
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
module MediawikiApi
|
2
|
+
class ApiError < StandardError
|
3
|
+
attr_reader :response
|
4
|
+
|
5
|
+
def initialize(response)
|
6
|
+
@response = response
|
7
|
+
end
|
8
|
+
|
9
|
+
def code
|
10
|
+
data["code"]
|
11
|
+
end
|
12
|
+
|
13
|
+
def info
|
14
|
+
data["info"]
|
15
|
+
end
|
16
|
+
|
17
|
+
def to_s
|
18
|
+
"#{info} (#{code})"
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
def data
|
24
|
+
@response.data || {}
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
class CreateAccountError < StandardError
|
29
|
+
end
|
30
|
+
|
31
|
+
class LoginError < StandardError
|
32
|
+
end
|
33
|
+
|
34
|
+
class TokenError < StandardError
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,88 @@
|
|
1
|
+
require "forwardable"
|
2
|
+
require "json"
|
3
|
+
|
4
|
+
module MediawikiApi
|
5
|
+
# Provides access to a parsed MediaWiki API responses.
|
6
|
+
#
|
7
|
+
# Some types of responses, depending on the action, contain a level or two
|
8
|
+
# of addition structure (an envelope) above the actual payload. The {#data}
|
9
|
+
# method provides a way of easily getting at it.
|
10
|
+
#
|
11
|
+
# @example
|
12
|
+
# # http.body => '{"query": {"userinfo": {"some": "data"}}}'
|
13
|
+
# response = Response.new(http, ["query", "userinfo"])
|
14
|
+
# response.data # => { "some" => "data" }
|
15
|
+
#
|
16
|
+
class Response
|
17
|
+
extend Forwardable
|
18
|
+
|
19
|
+
def_delegators :@response, :status, :success?
|
20
|
+
|
21
|
+
# Constructs a new response.
|
22
|
+
#
|
23
|
+
# @param response [Faraday::Response]
|
24
|
+
# @param envelope [Array] Property names for expected payload nesting.
|
25
|
+
#
|
26
|
+
def initialize(response, envelope = [])
|
27
|
+
@response = response
|
28
|
+
@envelope = envelope
|
29
|
+
end
|
30
|
+
|
31
|
+
# Accessor for root response object values.
|
32
|
+
#
|
33
|
+
# @param key [String]
|
34
|
+
#
|
35
|
+
# @return [Object]
|
36
|
+
#
|
37
|
+
def [](key)
|
38
|
+
response_object[key]
|
39
|
+
end
|
40
|
+
|
41
|
+
# The main payload from the parsed response, removed from its envelope.
|
42
|
+
#
|
43
|
+
# @return [Object]
|
44
|
+
#
|
45
|
+
def data
|
46
|
+
case response_object
|
47
|
+
when Hash
|
48
|
+
open_envelope(response_object)
|
49
|
+
else
|
50
|
+
response_object
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
# Set of warning messages from the response.
|
55
|
+
#
|
56
|
+
# @return [Array]
|
57
|
+
#
|
58
|
+
def warnings
|
59
|
+
if response_object["warnings"]
|
60
|
+
response_object["warnings"].values.map(&:values).flatten
|
61
|
+
else
|
62
|
+
[]
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
# Whether the response contains warnings.
|
67
|
+
#
|
68
|
+
# @return [true, false]
|
69
|
+
#
|
70
|
+
def warnings?
|
71
|
+
!warnings.empty?
|
72
|
+
end
|
73
|
+
|
74
|
+
private
|
75
|
+
|
76
|
+
def open_envelope(obj, env = @envelope)
|
77
|
+
if !obj.is_a?(Hash) || env.nil? || env.empty? || !obj.include?(env.first)
|
78
|
+
obj
|
79
|
+
else
|
80
|
+
open_envelope(obj[env.first], env[1..-1])
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
def response_object
|
85
|
+
@response_object ||= JSON.parse(@response.body)
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
data/mediawiki_api.gemspec
CHANGED
@@ -6,8 +6,8 @@ require "mediawiki_api/version"
|
|
6
6
|
Gem::Specification.new do |spec|
|
7
7
|
spec.name = "mediawiki_api"
|
8
8
|
spec.version = MediawikiApi::VERSION
|
9
|
-
spec.authors = ["Amir Aharoni", "Chris McMahon", "Jeff Hall", "Juliusz Gonera", "Zeljko Filipin"]
|
10
|
-
spec.email = ["amir.aharoni@mail.huji.ac.il", "cmcmahon@wikimedia.org", "jhall@wikimedia.org", "jgonera@wikimedia.org", "zeljko.filipin@gmail.com"]
|
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
11
|
spec.summary = %q{A library for interacting with MediaWiki API from Ruby.}
|
12
12
|
spec.description = %q{Uses adapter-agnostic Faraday gem to talk to MediaWiki API.}
|
13
13
|
spec.homepage = "https://github.com/wikimedia/mediawiki-ruby-api"
|
data/spec/client_spec.rb
CHANGED
@@ -1,9 +1,112 @@
|
|
1
1
|
require "spec_helper"
|
2
|
+
require "webmock/rspec"
|
3
|
+
require "support/request_helpers"
|
2
4
|
|
3
5
|
describe MediawikiApi::Client do
|
4
|
-
|
6
|
+
include MediawikiApi::RequestHelpers
|
7
|
+
|
8
|
+
let(:client) { MediawikiApi::Client.new(api_url) }
|
9
|
+
|
10
|
+
subject { client }
|
11
|
+
|
5
12
|
body_base = { cookieprefix: "prefix", sessionid: "123" }
|
6
13
|
|
14
|
+
describe "#action" do
|
15
|
+
subject { client.action(action, params) }
|
16
|
+
|
17
|
+
let(:action) { "something" }
|
18
|
+
let(:params) { {} }
|
19
|
+
|
20
|
+
let(:response) { { headers: response_headers, body: response_body.to_json } }
|
21
|
+
let(:response_headers) { nil }
|
22
|
+
let(:response_body) { { "something" => {} } }
|
23
|
+
|
24
|
+
before do
|
25
|
+
@token_request = stub_token_request(action)
|
26
|
+
@request = stub_api_request(:post, action: action, token: mock_token).to_return(response)
|
27
|
+
end
|
28
|
+
|
29
|
+
it { is_expected.to be_a(MediawikiApi::Response) }
|
30
|
+
|
31
|
+
it "makes requests for both the right token and API action" do
|
32
|
+
subject
|
33
|
+
expect(@token_request).to have_been_made
|
34
|
+
expect(@request).to have_been_made
|
35
|
+
end
|
36
|
+
|
37
|
+
context "without a required token" do
|
38
|
+
let(:params) { { token_type: false } }
|
39
|
+
|
40
|
+
before do
|
41
|
+
@request_with_token = @request
|
42
|
+
@request_without_token = stub_api_request(:post, action: action).to_return(response)
|
43
|
+
end
|
44
|
+
|
45
|
+
it "does not request a token" do
|
46
|
+
subject
|
47
|
+
expect(@token_request).to_not have_been_made
|
48
|
+
end
|
49
|
+
|
50
|
+
it "makes the action request without a token" do
|
51
|
+
subject
|
52
|
+
expect(@request_without_token).to have_been_made
|
53
|
+
expect(@request_with_token).to_not have_been_made
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
context "given parameters" do
|
58
|
+
let(:params) { { foo: "value" } }
|
59
|
+
|
60
|
+
before do
|
61
|
+
@request_with_parameters = stub_action_request(action, foo: "value").to_return(response)
|
62
|
+
end
|
63
|
+
|
64
|
+
it "includes them" do
|
65
|
+
subject
|
66
|
+
expect(@request_with_parameters).to have_been_made
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
context "parameter compilation" do
|
71
|
+
context "negated parameters" do
|
72
|
+
let(:params) { { foo: false } }
|
73
|
+
|
74
|
+
before do
|
75
|
+
@request_with_parameter = stub_action_request(action, foo: false).to_return(response)
|
76
|
+
@request_without_parameter = stub_action_request(action).to_return(response)
|
77
|
+
end
|
78
|
+
|
79
|
+
it "omits the parameter" do
|
80
|
+
subject
|
81
|
+
expect(@request_with_parameter).to_not have_been_made
|
82
|
+
expect(@request_without_parameter).to have_been_made
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
context "array parameters" do
|
87
|
+
let(:params) { { foo: ["one", "two"] } }
|
88
|
+
|
89
|
+
before do
|
90
|
+
@request = stub_action_request(action, foo: "one|two").to_return(response)
|
91
|
+
end
|
92
|
+
|
93
|
+
it "pipe delimits values" do
|
94
|
+
subject
|
95
|
+
expect(@request).to have_been_made
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
context "when the response is an error" do
|
101
|
+
let(:response_headers) { { "MediaWiki-API-Error" => "code" } }
|
102
|
+
let(:response_body) { { error: { info: "detailed message", code: "code" } } }
|
103
|
+
|
104
|
+
it "raises an ApiError" do
|
105
|
+
expect { subject }.to raise_error(MediawikiApi::ApiError, "detailed message (code)")
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
7
110
|
describe "#log_in" do
|
8
111
|
|
9
112
|
it "logs in when API returns Success" do
|
@@ -33,12 +136,15 @@ describe MediawikiApi::Client do
|
|
33
136
|
end
|
34
137
|
|
35
138
|
it "logs in" do
|
36
|
-
subject.log_in
|
139
|
+
response = subject.log_in("Test", "qwe123")
|
140
|
+
|
141
|
+
expect(response).to include("result" => "Success")
|
37
142
|
expect(subject.logged_in).to be true
|
38
143
|
end
|
39
144
|
|
40
145
|
it "sends second request with token and cookies" do
|
41
|
-
subject.log_in
|
146
|
+
response = subject.log_in("Test", "qwe123")
|
147
|
+
|
42
148
|
expect(@success_req).to have_been_requested
|
43
149
|
end
|
44
150
|
end
|
@@ -74,14 +180,6 @@ describe MediawikiApi::Client do
|
|
74
180
|
subject.create_page("Test", "test123")
|
75
181
|
expect(@edit_req).to have_been_requested
|
76
182
|
end
|
77
|
-
|
78
|
-
context "when API returns Success" do
|
79
|
-
before do
|
80
|
-
@edit_req.to_return(body: { result: "Success" }.to_json )
|
81
|
-
end
|
82
|
-
|
83
|
-
it "returns a MediawikiApi::Page"
|
84
|
-
end
|
85
183
|
end
|
86
184
|
|
87
185
|
describe "#delete_page" do
|
@@ -118,7 +216,7 @@ describe MediawikiApi::Client do
|
|
118
216
|
with(body: { format: "json", action: "createaccount", name: "Test", password: "qwe123" }).
|
119
217
|
to_return(body: { createaccount: body_base.merge({ result: "Success" }) }.to_json )
|
120
218
|
|
121
|
-
expect(subject.create_account("Test", "qwe123")).to
|
219
|
+
expect(subject.create_account("Test", "qwe123")).to include("result" => "Success")
|
122
220
|
end
|
123
221
|
|
124
222
|
context "when API returns NeedToken" do
|
@@ -139,7 +237,7 @@ describe MediawikiApi::Client do
|
|
139
237
|
end
|
140
238
|
|
141
239
|
it "creates an account" do
|
142
|
-
expect(subject.create_account("Test", "qwe123")).to
|
240
|
+
expect(subject.create_account("Test", "qwe123")).to include("result" => "Success")
|
143
241
|
end
|
144
242
|
|
145
243
|
it "sends second request with token and cookies" do
|
@@ -162,4 +260,34 @@ describe MediawikiApi::Client do
|
|
162
260
|
end
|
163
261
|
end
|
164
262
|
end
|
263
|
+
|
264
|
+
describe "#watch_page" do
|
265
|
+
before do
|
266
|
+
stub_request(:get, api_url).
|
267
|
+
with(query: { format: "json", action: "tokens", type: "watch" }).
|
268
|
+
to_return(body: { tokens: { watchtoken: "t123" } }.to_json )
|
269
|
+
@watch_req = stub_request(:post, api_url).
|
270
|
+
with(body: { format: "json", token: "t123", action: "watch", titles: "Test" })
|
271
|
+
end
|
272
|
+
|
273
|
+
it "sends a valid watch request" do
|
274
|
+
subject.watch_page("Test")
|
275
|
+
expect(@watch_req).to have_been_requested
|
276
|
+
end
|
277
|
+
end
|
278
|
+
|
279
|
+
describe "#unwatch_page" do
|
280
|
+
before do
|
281
|
+
stub_request(:get, api_url).
|
282
|
+
with(query: { format: "json", action: "tokens", type: "watch" }).
|
283
|
+
to_return(body: { tokens: { watchtoken: "t123" } }.to_json )
|
284
|
+
@watch_req = stub_request(:post, api_url).
|
285
|
+
with(body: { format: "json", token: "t123", action: "watch", titles: "Test", unwatch: "true" })
|
286
|
+
end
|
287
|
+
|
288
|
+
it "sends a valid unwatch request" do
|
289
|
+
subject.unwatch_page("Test")
|
290
|
+
expect(@watch_req).to have_been_requested
|
291
|
+
end
|
292
|
+
end
|
165
293
|
end
|
@@ -0,0 +1,90 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe MediawikiApi::Response do
|
4
|
+
let(:response) { MediawikiApi::Response.new(faraday_response, envelope) }
|
5
|
+
|
6
|
+
let(:faraday_response) { double("Faraday::Response", body: body) }
|
7
|
+
let(:body) { "{}" }
|
8
|
+
let(:response_object) { JSON.parse(body) }
|
9
|
+
let(:envelope) { [] }
|
10
|
+
|
11
|
+
describe "#data" do
|
12
|
+
subject { response.data }
|
13
|
+
|
14
|
+
context "with a JSON object response body" do
|
15
|
+
let(:body) { '{ "query": { "result": "success" } }' }
|
16
|
+
|
17
|
+
context "and no expected envelope" do
|
18
|
+
let(:envelope) { [] }
|
19
|
+
|
20
|
+
it { is_expected.to eq(response_object) }
|
21
|
+
end
|
22
|
+
|
23
|
+
context "and a single-level envelope" do
|
24
|
+
let(:envelope) { ["query"] }
|
25
|
+
let(:nested_object) { response_object["query"] }
|
26
|
+
|
27
|
+
it { is_expected.to eq(nested_object) }
|
28
|
+
end
|
29
|
+
|
30
|
+
context "and a multi-level envelope" do
|
31
|
+
let(:envelope) { ["query", "result"] }
|
32
|
+
let(:nested_object) { response_object["query"]["result"] }
|
33
|
+
|
34
|
+
it { is_expected.to eq(nested_object) }
|
35
|
+
end
|
36
|
+
|
37
|
+
context "and a multi-level envelope that doesn't completely match" do
|
38
|
+
let(:envelope) { ["query", "something"] }
|
39
|
+
let(:partially_nested_object) { response_object["query"] }
|
40
|
+
|
41
|
+
it { is_expected.to eq(partially_nested_object) }
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
context "with a JSON array response body" do
|
46
|
+
let(:body) { '[ "something" ]' }
|
47
|
+
|
48
|
+
context "with any expected envelope" do
|
49
|
+
let(:envelope) { ["what", "ever"] }
|
50
|
+
|
51
|
+
it { is_expected.to eq(response_object) }
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
describe "#warnings" do
|
57
|
+
subject { response.warnings }
|
58
|
+
|
59
|
+
context "where the response contains no warnings" do
|
60
|
+
let(:body) { '{ "query": { "result": "success" } }' }
|
61
|
+
|
62
|
+
it { is_expected.to be_empty }
|
63
|
+
end
|
64
|
+
|
65
|
+
context "where the response contains warnings" do
|
66
|
+
let(:body) { '{ "warnings": { "main": { "*": "sorta bad message" } } }' }
|
67
|
+
|
68
|
+
it { is_expected.to_not be_empty }
|
69
|
+
it { is_expected.to include("sorta bad message") }
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
describe "#warnings?" do
|
74
|
+
subject { response.warnings? }
|
75
|
+
|
76
|
+
before { allow(response).to receive(:warnings) { warnings } }
|
77
|
+
|
78
|
+
context "where there are warnings" do
|
79
|
+
let(:warnings) { ["warning"] }
|
80
|
+
|
81
|
+
it { is_expected.to be(true) }
|
82
|
+
end
|
83
|
+
|
84
|
+
context "where there are no warnings" do
|
85
|
+
let(:warnings) { [] }
|
86
|
+
|
87
|
+
it { is_expected.to be(false) }
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
data/spec/spec_helper.rb
CHANGED
@@ -1,16 +1,2 @@
|
|
1
|
-
require "
|
2
|
-
require
|
3
|
-
require "mediawiki_api"
|
4
|
-
|
5
|
-
module TestHelpers
|
6
|
-
def api_url
|
7
|
-
"http://localhost/api.php"
|
8
|
-
end
|
9
|
-
def index_url
|
10
|
-
"http://localhost/w/index.php"
|
11
|
-
end
|
12
|
-
end
|
13
|
-
|
14
|
-
RSpec.configure do |conf|
|
15
|
-
conf.include TestHelpers
|
16
|
-
end
|
1
|
+
require "bundler/setup"
|
2
|
+
Bundler.require(:default, :development)
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module MediawikiApi::RequestHelpers
|
2
|
+
def api_url
|
3
|
+
"http://localhost/api.php"
|
4
|
+
end
|
5
|
+
|
6
|
+
def index_url
|
7
|
+
"http://localhost/w/index.php"
|
8
|
+
end
|
9
|
+
|
10
|
+
def mock_token
|
11
|
+
"token123"
|
12
|
+
end
|
13
|
+
|
14
|
+
def stub_api_request(method, params)
|
15
|
+
stub_request(method, api_url).
|
16
|
+
with((method == :post ? :body : :query) => params.merge(format: "json"))
|
17
|
+
end
|
18
|
+
|
19
|
+
def stub_action_request(action, params = {})
|
20
|
+
method = params.delete(:http_method) || :post
|
21
|
+
|
22
|
+
stub_api_request(method, params.merge(action: action, token: mock_token))
|
23
|
+
end
|
24
|
+
|
25
|
+
def stub_token_request(type)
|
26
|
+
stub_api_request(:get, action: "tokens", type: type).
|
27
|
+
to_return(body: { tokens: { "#{type}token" => mock_token } }.to_json)
|
28
|
+
end
|
29
|
+
end
|
metadata
CHANGED
@@ -1,18 +1,19 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: mediawiki_api
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Amir Aharoni
|
8
8
|
- Chris McMahon
|
9
|
+
- Dan Duvall
|
9
10
|
- Jeff Hall
|
10
11
|
- Juliusz Gonera
|
11
12
|
- Zeljko Filipin
|
12
13
|
autorequire:
|
13
14
|
bindir: bin
|
14
15
|
cert_chain: []
|
15
|
-
date: 2014-
|
16
|
+
date: 2014-08-06 00:00:00.000000000 Z
|
16
17
|
dependencies:
|
17
18
|
- !ruby/object:Gem::Dependency
|
18
19
|
name: faraday
|
@@ -126,6 +127,7 @@ description: Uses adapter-agnostic Faraday gem to talk to MediaWiki API.
|
|
126
127
|
email:
|
127
128
|
- amir.aharoni@mail.huji.ac.il
|
128
129
|
- cmcmahon@wikimedia.org
|
130
|
+
- dduvall@wikimedia.org
|
129
131
|
- jhall@wikimedia.org
|
130
132
|
- jgonera@wikimedia.org
|
131
133
|
- zeljko.filipin@gmail.com
|
@@ -133,19 +135,23 @@ executables: []
|
|
133
135
|
extensions: []
|
134
136
|
extra_rdoc_files: []
|
135
137
|
files:
|
138
|
+
- ".gitignore"
|
136
139
|
- ".gitreview"
|
137
140
|
- ".rspec"
|
138
141
|
- CREDITS
|
139
142
|
- Gemfile
|
140
|
-
- Gemfile.lock
|
141
143
|
- LICENSE.txt
|
142
144
|
- README.md
|
143
145
|
- lib/mediawiki_api.rb
|
144
146
|
- lib/mediawiki_api/client.rb
|
147
|
+
- lib/mediawiki_api/exceptions.rb
|
148
|
+
- lib/mediawiki_api/response.rb
|
145
149
|
- lib/mediawiki_api/version.rb
|
146
150
|
- mediawiki_api.gemspec
|
147
151
|
- spec/client_spec.rb
|
152
|
+
- spec/response_spec.rb
|
148
153
|
- spec/spec_helper.rb
|
154
|
+
- spec/support/request_helpers.rb
|
149
155
|
homepage: https://github.com/wikimedia/mediawiki-ruby-api
|
150
156
|
licenses:
|
151
157
|
- GPL-2
|
@@ -166,10 +172,12 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
166
172
|
version: '0'
|
167
173
|
requirements: []
|
168
174
|
rubyforge_project:
|
169
|
-
rubygems_version: 2.
|
175
|
+
rubygems_version: 2.2.2
|
170
176
|
signing_key:
|
171
177
|
specification_version: 4
|
172
178
|
summary: A library for interacting with MediaWiki API from Ruby.
|
173
179
|
test_files:
|
174
180
|
- spec/client_spec.rb
|
181
|
+
- spec/response_spec.rb
|
175
182
|
- spec/spec_helper.rb
|
183
|
+
- spec/support/request_helpers.rb
|
data/Gemfile.lock
DELETED
@@ -1,54 +0,0 @@
|
|
1
|
-
PATH
|
2
|
-
remote: .
|
3
|
-
specs:
|
4
|
-
mediawiki_api (0.1.4)
|
5
|
-
faraday (~> 0.9, >= 0.9.0)
|
6
|
-
faraday-cookie_jar (~> 0.0, >= 0.0.6)
|
7
|
-
|
8
|
-
GEM
|
9
|
-
remote: https://rubygems.org/
|
10
|
-
specs:
|
11
|
-
addressable (2.3.6)
|
12
|
-
crack (0.4.2)
|
13
|
-
safe_yaml (~> 1.0.0)
|
14
|
-
diff-lcs (1.2.5)
|
15
|
-
domain_name (0.5.19)
|
16
|
-
unf (>= 0.0.5, < 1.0.0)
|
17
|
-
faraday (0.9.0)
|
18
|
-
multipart-post (>= 1.2, < 3)
|
19
|
-
faraday-cookie_jar (0.0.6)
|
20
|
-
faraday (>= 0.7.4)
|
21
|
-
http-cookie (~> 1.0.0)
|
22
|
-
http-cookie (1.0.2)
|
23
|
-
domain_name (~> 0.5)
|
24
|
-
multipart-post (2.0.0)
|
25
|
-
rake (0.9.6)
|
26
|
-
rspec (3.0.0)
|
27
|
-
rspec-core (~> 3.0.0)
|
28
|
-
rspec-expectations (~> 3.0.0)
|
29
|
-
rspec-mocks (~> 3.0.0)
|
30
|
-
rspec-core (3.0.2)
|
31
|
-
rspec-support (~> 3.0.0)
|
32
|
-
rspec-expectations (3.0.2)
|
33
|
-
diff-lcs (>= 1.2.0, < 2.0)
|
34
|
-
rspec-support (~> 3.0.0)
|
35
|
-
rspec-mocks (3.0.2)
|
36
|
-
rspec-support (~> 3.0.0)
|
37
|
-
rspec-support (3.0.2)
|
38
|
-
safe_yaml (1.0.3)
|
39
|
-
unf (0.1.4)
|
40
|
-
unf_ext
|
41
|
-
unf_ext (0.0.6)
|
42
|
-
webmock (1.18.0)
|
43
|
-
addressable (>= 2.3.6)
|
44
|
-
crack (>= 0.3.2)
|
45
|
-
|
46
|
-
PLATFORMS
|
47
|
-
ruby
|
48
|
-
|
49
|
-
DEPENDENCIES
|
50
|
-
bundler (~> 1.3)
|
51
|
-
mediawiki_api!
|
52
|
-
rake (~> 0)
|
53
|
-
rspec (~> 3.0, >= 3.0.0)
|
54
|
-
webmock (~> 1.17, >= 1.17.2)
|