discourse_api 0.45.0 → 0.48.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (37) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ci.yml +13 -17
  3. data/.gitignore +0 -19
  4. data/CHANGELOG.md +39 -0
  5. data/discourse_api.gemspec +4 -4
  6. data/examples/bookmark_topic.rb +15 -0
  7. data/examples/category.rb +3 -0
  8. data/examples/invite_users.rb +21 -3
  9. data/examples/notifications.rb +18 -0
  10. data/examples/topic_lists.rb +3 -0
  11. data/lib/discourse_api/api/categories.rb +37 -11
  12. data/lib/discourse_api/api/dashboard.rb +2 -2
  13. data/lib/discourse_api/api/groups.rb +15 -4
  14. data/lib/discourse_api/api/invite.rb +67 -2
  15. data/lib/discourse_api/api/notifications.rb +5 -2
  16. data/lib/discourse_api/api/private_messages.rb +10 -3
  17. data/lib/discourse_api/api/search.rb +1 -1
  18. data/lib/discourse_api/api/site_settings.rb +3 -3
  19. data/lib/discourse_api/api/topics.rb +30 -2
  20. data/lib/discourse_api/api/users.rb +4 -0
  21. data/lib/discourse_api/client.rb +13 -1
  22. data/lib/discourse_api/error.rb +3 -0
  23. data/lib/discourse_api/single_sign_on.rb +6 -6
  24. data/lib/discourse_api/version.rb +1 -1
  25. data/spec/discourse_api/api/categories_spec.rb +90 -0
  26. data/spec/discourse_api/api/groups_spec.rb +29 -0
  27. data/spec/discourse_api/api/invite_spec.rb +123 -0
  28. data/spec/discourse_api/api/private_messages_spec.rb +4 -4
  29. data/spec/discourse_api/api/search_spec.rb +2 -2
  30. data/spec/discourse_api/api/topics_spec.rb +69 -3
  31. data/spec/discourse_api/api/users_spec.rb +14 -0
  32. data/spec/discourse_api/client_spec.rb +21 -0
  33. data/spec/fixtures/notification_success.json +3 -0
  34. data/spec/fixtures/retrieve_invite.json +116 -0
  35. data/spec/fixtures/top.json +108 -0
  36. data/spec/fixtures/topic_posts.json +1 -0
  37. metadata +17 -7
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 82cd748487d5d986f8327b702088ea4ea22c43cae311c2634dfa5d9cf4c4059f
4
- data.tar.gz: a4fd68101134895f5071443f221a2327e9fe9e4a0a79df0d53a620c7e08d685d
3
+ metadata.gz: '047378d84854b5dae025d57550c071bf9944257a4048bd1be5b51b389e995dc3'
4
+ data.tar.gz: af75df86361c631aba91dc685c26e2f0d392597fbd561ef93b628f319f907457
5
5
  SHA512:
6
- metadata.gz: 42546f17e134cd1e4ac6be81c8fdb53cf82461d60a73859dd757c2ec69e1128127f33a131e45dfec3d910012702256a173a7a1afd2a9873dc3d8a3e220c4af1a
7
- data.tar.gz: 990fc89fd2a4935704e1512b858872b41ed7a6b7ba3ef28f583ad272e0675daff74f61b9a441632a8dc92c76892b8c507e53afce356317403c7c67f31a4fab18
6
+ metadata.gz: 7a435c702c930231c817d8192a20ddd859c3102cb3a9928ad4402dc1c019330d4dca79bd407f00ffa9e9038cebe7a0c138c081a019f4373711bfea67104c1a00
7
+ data.tar.gz: 8664b7d0efa3982e223a61af2a7d28ea2744100fdf1f3dcef99c9c1c3f7c59481c264ac04092c9b0c4c6a5f7559bd1fa99c4010d69fb1451f1a7875678a06d19
@@ -5,8 +5,7 @@ on:
5
5
  push:
6
6
  branches:
7
7
  - master
8
- tags:
9
- - v*
8
+ - main
10
9
 
11
10
  jobs:
12
11
  build:
@@ -18,30 +17,25 @@ jobs:
18
17
  - 2.5
