discourse_api 0.44.0 → 0.47.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/.github/workflows/ci.yml +13 -17
- data/.gitignore +0 -19
- data/CHANGELOG.md +38 -7
- data/discourse_api.gemspec +5 -5
- data/examples/bookmark_topic.rb +15 -0
- data/examples/category.rb +3 -0
- data/examples/invite_users.rb +20 -2
- data/examples/manage_api_keys.rb +42 -0
- data/examples/notifications.rb +18 -0
- data/examples/topic_lists.rb +3 -0
- data/lib/discourse_api/api/api_key.rb +15 -9
- data/lib/discourse_api/api/categories.rb +41 -13
- data/lib/discourse_api/api/invite.rb +67 -2
- data/lib/discourse_api/api/notifications.rb +5 -2
- data/lib/discourse_api/api/private_messages.rb +10 -3
- data/lib/discourse_api/api/search.rb +1 -1
- data/lib/discourse_api/api/topics.rb +37 -2
- data/lib/discourse_api/api/users.rb +1 -1
- data/lib/discourse_api/client.rb +13 -1
- data/lib/discourse_api/error.rb +3 -0
- data/lib/discourse_api/version.rb +1 -1
- data/spec/discourse_api/api/api_key_spec.rb +59 -35
- data/spec/discourse_api/api/categories_spec.rb +90 -0
- data/spec/discourse_api/api/invite_spec.rb +123 -0
- data/spec/discourse_api/api/private_messages_spec.rb +4 -4
- data/spec/discourse_api/api/search_spec.rb +2 -2
- data/spec/discourse_api/api/topics_spec.rb +71 -0
- data/spec/discourse_api/client_spec.rb +15 -0
- data/spec/fixtures/api_key.json +12 -0
- data/spec/fixtures/list_api_keys.json +14 -0
- data/spec/fixtures/notification_success.json +3 -0
- data/spec/fixtures/retrieve_invite.json +116 -0
- data/spec/fixtures/top.json +108 -0
- data/spec/fixtures/topic_posts.json +1 -0
- metadata +24 -13
- data/spec/fixtures/api.json +0 -12
- data/spec/fixtures/generate_master_key.json +0 -7
@@ -3,17 +3,82 @@ module DiscourseApi
|
|
3
3
|
module API
|
4
4
|
module Invite
|
5
5
|
def invite_user(params = {})
|
6
|
-
|
6
|
+
args = API.params(params)
|
7
|
+
.optional(
|
8
|
+
:email,
|
9
|
+
:skip_email,
|
10
|
+
:custom_message,
|
11
|
+
:max_redemptions_allowed,
|
12
|
+
:topic_id,
|
13
|
+
:group_ids,
|
14
|
+
:expires_at
|
15
|
+
).to_h
|
16
|
+
|
17
|
+
post("/invites", args)
|
7
18
|
end
|
8
19
|
|
20
|
+
# TODO: Deprecated. Remove after 20220506
|
9
21
|
def invite_user_to_topic(params = {})
|
10
|
-
|
22
|
+
deprecated(__method__, 'invite_to_topic')
|
23
|
+
invite_to_topic(params[:topic_id], params)
|
24
|
+
end
|
25
|
+
|
26
|
+
def invite_to_topic(topic_id, params = {})
|
27
|
+
args = API.params(params)
|
28
|
+
.optional(
|
29
|
+
:email,
|
30
|
+
:user,
|
31
|
+
:group_ids,
|
32
|
+
:custom_message
|
33
|
+
).to_h
|
34
|
+
|
35
|
+
post("/t/#{topic_id}/invite", args)
|
36
|
+
end
|
37
|
+
|
38
|
+
def retrieve_invite(params = {})
|
39
|
+
args = API.params(params).required(:email).to_h
|
40
|
+
|
41
|
+
response = get("invites/retrieve.json", args)
|
42
|
+
|
43
|
+
response.body
|
11
44
|
end
|
12
45
|
|
13
46
|
# requires this plugin => https://github.com/discourse/discourse-invite-tokens
|
14
47
|
def disposable_tokens(params = {})
|
15
48
|
post("/invite-token/generate", params)
|
16
49
|
end
|
50
|
+
|
51
|
+
def update_invite(invite_id, params = {})
|
52
|
+
args = API.params(params)
|
53
|
+
.optional(
|
54
|
+
:topic_id,
|
55
|
+
:group_ids,
|
56
|
+
:group_names,
|
57
|
+
:email,
|
58
|
+
:send_email,
|
59
|
+
:custom_message,
|
60
|
+
:max_redemptions_allowed,
|
61
|
+
:expires_at
|
62
|
+
).to_h
|
63
|
+
|
64
|
+
put("invites/#{invite_id}", args)
|
65
|
+
end
|
66
|
+
|
67
|
+
def destroy_all_expired_invites
|
68
|
+
post("invites/destroy-all-expired")
|
69
|
+
end
|
70
|
+
|
71
|
+
def resend_all_invites
|
72
|
+
post("invites/reinvite-all")
|
73
|
+
end
|
74
|
+
|
75
|
+
def resend_invite(email)
|
76
|
+
post("invites/reinvite", { "email": email })
|
77
|
+
end
|
78
|
+
|
79
|
+
def destroy_invite(invite_id)
|
80
|
+
delete("/invites", { id: invite_id })
|
81
|
+
end
|
17
82
|
end
|
18
83
|
end
|
19
84
|
end
|
@@ -2,8 +2,11 @@
|
|
2
2
|
module DiscourseApi
|
3
3
|
module API
|
4
4
|
module Notifications
|
5
|
-
def notifications
|
6
|
-
|
5
|
+
def notifications(params = {})
|
6
|
+
params = API.params(params)
|
7
|
+
.optional(:username, :recent, :limit, :offset, :filter)
|
8
|
+
|
9
|
+
response = get('/notifications.json', params)
|
7
10
|
response[:body]
|
8
11
|
end
|
9
12
|
end
|
@@ -3,13 +3,20 @@ module DiscourseApi
|
|
3
3
|
module API
|
4
4
|
module PrivateMessages
|
5
5
|
|
6
|
-
# :
|
6
|
+
# TODO: Deprecated. Remove after 20220628
|
7
|
+
def create_private_message(args = {})
|
8
|
+
deprecated(__method__, 'create_pm')
|
9
|
+
args[:target_recipients] = args.delete :target_usernames
|
10
|
+
create_pm(args.to_h)
|
11
|
+
end
|
12
|
+
|
13
|
+
# :target_recipients REQUIRED comma separated list of usernames
|
7
14
|
# :category OPTIONAL name of category, not ID
|
8
15
|
# :created_at OPTIONAL seconds since epoch.
|
9
|
-
def
|
16
|
+
def create_pm(args = {})
|
10
17
|
args[:archetype] = 'private_message'
|
11
18
|
args = API.params(args)
|
12
|
-
.required(:title, :raw, :
|
19
|
+
.required(:title, :raw, :target_recipients, :archetype)
|
13
20
|
.optional(:category, :created_at, :api_username)
|
14
21
|
post("/posts", args.to_h)
|
15
22
|
end
|
@@ -12,7 +12,7 @@ module DiscourseApi
|
|
12
12
|
raise ArgumentError.new("#{term} is required but not specified") unless term
|
13
13
|
raise ArgumentError.new("#{term} is required but not specified") unless !term.empty?
|
14
14
|
|
15
|
-
response = get('/search
|
15
|
+
response = get('/search', options.merge(q: term))
|
16
16
|
response[:body]
|
17
17
|
end
|
18
18
|
end
|
@@ -29,6 +29,11 @@ module DiscourseApi
|
|
29
29
|
response[:body]['topic_list']['topics']
|
30
30
|
end
|
31
31
|
|
32
|
+
def top_topics(params = {})
|
33
|
+
response = get("/top.json", params)
|
34
|
+
response[:body]['topic_list']['topics']
|
35
|
+
end
|
36
|
+
|
32
37
|
def new_topics(params = {})
|
33
38
|
response = get("/new.json", params)
|
34
39
|
response[:body]['topic_list']['topics']
|
@@ -69,15 +74,45 @@ module DiscourseApi
|
|
69
74
|
delete("/t/#{id}.json")
|
70
75
|
end
|
71
76
|
|
72
|
-
def topic_posts(topic_id, post_ids = [])
|
77
|
+
def topic_posts(topic_id, post_ids = [], params = {})
|
78
|
+
params = API.params(params)
|
79
|
+
.optional(:asc,
|
80
|
+
:filter,
|
81
|
+
:include_raw,
|
82
|
+
:include_suggested,
|
83
|
+
:post_number,
|
84
|
+
:username_filters,
|
85
|
+
)
|
86
|
+
|
73
87
|
url = ["/t/#{topic_id}/posts.json"]
|
74
88
|
if post_ids.count > 0
|
75
89
|
url.push('?')
|
76
90
|
url.push(post_ids.map { |id| "post_ids[]=#{id}" }.join('&'))
|
77
91
|
end
|
78
|
-
response = get(url.join)
|
92
|
+
response = get(url.join, params)
|
79
93
|
response[:body]
|
80
94
|
end
|
95
|
+
|
96
|
+
def change_owner(topic_id, params = {})
|
97
|
+
params = API.params(params)
|
98
|
+
.required(:username, :post_ids)
|
99
|
+
|
100
|
+
post("/t/#{topic_id}/change-owner.json", params)
|
101
|
+
end
|
102
|
+
|
103
|
+
def topic_set_user_notification_level(topic_id, params)
|
104
|
+
params = API.params(params)
|
105
|
+
.required(:notification_level)
|
106
|
+
post("/t/#{topic_id}/notifications", params)
|
107
|
+
end
|
108
|
+
|
109
|
+
def bookmark_topic(topic_id)
|
110
|
+
put("/t/#{topic_id}/bookmark.json")
|
111
|
+
end
|
112
|
+
|
113
|
+
def remove_topic_bookmark(topic_id)
|
114
|
+
put("/t/#{topic_id}/remove_bookmarks.json")
|
115
|
+
end
|
81
116
|
end
|
82
117
|
end
|
83
118
|
end
|
@@ -52,7 +52,7 @@ module DiscourseApi
|
|
52
52
|
def create_user(args)
|
53
53
|
args = API.params(args)
|
54
54
|
.required(:name, :email, :password, :username)
|
55
|
-
.optional(:active, :staged, :user_fields)
|
55
|
+
.optional(:active, :approved, :staged, :user_fields)
|
56
56
|
.to_h
|
57
57
|
post("/users", args)
|
58
58
|
end
|
data/lib/discourse_api/client.rb
CHANGED
@@ -29,7 +29,9 @@ module DiscourseApi
|
|
29
29
|
class Client
|
30
30
|
attr_accessor :api_key
|
31
31
|
attr_accessor :basic_auth
|
32
|
-
attr_reader :host, :api_username
|
32
|
+
attr_reader :host, :api_username, :timeout
|
33
|
+
|
34
|
+
DEFAULT_TIMEOUT = 30
|
33
35
|
|
34
36
|
include DiscourseApi::API::Categories
|
35
37
|
include DiscourseApi::API::Search
|
@@ -60,6 +62,11 @@ module DiscourseApi
|
|
60
62
|
@use_relative = check_subdirectory(host)
|
61
63
|
end
|
62
64
|
|
65
|
+
def timeout=(timeout)
|
66
|
+
@timeout = timeout
|
67
|
+
@connection.options.timeout = timeout if @connection
|
68
|
+
end
|
69
|
+
|
63
70
|
def api_username=(api_username)
|
64
71
|
@api_username = api_username
|
65
72
|
@connection.headers['Api-Username'] = api_username unless @connection.nil?
|
@@ -68,6 +75,9 @@ module DiscourseApi
|
|
68
75
|
def connection_options
|
69
76
|
@connection_options ||= {
|
70
77
|
url: @host,
|
78
|
+
request: {
|
79
|
+
timeout: @timeout || DEFAULT_TIMEOUT
|
80
|
+
},
|
71
81
|
headers: {
|
72
82
|
accept: 'application/json',
|
73
83
|
user_agent: user_agent,
|
@@ -158,6 +168,8 @@ module DiscourseApi
|
|
158
168
|
response.env
|
159
169
|
rescue Faraday::ClientError, JSON::ParserError
|
160
170
|
raise DiscourseApi::Error
|
171
|
+
rescue Faraday::ConnectionFailed
|
172
|
+
raise DiscourseApi::Timeout
|
161
173
|
end
|
162
174
|
|
163
175
|
def handle_error(response)
|
data/lib/discourse_api/error.rb
CHANGED
@@ -10,77 +10,101 @@ describe DiscourseApi::API::ApiKey do
|
|
10
10
|
)
|
11
11
|
}
|
12
12
|
|
13
|
-
describe "#
|
13
|
+
describe "#list_api_keys" do
|
14
14
|
before do
|
15
|
-
url = "#{host}/admin/api
|
16
|
-
stub_get(url).to_return(body: fixture("
|
15
|
+
url = "#{host}/admin/api/keys"
|
16
|
+
stub_get(url).to_return(body: fixture("list_api_keys.json"),
|
17
17
|
headers: { content_type: "application/json" })
|
18
18
|
end
|
19
19
|
|
20
20
|
it "requests the correct resource" do
|
21
|
-
subject.
|
22
|
-
url = "#{host}/admin/api
|
21
|
+
subject.list_api_keys
|
22
|
+
url = "#{host}/admin/api/keys"
|
23
23
|
expect(a_get(url)).to have_been_made
|
24
24
|
end
|
25
25
|
|
26
26
|
it "returns the requested api keys" do
|
27
|
-
|
28
|
-
expect(
|
29
|
-
expect(
|
30
|
-
expect(
|
27
|
+
keys = subject.list_api_keys
|
28
|
+
expect(keys["keys"]).to be_an Array
|
29
|
+
expect(keys["keys"].first).to be_a Hash
|
30
|
+
expect(keys["keys"].first).to have_key('key')
|
31
31
|
end
|
32
32
|
end
|
33
33
|
|
34
|
-
describe "#
|
34
|
+
describe "#create_api_key" do
|
35
35
|
before do
|
36
|
-
url = "#{host}/admin/api/
|
37
|
-
stub_post(url).to_return(body: fixture("
|
36
|
+
url = "#{host}/admin/api/keys"
|
37
|
+
stub_post(url).to_return(body: fixture("api_key.json"),
|
38
38
|
headers: { content_type: "application/json" })
|
39
39
|
end
|
40
40
|
|
41
|
-
it "
|
42
|
-
|
43
|
-
|
44
|
-
expect(
|
45
|
-
|
41
|
+
it "requests the correct resource" do
|
42
|
+
subject.create_api_key(key: { username: 'robin' })
|
43
|
+
url = "#{host}/admin/api/keys"
|
44
|
+
expect(a_post(url)).to have_been_made
|
45
|
+
end
|
46
|
+
|
47
|
+
it "returns the generated api key" do
|
48
|
+
api_key = subject.create_api_key(key: { username: 'robin' })
|
49
|
+
expect(api_key).to be_a Hash
|
50
|
+
expect(api_key['key']).to have_key('key')
|
46
51
|
end
|
47
52
|
end
|
48
53
|
|
49
54
|
describe "#revoke_api_key" do
|
50
55
|
before do
|
51
|
-
url = "#{host}/admin/api/
|
52
|
-
|
53
|
-
|
56
|
+
url = "#{host}/admin/api/keys/10/revoke"
|
57
|
+
stub_post(url).to_return(body: fixture("api_key.json"),
|
58
|
+
headers: { content_type: "application/json" })
|
54
59
|
end
|
55
60
|
|
56
61
|
it "requests the correct resource" do
|
57
62
|
subject.revoke_api_key(10)
|
58
|
-
url = "#{host}/admin/api/
|
59
|
-
expect(
|
63
|
+
url = "#{host}/admin/api/keys/10/revoke"
|
64
|
+
expect(a_post(url)).to have_been_made
|
60
65
|
end
|
61
66
|
|
62
|
-
it "returns
|
63
|
-
|
64
|
-
expect(
|
67
|
+
it "returns the api key" do
|
68
|
+
api_key = subject.revoke_api_key(10)
|
69
|
+
expect(api_key['key']).to have_key('key')
|
65
70
|
end
|
66
71
|
end
|
67
72
|
|
68
|
-
describe "#
|
73
|
+
describe "#undo_revoke_api_key" do
|
69
74
|
before do
|
70
|
-
url = "#{host}/admin/api/
|
71
|
-
|
72
|
-
|
75
|
+
url = "#{host}/admin/api/keys/10/undo-revoke"
|
76
|
+
stub_post(url).to_return(body: fixture("api_key.json"),
|
77
|
+
headers: { content_type: "application/json" })
|
73
78
|
end
|
74
79
|
|
75
80
|
it "requests the correct resource" do
|
76
|
-
subject.
|
77
|
-
url = "#{host}/admin/api/
|
78
|
-
expect(
|
81
|
+
subject.undo_revoke_api_key(10)
|
82
|
+
url = "#{host}/admin/api/keys/10/undo-revoke"
|
83
|
+
expect(a_post(url)).to have_been_made
|
79
84
|
end
|
80
85
|
|
81
|
-
it "returns the
|
82
|
-
|
83
|
-
expect(
|
86
|
+
it "returns the api key" do
|
87
|
+
api_key = subject.undo_revoke_api_key(10)
|
88
|
+
expect(api_key['key']).to have_key('key')
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
describe "#delete_api_key" do
|
93
|
+
before do
|
94
|
+
url = "#{host}/admin/api/keys/10"
|
95
|
+
stub_delete(url).to_return(body: '{"success": "OK"}',
|
96
|
+
headers: { content_type: "application/json" })
|
97
|
+
end
|
98
|
+
|
99
|
+
it "requests the correct resource" do
|
100
|
+
subject.delete_api_key(10)
|
101
|
+
url = "#{host}/admin/api/keys/10"
|
102
|
+
expect(a_delete(url)).to have_been_made
|
103
|
+
end
|
104
|
+
|
105
|
+
it "returns 200" do
|
106
|
+
response = subject.delete_api_key(10)
|
107
|
+
expect(response['status']).to eq(200)
|
84
108
|
end
|
85
109
|
end
|
86
110
|
end
|
@@ -28,6 +28,25 @@ describe DiscourseApi::API::Categories do
|
|
28
28
|
end
|
29
29
|
end
|
30
30
|
|
31
|
+
describe "#categories_full" do
|
32
|
+
before do
|
33
|
+
stub_get("#{host}/categories.json")
|
34
|
+
.to_return(body: fixture("categories.json"), headers: { content_type: "application/json" })
|
35
|
+
end
|
36
|
+
|
37
|
+
it "requests the correct resource" do
|
38
|
+
subject.categories
|
39
|
+
expect(a_get("#{host}/categories.json")).to have_been_made
|
40
|
+
end
|
41
|
+
|
42
|
+
it "returns the entire categories response" do
|
43
|
+
categories = subject.categories_full
|
44
|
+
expect(categories).to be_a Hash
|
45
|
+
expect(categories).to have_key 'category_list'
|
46
|
+
expect(categories).to have_key 'featured_users'
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
31
50
|
describe '#category_latest_topics' do
|
32
51
|
before do
|
33
52
|
stub_get("#{host}/c/category-slug/l/latest.json")
|
@@ -40,6 +59,20 @@ describe DiscourseApi::API::Categories do
|
|
40
59
|
end
|
41
60
|
end
|
42
61
|
|
62
|
+
describe '#category_latest_topics_full' do
|
63
|
+
before do
|
64
|
+
stub_get("#{host}/c/category-slug/l/latest.json")
|
65
|
+
.to_return(body: fixture("category_latest_topics.json"), headers: { content_type: "application/json" })
|
66
|
+
end
|
67
|
+
|
68
|
+
it "returns the entire latest topics in a category response" do
|
69
|
+
latest_topics = subject.category_latest_topics_full(category_slug: 'category-slug')
|
70
|
+
expect(latest_topics).to be_a Hash
|
71
|
+
expect(latest_topics).to have_key 'topic_list'
|
72
|
+
expect(latest_topics).to have_key 'users'
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
43
76
|
describe '#category_top_topics' do
|
44
77
|
before do
|
45
78
|
stub_get("#{host}/c/category-slug/l/top.json")
|
@@ -55,6 +88,23 @@ describe DiscourseApi::API::Categories do
|
|
55
88
|
end
|
56
89
|
end
|
57
90
|
|
91
|
+
describe '#category_top_topics_full' do
|
92
|
+
before do
|
93
|
+
stub_get("#{host}/c/category-slug/l/top.json")
|
94
|
+
.to_return(
|
95
|
+
body: fixture("category_topics.json"),
|
96
|
+
headers: { content_type: "application/json" }
|
97
|
+
)
|
98
|
+
end
|
99
|
+
|
100
|
+
it "returns the entire top topics in a category response" do
|
101
|
+
topics = subject.category_top_topics_full('category-slug')
|
102
|
+
expect(topics).to be_a Hash
|
103
|
+
expect(topics).to have_key 'topic_list'
|
104
|
+
expect(topics).to have_key 'users'
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
58
108
|
describe '#category_new_topics' do
|
59
109
|
before do
|
60
110
|
stub_get("#{host}/c/category-slug/l/new.json")
|
@@ -70,6 +120,23 @@ describe DiscourseApi::API::Categories do
|
|
70
120
|
end
|
71
121
|
end
|
72
122
|
|
123
|
+
describe '#category_new_topics_full' do
|
124
|
+
before do
|
125
|
+
stub_get("#{host}/c/category-slug/l/new.json")
|
126
|
+
.to_return(
|
127
|
+
body: fixture("category_topics.json"),
|
128
|
+
headers: { content_type: "application/json" }
|
129
|
+
)
|
130
|
+
end
|
131
|
+
|
132
|
+
it "returns the new topics in a category" do
|
133
|
+
topics = subject.category_new_topics_full('category-slug')
|
134
|
+
expect(topics).to be_a Hash
|
135
|
+
expect(topics).to have_key 'topic_list'
|
136
|
+
expect(topics).to have_key 'users'
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
73
140
|
describe '#category_new_category' do
|
74
141
|
before do
|
75
142
|
stub_post("#{host}/categories")
|
@@ -85,4 +152,27 @@ describe DiscourseApi::API::Categories do
|
|
85
152
|
end
|
86
153
|
end
|
87
154
|
|
155
|
+
describe "#category_set_user_notification" do
|
156
|
+
before do
|
157
|
+
stub_post("#{host}/category/1/notifications").to_return(body: fixture("notification_success.json"), headers: { content_type: "application/json" })
|
158
|
+
end
|
159
|
+
|
160
|
+
it "makes the post request" do
|
161
|
+
response = subject.category_set_user_notification(id: 1, notification_level: 3)
|
162
|
+
expect(a_post("#{host}/category/1/notifications")).to have_been_made
|
163
|
+
expect(response['success']).to eq('OK')
|
164
|
+
end
|
165
|
+
end
|
166
|
+
|
167
|
+
describe "#category_set_user_notification_level" do
|
168
|
+
before do
|
169
|
+
stub_post("#{host}/category/1/notifications").to_return(body: fixture("notification_success.json"), headers: { content_type: "application/json" })
|
170
|
+
end
|
171
|
+
|
172
|
+
it "makes the post request" do
|
173
|
+
response = subject.category_set_user_notification_level(1, notification_level: 3)
|
174
|
+
expect(a_post("#{host}/category/1/notifications").with(body: "notification_level=3")).to have_been_made
|
175
|
+
expect(response['success']).to eq('OK')
|
176
|
+
end
|
177
|
+
end
|
88
178
|
end
|