19
18
  - 2.6
20
19
  - 2.7
20
+ - 3.0
21
21
 
22
22
  steps:
23
- - uses: actions/checkout@v1
23
+ - uses: actions/checkout@v2
24
24
 
25
25
  - name: Setup ruby
26
- uses: actions/setup-ruby@v1
26
+ uses: ruby/setup-ruby@v1
27
27
  with:
28
28
  ruby-version: ${{ matrix.ruby }}
29
- architecture: 'x64'
30
-
31
- - name: Setup bundler
32
- run: gem install bundler
33
-
34
- - name: Setup gems
35
- run: bundle install
29
+ bundler-cache: true
36
30
 
37
- - name: Rubocop
31
+ - name: Lint
38
32
  run: bundle exec rubocop
39
33
 
40
- - name: RSpec
41
- run: bundle exec rspec
34
+ - name: Tests
35
+ run: bundle exec rake test
42
36
 
43
37
  publish:
44
- if: contains(github.ref, 'refs/tags/v')
38
+ if: github.event_name == 'push' && (github.ref == 'refs/heads/main' || github.ref == 'refs/heads/master')
45
39
  needs: build
46
40
  runs-on: ubuntu-latest
47
41
 
@@ -49,6 +43,8 @@ jobs:
49
43
  - uses: actions/checkout@v2
50
44
 
51
45
  - name: Release Gem
52
- uses: CvX/publish-rubygems-action@master
46
+ uses: discourse/publish-rubygems-action@v2-beta
53
47
  env:
54
- RUBYGEMS_API_KEY: ${{secrets.RUBYGEMS_API_KEY}}
48
+ RUBYGEMS_API_KEY: ${{ secrets.RUBYGEMS_API_KEY }}
49
+ GIT_EMAIL: team@discourse.org
50
+ GIT_NAME: discoursebot
data/.gitignore CHANGED
@@ -1,22 +1,3 @@
1
- *.gem
2
- *.rbc
3
- .bundle
4
- .config
5
- .yardoc
6
1
  Gemfile.lock
7
- InstalledFiles
8
- _yardoc
9
2
  coverage
10
- doc/
11
- lib/bundler/man
12
- pkg
13
- rdoc
14
- spec/reports
15
- test/tmp
16
- test/version_tmp
17
- tmp
18
- bin/
19
- .ruby-gemset
20
- .ruby-version
21
- .env
22
3
  /config.yml
data/CHANGELOG.md CHANGED
@@ -6,6 +6,45 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
6
6
 
7
7
  ## [Unreleased]
8
8
 
9
+ ## [0.48.0] - 2022-01-28
10
+ ### Added
11
+ - `group_add_owners` method (#239)
12
+ - `group_remove_owners` method (#239)
13
+ - `anonymize` method (#241)
14
+
15
+ ### Changed
16
+ - `DiscourseApi::Timeout` error now inherits from `DiscourseApi::Error` (#240)
17
+ - `DiscourseApi::SingleSignOn#groups` now returns an array of strings where each string is a group name, rather than an array with a single string that contains all the groups comma-concatenated (#243)
18
+
19
+ ## [0.47.0] - 2021-07-19
20
+ ### Added
21
+ - Update invite method
22
+ - Retrieve invite method
23
+ - Destroy all expired invites method
24
+ - Destroy invite method
25
+ - Resend all invites method
26
+ - Resend invite method
27
+ - Pass params to get notifications API
28
+
29
+ ### Deprecated
30
+ - `invite_user_to_topic` has been deprecated, use `invite_to_topic` instead.
31
+ - `create_private_message` has been deprecated, use `create_pm` instead.
32
+
33
+ ## [0.46.0] - 2021-04-12
34
+ ### Added
35
+ - Allow bookmarking topics
36
+ - Add timeout to requests
37
+ - Add params to get_topic_posts
38
+
39
+ ## [0.45.1] - 2021-03-11
40
+ ### Added
41
+ - Fetch global top topics
42
+ - Allow setting topic notifications
43
+ - Return full category response
44
+
45
+ ### Changed
46
+ - Use new search endpoint
47
+
9
48
  ## [0.45.0] - 2021-01-15
10
49
  ### Added
11
50
  - Tag configuration in create_category/update_category
@@ -18,9 +18,9 @@ Gem::Specification.new do |spec|
18
18
  spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
19
  spec.require_paths = ['lib']
20
20
 
21
- spec.add_dependency 'faraday', '~> 1.0'
22
- spec.add_dependency 'faraday_middleware', '~> 1.0'
23
- spec.add_dependency 'rack', '>= 1.6'
21
+ spec.add_runtime_dependency 'faraday', '~> 1.0'
22
+ spec.add_runtime_dependency 'faraday_middleware', '~> 1.0'
23
+ spec.add_runtime_dependency 'rack', '>= 1.6'
24
24
 
25
25
  spec.add_development_dependency 'bundler', '~> 2.0'
26
26
  spec.add_development_dependency 'guard', '~> 2.14'
@@ -30,7 +30,7 @@ Gem::Specification.new do |spec|
30
30
  spec.add_development_dependency 'rspec', '~> 3.4'
31
31
  spec.add_development_dependency 'simplecov', '~> 0.11'
32
32
  spec.add_development_dependency 'webmock', '~> 3.0'
33
- spec.add_development_dependency 'rubocop-discourse'
33
+ spec.add_development_dependency 'rubocop-discourse', '~> 2.4.1'
34
34
 
35
35
  spec.required_ruby_version = '>= 2.5.0'
36
36
  end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+ $LOAD_PATH.unshift File.expand_path('../../lib', __FILE__)
3
+ require File.expand_path('../../lib/discourse_api', __FILE__)
4
+
5
+ config = DiscourseApi::ExampleHelper.load_yml
6
+
7
+ client = DiscourseApi::Client.new(config['host'] || 'http://localhost:3000')
8
+ client.api_key = config['api_key'] || "YOUR_API_KEY"
9
+ client.api_username = config['api_username'] || "YOUR_USERNAME"
10
+
11
+ # Bookmark topic
12
+ puts client.bookmark_topic(1418)
13
+
14
+ # Remove bookmark from topic
15
+ puts client.remove_topic_bookmark(1418)
data/examples/category.rb CHANGED
@@ -14,6 +14,9 @@ puts client.categories()
14
14
  # get sub categories for parent category with id 2
15
15
  puts client.categories(parent_category_id: 2)
16
16
 
17
+ # get the full categories response
18
+ puts client.categories_full()
19
+
17
20
  # List topics in a category
18
21
  category_topics = client.category_latest_topics(category_slug: "test-category")
19
22
  puts category_topics
@@ -9,10 +9,28 @@ client.api_key = config['api_key'] || "YOUR_API_KEY"
9
9
  client.api_username = config['api_username'] || "YOUR_USERNAME"
10
10
 
11
11
  # invite user
12
- client.invite_user(email: "name@example.com", group_ids: "41,42")
12
+ invite = client.invite_user(email: "name@example.com", group_ids: "41,42")
13
+
14
+ #update invite
15
+ client.update_invite(invite["id"], email: "namee@example.com")
16
+
17
+ # resend invite
18
+ client.resend_invite("namee@example.com")
13
19
 
14
20
  # invite to a topic
15
- client.invite_user_to_topic(email: "foo@bar.com", topic_id: 1)
21
+ client.invite_to_topic(1, email: "foo@bar.com")
16
22
 
17
23
  # if the user is an admin you may invite to a group as well
18
- client.invite_user_to_topic(email: "foo@bar.com", topic_id: 1, group_ids: "1,2,3")
24
+ client.invite_to_topic(1, email: "foo@bar.com", group_ids: "1,2,3")
25
+
26
+ # retrieve invite
27
+ puts client.retrieve_invite(email: "foo@bar.com")
28
+
29
+ # resend all invites
30
+ client.resend_all_invites
31
+
32
+ # destroy invite
33
+ client.destroy_invite(invite["id"])
34
+
35
+ # destroy all expired invites
36
+ client.destroy_all_expired_invites
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+ $LOAD_PATH.unshift File.expand_path('../../lib', __FILE__)
3
+ require File.expand_path('../../lib/discourse_api', __FILE__)
4
+
5
+ config = DiscourseApi::ExampleHelper.load_yml
6
+
7
+ client = DiscourseApi::Client.new(config['host'] || 'http://localhost:3000')
8
+ client.api_key = config['api_key'] || "YOUR_API_KEY"
9
+ client.api_username = config['api_username'] || "YOUR_USERNAME"
10
+
11
+ # watch an entire category
12
+ client.category_set_user_notification_level(1, notification_level: 3)
13
+
14
+ # mute a topic
15
+ client.topic_set_user_notification_level(1, notification_level: 0)
16
+
17
+ # get user notifications
18
+ client.notifications(username: 'discourse')
@@ -11,6 +11,9 @@ client.api_username = config['api_username'] || "YOUR_USERNAME"
11
11
  # get latest topics
12
12
  puts client.latest_topics({})
13
13
 
14
+ # get top topics
15
+ puts client.top_topics
16
+
14
17
  # recategorize topic
15
18
  puts client.recategorize_topic(topic_id: 108, category_id: 5)
16
19
 
@@ -36,11 +36,24 @@ module DiscourseApi
36
36
  end
37
37
 
38
38
  def categories(params = {})
39
+ categories_full(params)['category_list']['categories']
40
+ end
41
+
42
+ def categories_full(params = {})
39
43
  response = get('/categories.json', params)
40
- response[:body]['category_list']['categories']
44
+ response[:body]
41
45
  end
42
46
 
43
47
  def category_latest_topics(args = {})
48
+ response = category_latest_topics_full(args)
49
+ if response['errors']
50
+ response['errors']
51
+ else
52
+ response['topic_list']['topics']
53
+ end
54
+ end
55
+
56
+ def category_latest_topics_full(args = {})
44
57
  params = API.params(args)
45
58
  .required(:category_slug)
46
59
  .optional(:page).to_h
@@ -49,25 +62,31 @@ module DiscourseApi
49
62
  url = "#{url}?page=#{params[:page]}"
50
63
  end
51
64
  response = get(url)
52
- if response[:body]['errors']
53
- response[:body]['errors']
54
- else
55
- response[:body]['topic_list']['topics']
56
- end
65
+ response[:body]
57
66
  end
58
67
 
59
68
  def category_top_topics(category_slug)
60
- response = get("/c/#{category_slug}/l/top.json")
61
- if response[:body]['errors']
62
- response[:body]['errors']
69
+ response = category_top_topics_full(category_slug)
70
+ if response['errors']
71
+ response['errors']
63
72
  else
64
- response[:body]['topic_list']['topics']
73
+ response['topic_list']['topics']
65
74
  end
66
75
  end
67
76
 
77
+ def category_top_topics_full(category_slug)
78
+ response = get("/c/#{category_slug}/l/top.json")
79
+ response[:body]
80
+ end
81
+
68
82
  def category_new_topics(category_slug)
83
+ response = category_new_topics_full(category_slug)
84
+ response['topic_list']['topics']
85
+ end
86
+
87
+ def category_new_topics_full(category_slug)
69
88
  response = get("/c/#{category_slug}/l/new.json")
70
- response[:body]['topic_list']['topics']
89
+ response[:body]
71
90
  end
72
91
 
73
92
  def category(id)
@@ -75,12 +94,19 @@ module DiscourseApi
75
94
  response[:body]['category']
76
95
  end
77
96
 
97
+ # TODO: Deprecated. Remove after 20210727
78
98
  def category_set_user_notification(args = {})
79
99
  category_id = args[:id]
80
100
  args = API.params(args)
81
101
  .required(:notification_level)
82
102
  post("/category/#{category_id}/notifications", args)
83
103
  end
104
+
105
+ def category_set_user_notification_level(category_id, params)
106
+ params = API.params(params)
107
+ .required(:notification_level)
108
+ post("/category/#{category_id}/notifications", params)
109
+ end
84
110
  end
85
111
  end
86
112
  end
@@ -14,10 +14,10 @@ module DiscourseApi
14
14
  topics = global_reports[3]
15
15
  posts = global_reports[4]
16
16
 
17
- totals = {
17
+ {
18
18
  'users' => users['total'],
19
19
  'topics' => topics['total'],
20
- 'posts' => posts['total']
20
+ 'posts' => posts['total'],
21
21
  }
22
22
  end
23
23
  end
@@ -38,7 +38,6 @@ module DiscourseApi
38
38
  :messageable_level,
39
39
  :name,
40
40
  :automatic_membership_email_domains,
41
- :automatic_membership_retroactive,
42
41
  :title,
43
42
  :primary_group,
44
43
  :grant_trust_level,
@@ -47,19 +46,31 @@ module DiscourseApi
47
46
  :flair_bg_color,
48
47
  :flair_color,
49
48
  :bio_raw,
50
- :members_visibility_level,
49
+ :visibility_level,
51
50
  :public_admission,
52
51
  :public_exit,
53
52
  :allow_membership_requests,
54
53
  :full_name,
55
54
  :default_notification_level,
56
- :usernames,
57
- :owner_usernames,
58
55
  :membership_request_template)
59
56
  .to_h
60
57
  put("/groups/#{group_id}", group: args)
61
58
  end
62
59
 
60
+ def group_add_owners(group_id, args)
61
+ args = API.params(args)
62
+ .required(:usernames)
63
+ .to_h
64
+ put("/admin/groups/#{group_id}/owners.json", group: args)
65
+ end
66
+
67
+ def group_remove_owners(group_id, args)
68
+ args = API.params(args)
69
+ .required(:usernames)
70
+ .to_h
71
+ delete("/admin/groups/#{group_id}/owners.json", group: args)
72
+ end
73
+
63
74
  def groups(args = {})
64
75
  params = API.params(args)
65
76
  .optional(:page)
@@ -3,17 +3,82 @@ module DiscourseApi
3
3
  module API
4
4
  module Invite
5
5
  def invite_user(params = {})
6
- post("/invites", params)
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
- post("/t/#{params[:topic_id]}/invite", params)
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
- response = get('/notifications.json')
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
- # :target_usernames REQUIRED comma separated list of usernames
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 create_private_message(args = {})
16
+ def create_pm(args = {})
10
17
  args[:archetype] = 'private_message'
11
18
  args = API.params(args)
12
- .required(:title, :raw, :target_usernames, :archetype)
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/query', options.merge(term: term))
15
+ response = get('/search', options.merge(q: term))
16
16
  response[:body]
17
17
  end
18
18
  end
@@ -3,10 +3,10 @@ module DiscourseApi
3
3
  module API
4
4
  module SiteSettings
5
5
  def site_setting_update(args = {})
6
- params = API.params(args)
7
- .required(:name, :value).to_h
6
+ params = API.params(args).required(:name, :value).to_h
8
7
  new_site_setting = { params[:name] => params[:value] }
9
- response = put("/admin/site_settings/#{params[:name]}", new_site_setting)
8
+
9
+ put("/admin/site_settings/#{params[:name]}", new_site_setting)
10
10
  end
11
11
  end
12
12
  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,13 +74,22 @@ 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
81
95
 
@@ -85,6 +99,20 @@ module DiscourseApi
85
99
 
86
100
  post("/t/#{topic_id}/change-owner.json", params)
87
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
88
116
  end
89
117
  end
90
118
  end
@@ -102,6 +102,10 @@ module DiscourseApi
102
102
  put("/admin/users/#{user_id}/unsuspend")
103
103
  end
104
104
 
105
+ def anonymize(user_id)
106
+ put("/admin/users/#{user_id}/anonymize")
107
+ end
108
+
105
109
  def delete_user(user_id, delete_posts = false)
106
110
  delete("/admin/users/#{user_id}.json?delete_posts=#{delete_posts}")
107
111
  end
@@ -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)
@@ -33,4 +33,7 @@ module DiscourseApi
33
33
 
34
34
  class TooManyRequests < DiscourseError
35
35
  end
36
+
37
+ class Timeout < Error
38
+ end
36
39
  end